using SIGCM2.Domain.Pricing.Exceptions;

namespace SIGCM2.Domain.Pricing.ChargeableChars;

/// <summary>
/// PRC-001 — Rich domain entity for chargeable character configuration.
/// Represents a price-per-occurrence for a special character in classified ad text,
/// scoped to a ProductType (ProductTypeId) or global (ProductTypeId = null).
///
/// Forward-only price history: each new price schedules a NEW row; the current row
/// is closed via SP (ValidTo = newValidFrom - 1 day). ScheduleNewPrice does NOT mutate
/// this instance — it returns a new one. The actual close+insert happens in the repository.
///
/// ProductTypeId = null → global default (lowest priority, overridden by per-ProductType row).
/// </summary>
public sealed class ChargeableCharConfig
{
    public long Id { get; }
    public int? ProductTypeId { get; }
    public string Symbol { get; }
    public string Category { get; }
    public decimal PricePerUnit { get; private set; }
    public DateOnly ValidFrom { get; }
    public DateOnly? ValidTo { get; private set; }
    public bool IsActive { get; private set; }

    private ChargeableCharConfig(
        long id, int? productTypeId, string symbol, string category,
        decimal price, DateOnly validFrom, DateOnly? validTo, bool isActive)
    {
        Id = id;
        ProductTypeId = productTypeId;
        Symbol = symbol;
        Category = category;
        PricePerUnit = price;
        ValidFrom = validFrom;
        ValidTo = validTo;
        IsActive = isActive;
    }

    /// <summary>
    /// Factory for new configs. Enforces all domain invariants.
    /// Id is set to 0 until the entity is persisted.
    /// </summary>
    public static ChargeableCharConfig Create(
        int? productTypeId, string symbol, string category, decimal price, DateOnly validFrom)
    {
        if (string.IsNullOrWhiteSpace(symbol))
            throw new ChargeableCharConfigInvalidException(
                nameof(Symbol), "Symbol no puede estar vacío.");

        if (symbol.Length > 4)
            throw new ChargeableCharConfigInvalidException(
                nameof(Symbol), "Symbol no puede exceder 4 caracteres.");

        if (price <= 0m)
            throw new ChargeableCharConfigInvalidException(
                nameof(PricePerUnit), "PricePerUnit debe ser > 0.");

        if (!ChargeableCharCategories.IsValid(category))
            throw new ChargeableCharConfigInvalidException(
                nameof(Category), $"Category '{category}' inválida. Valores válidos: Currency, Percentage, Exclamation, Question, Other.");

        return new ChargeableCharConfig(0, productTypeId, symbol, category, price, validFrom, null, true);
    }

    /// <summary>
    /// Reconstructor from database (no validation). Used by the repository mapper only.
    /// Allows creating entities with any state (e.g., IsActive=false, ValidTo set).
    /// </summary>
    public static ChargeableCharConfig Rehydrate(
        long id, int? productTypeId, string symbol, string category,
        decimal price, DateOnly validFrom, DateOnly? validTo, bool isActive)
        => new(id, productTypeId, symbol, category, price, validFrom, validTo, isActive);

    /// <summary>
    /// Schedules a new price (forward-only semantics).
    /// Returns a NEW ChargeableCharConfig instance with the updated price and validFrom.
    /// This instance is NOT mutated — the close+insert of rows happens in the repository via SP.
    ///
    /// Validates:
    /// - newValidFrom >= today (Argentina) via TimeProvider
    /// - newValidFrom > current ValidFrom (strictly greater — forward-only)
    /// - newPrice > 0
    /// </summary>
    public ChargeableCharConfig ScheduleNewPrice(decimal newPrice, DateOnly newValidFrom, TimeProvider timeProvider)
    {
        var today = timeProvider.GetArgentinaToday();

        if (newValidFrom < today)
            throw new ChargeableCharConfigInvalidException(
                nameof(ValidFrom),
                $"newValidFrom ({newValidFrom:yyyy-MM-dd}) debe ser >= hoy_AR ({today:yyyy-MM-dd}).");

        if (newValidFrom <= ValidFrom)
            throw new ChargeableCharConfigForwardOnlyException(ProductTypeId, Symbol, newValidFrom, ValidFrom);

        // Create validates price > 0 and category — reuse factory
        return Create(ProductTypeId, Symbol, Category, newPrice, newValidFrom);
    }

    /// <summary>
    /// Deactivates this config row. Sets IsActive = false and ValidTo = today.
    /// Idempotent: no-op if already inactive.
    /// </summary>
    public void Deactivate(DateOnly today)
    {
        if (!IsActive) return; // idempotent
        IsActive = false;
        ValidTo = today;
    }
}
