namespace SIGCM2.Domain.Entities;

/// <summary>
/// Immutable N-ary tree node for the commercial catalog taxonomy.
/// Follows the same sealed-class + factory + with-methods pattern as Medio.cs.
/// </summary>
public sealed class Rubro
{
    private const int NombreMaxLength = 200;

    public int Id { get; }
    public int? ParentId { get; }
    public string Nombre { get; }
    public int Orden { get; }
    public bool Activo { get; }
    public int? TarifarioBaseId { get; }
    public DateTime FechaCreacion { get; }
    public DateTime? FechaModificacion { get; }

    /// <summary>
    /// Full hydration constructor — used by the repository to reconstruct from DB rows.
    /// </summary>
    public Rubro(
        int id,
        int? parentId,
        string nombre,
        int orden,
        bool activo,
        int? tarifarioBaseId,
        DateTime fechaCreacion,
        DateTime? fechaModificacion)
    {
        Id = id;
        ParentId = parentId;
        Nombre = nombre;
        Orden = orden;
        Activo = activo;
        TarifarioBaseId = tarifarioBaseId;
        FechaCreacion = fechaCreacion;
        FechaModificacion = fechaModificacion;
    }

    /// <summary>
    /// Factory for creating a new Rubro.
    /// Id=0 — DB assigns via IDENTITY.
    /// Activo=true, FechaModificacion=null by default.
    /// FechaCreacion is set from TimeProvider so it is testable.
    /// </summary>
    public static Rubro ForCreation(
        string nombre,
        int? parentId,
        int orden,
        int? tarifarioBaseId,
        TimeProvider timeProvider)
    {
        ValidateNombre(nombre);

        if (parentId.HasValue && parentId.Value <= 0)
            throw new ArgumentException("parentId debe ser un entero positivo cuando no es nulo.", nameof(parentId));

        if (tarifarioBaseId.HasValue && tarifarioBaseId.Value < 0)
            throw new ArgumentException("tarifarioBaseId no puede ser negativo.", nameof(tarifarioBaseId));

        return new Rubro(
            id: 0,
            parentId: parentId,
            nombre: nombre,
            orden: orden,
            activo: true,
            tarifarioBaseId: tarifarioBaseId,
            fechaCreacion: timeProvider.GetUtcNow().UtcDateTime,
            fechaModificacion: null);
    }

    /// <summary>
    /// Returns a new Rubro instance with an updated Nombre and FechaModificacion.
    /// Does NOT mutate the current instance.
    /// </summary>
    public Rubro WithRenamed(string nuevoNombre, TimeProvider timeProvider)
    {
        ValidateNombre(nuevoNombre);

        return new Rubro(
            id: Id,
            parentId: ParentId,
            nombre: nuevoNombre,
            orden: Orden,
            activo: Activo,
            tarifarioBaseId: TarifarioBaseId,
            fechaCreacion: FechaCreacion,
            fechaModificacion: timeProvider.GetUtcNow().UtcDateTime);
    }

    /// <summary>
    /// Returns a new Rubro instance with updated ParentId and Orden.
    /// Does NOT mutate the current instance.
    /// </summary>
    public Rubro WithMoved(int? nuevoParentId, int nuevoOrden, TimeProvider timeProvider)
    {
        return new Rubro(
            id: Id,
            parentId: nuevoParentId,
            nombre: Nombre,
            orden: nuevoOrden,
            activo: Activo,
            tarifarioBaseId: TarifarioBaseId,
            fechaCreacion: FechaCreacion,
            fechaModificacion: timeProvider.GetUtcNow().UtcDateTime);
    }

    /// <summary>
    /// Returns a new Rubro instance with updated Activo flag.
    /// Use Deactivate (false) or Reactivate (true).
    /// Does NOT mutate the current instance.
    /// </summary>
    public Rubro WithActivo(bool activo, TimeProvider timeProvider)
    {
        return new Rubro(
            id: Id,
            parentId: ParentId,
            nombre: Nombre,
            orden: Orden,
            activo: activo,
            tarifarioBaseId: TarifarioBaseId,
            fechaCreacion: FechaCreacion,
            fechaModificacion: timeProvider.GetUtcNow().UtcDateTime);
    }

    private static void ValidateNombre(string nombre)
    {
        if (string.IsNullOrWhiteSpace(nombre))
            throw new ArgumentException("El nombre del rubro no puede estar vacío o ser solo espacios.", nameof(nombre));

        if (nombre.Length > NombreMaxLength)
            throw new ArgumentException(
                $"El nombre del rubro no puede superar los {NombreMaxLength} caracteres.",
                nameof(nombre));
    }
}
