namespace SIGCM2.Domain.Entities;

/// <summary>
/// Immutable product-type descriptor for the commercial catalog.
/// Flags drive form behavior (HasDuration, RequiresText, RequiresCategory, IsBundle).
/// Multimedia limits (Max*) are null = no limit.
/// Invariant: if AllowImages == false, the 4 Max* fields must be null
/// (enforced by ForCreation and WithUpdatedMultimedia).
/// </summary>
public sealed class ProductType
{
    private const int NombreMaxLength = 200;

    public int Id { get; }
    public string Nombre { get; }
    public bool HasDuration { get; }
    public bool RequiresText { get; }
    public bool RequiresCategory { get; }
    public bool IsBundle { get; }
    public bool AllowImages { get; }
    public int? MaxImages { get; }
    public decimal? MaxImageSizeMB { get; }
    public int? MaxImageWidth { get; }
    public int? MaxImageHeight { get; }
    public bool IsActive { get; }
    public DateTime FechaCreacion { get; }
    public DateTime? FechaModificacion { get; }

    /// <summary>
    /// Full hydration constructor — used by the repository to reconstruct from DB rows.
    /// </summary>
    public ProductType(
        int id,
        string nombre,
        bool hasDuration,
        bool requiresText,
        bool requiresCategory,
        bool isBundle,
        bool allowImages,
        int? maxImages,
        decimal? maxImageSizeMB,
        int? maxImageWidth,
        int? maxImageHeight,
        bool isActive,
        DateTime fechaCreacion,
        DateTime? fechaModificacion)
    {
        Id = id;
        Nombre = nombre;
        HasDuration = hasDuration;
        RequiresText = requiresText;
        RequiresCategory = requiresCategory;
        IsBundle = isBundle;
        AllowImages = allowImages;
        MaxImages = maxImages;
        MaxImageSizeMB = maxImageSizeMB;
        MaxImageWidth = maxImageWidth;
        MaxImageHeight = maxImageHeight;
        IsActive = isActive;
        FechaCreacion = fechaCreacion;
        FechaModificacion = fechaModificacion;
    }

    /// <summary>
    /// Factory for creating a new ProductType.
    /// Id=0 — DB assigns via IDENTITY.
    /// IsActive=true, FechaModificacion=null by default.
    /// AllowImages=false normalizes all 4 Max* fields to null.
    /// </summary>
    public static ProductType ForCreation(
        string nombre,
        bool hasDuration,
        bool requiresText,
        bool requiresCategory,
        bool isBundle,
        bool allowImages,
        int? maxImages,
        decimal? maxImageSizeMB,
        int? maxImageWidth,
        int? maxImageHeight,
        TimeProvider timeProvider)
    {
        ValidateNombre(nombre);

        var (mi, ms, mw, mh) = NormalizeMultimedia(allowImages, maxImages, maxImageSizeMB, maxImageWidth, maxImageHeight);

        return new ProductType(
            id: 0,
            nombre: nombre,
            hasDuration: hasDuration,
            requiresText: requiresText,
            requiresCategory: requiresCategory,
            isBundle: isBundle,
            allowImages: allowImages,
            maxImages: mi,
            maxImageSizeMB: ms,
            maxImageWidth: mw,
            maxImageHeight: mh,
            isActive: true,
            fechaCreacion: timeProvider.GetUtcNow().UtcDateTime,
            fechaModificacion: null);
    }

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

        return new ProductType(
            Id, nuevoNombre, HasDuration, RequiresText, RequiresCategory, IsBundle,
            AllowImages, MaxImages, MaxImageSizeMB, MaxImageWidth, MaxImageHeight,
            IsActive, FechaCreacion, timeProvider.GetUtcNow().UtcDateTime);
    }

    /// <summary>
    /// Returns a new ProductType with updated flags, preserving multimedia fields.
    /// Does NOT mutate the current instance.
    /// </summary>
    public ProductType WithUpdatedFlags(
        bool hasDuration,
        bool requiresText,
        bool requiresCategory,
        bool isBundle,
        TimeProvider timeProvider)
    {
        return new ProductType(
            Id, Nombre, hasDuration, requiresText, requiresCategory, isBundle,
            AllowImages, MaxImages, MaxImageSizeMB, MaxImageWidth, MaxImageHeight,
            IsActive, FechaCreacion, timeProvider.GetUtcNow().UtcDateTime);
    }

    /// <summary>
    /// Returns a new ProductType with updated multimedia limits.
    /// AllowImages=false normalizes all 4 Max* fields to null.
    /// Does NOT mutate the current instance.
    /// </summary>
    public ProductType WithUpdatedMultimedia(
        bool allowImages,
        int? maxImages,
        decimal? maxImageSizeMB,
        int? maxImageWidth,
        int? maxImageHeight,
        TimeProvider timeProvider)
    {
        var (mi, ms, mw, mh) = NormalizeMultimedia(allowImages, maxImages, maxImageSizeMB, maxImageWidth, maxImageHeight);

        return new ProductType(
            Id, Nombre, HasDuration, RequiresText, RequiresCategory, IsBundle,
            allowImages, mi, ms, mw, mh,
            IsActive, FechaCreacion, timeProvider.GetUtcNow().UtcDateTime);
    }

    /// <summary>
    /// Returns a new ProductType with IsActive=false and FechaModificacion updated.
    /// Does NOT mutate the current instance.
    /// </summary>
    public ProductType WithDeactivated(TimeProvider timeProvider)
    {
        return new ProductType(
            Id, Nombre, HasDuration, RequiresText, RequiresCategory, IsBundle,
            AllowImages, MaxImages, MaxImageSizeMB, MaxImageWidth, MaxImageHeight,
            isActive: false, FechaCreacion, timeProvider.GetUtcNow().UtcDateTime);
    }

    // ── Private helpers ───────────────────────────────────────────────────────

    private static (int?, decimal?, int?, int?) NormalizeMultimedia(
        bool allow, int? mi, decimal? ms, int? mw, int? mh)
        => allow ? (mi, ms, mw, mh) : (null, null, null, null);

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

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