using System.Reflection;
using FluentAssertions;
using SIGCM2.Domain.Fiscal;
using IibbDomain = SIGCM2.Domain.Entities.IngresosBrutos;

namespace SIGCM2.Application.Tests.Domain.Fiscal;

public class IngresosBrutosTests
{
    private static readonly DateOnly Desde2020 = new(2020, 1, 1);
    private static readonly DateOnly Desde2026 = new(2026, 6, 1);

    private static IibbDomain MakeIIBB(
        int id = 1,
        ProvinciaArgentina provincia = ProvinciaArgentina.BuenosAires,
        string descripcion = "IIBB Buenos Aires",
        decimal alicuota = 3.5m,
        bool activo = true,
        DateOnly? vigenciaDesde = null,
        DateOnly? vigenciaHasta = null,
        int? predecesorId = null)
        => IibbDomain.FromDb(
            id: id,
            provincia: provincia,
            descripcion: descripcion,
            alicuota: alicuota,
            activo: activo,
            vigenciaDesde: vigenciaDesde ?? Desde2020,
            vigenciaHasta: vigenciaHasta,
            predecesorId: predecesorId,
            fechaCreacion: DateTime.UtcNow,
            fechaModificacion: null);

    // ── T200.20: ForCreation validations ──────────────────────────────────────

    [Fact]
    public void ForCreation_ValidArgs_ReturnsEntity()
    {
        var iibb = IibbDomain.ForCreation(ProvinciaArgentina.Cordoba, "IIBB Córdoba", 2.5m, Desde2020);

        iibb.Provincia.Should().Be(ProvinciaArgentina.Cordoba);
        iibb.Descripcion.Should().Be("IIBB Córdoba");
        iibb.Alicuota.Should().Be(2.5m);
        iibb.Activo.Should().BeTrue();
        iibb.Id.Should().Be(0);
        iibb.PredecesorId.Should().BeNull();
        iibb.VigenciaHasta.Should().BeNull();
    }

    [Fact]
    public void ForCreation_AlicuotaNegativa_ThrowsArgumentException()
    {
        var act = () => IibbDomain.ForCreation(ProvinciaArgentina.BuenosAires, "desc", -1m, Desde2020);

        act.Should().Throw<ArgumentException>()
            .WithParameterName("alicuota");
    }

    [Fact]
    public void ForCreation_AlicuotaMayorA100_ThrowsArgumentException()
    {
        var act = () => IibbDomain.ForCreation(ProvinciaArgentina.BuenosAires, "desc", 101m, Desde2020);

        act.Should().Throw<ArgumentException>()
            .WithParameterName("alicuota");
    }

    [Theory]
    [InlineData(0)]
    [InlineData(2.5)]
    [InlineData(100)]
    public void ForCreation_AlicuotaEnRango_NoLanza(double alicuota)
    {
        var act = () => IibbDomain.ForCreation(ProvinciaArgentina.Salta, "desc", (decimal)alicuota, Desde2020);

        act.Should().NotThrow();
    }

    [Fact]
    public void ForCreation_VigenciaHastaMenorQueDesde_ThrowsArgumentException()
    {
        var desde = new DateOnly(2026, 6, 1);
        var hasta = new DateOnly(2026, 1, 1);

        var act = () => IibbDomain.ForCreation(ProvinciaArgentina.SantaFe, "desc", 3m, desde, hasta);

        act.Should().Throw<ArgumentException>()
            .WithParameterName("vigenciaHasta");
    }

    // ── T200.22: With* methods ────────────────────────────────────────────────

    [Fact]
    public void WithDescripcion_ReturnsNewInstanceWithUpdatedDescripcion()
    {
        var original = MakeIIBB(descripcion: "Original");

        var updated = original.WithDescripcion("Actualizado", DateTime.UtcNow);

        updated.Should().NotBeSameAs(original);
        updated.Descripcion.Should().Be("Actualizado");
        updated.Alicuota.Should().Be(original.Alicuota, "Alicuota es inmutable");
    }

    [Fact]
    public void Deactivate_ReturnsNewInstanceWithActivoFalse()
    {
        var original = MakeIIBB(activo: true);

        var deactivated = original.Deactivate(DateTime.UtcNow);

        deactivated.Activo.Should().BeFalse();
        deactivated.Alicuota.Should().Be(original.Alicuota);
        deactivated.Id.Should().Be(original.Id);
    }

    [Fact]
    public void Reactivate_ReturnsNewInstanceWithActivoTrue()
    {
        var original = MakeIIBB(activo: false);

        var reactivated = original.Reactivate(DateTime.UtcNow);

        reactivated.Activo.Should().BeTrue();
    }

    [Fact]
    public void CerrarVigencia_SetsVigenciaHasta()
    {
        var original = MakeIIBB(vigenciaHasta: null);
        var hasta = new DateOnly(2026, 5, 31);

        var cerrado = original.CerrarVigencia(hasta, DateTime.UtcNow);

        cerrado.VigenciaHasta.Should().Be(hasta);
        cerrado.Alicuota.Should().Be(original.Alicuota);
    }

    // ── T200.24: NuevaVersion tuple ───────────────────────────────────────────

    [Fact]
    public void NuevaVersion_ReturnsPredecesoraCerradaYNuevaVersion()
    {
        var predecesora = MakeIIBB(id: 5, alicuota: 2.5m, vigenciaDesde: Desde2020, vigenciaHasta: null);

        var (cerrada, nueva) = predecesora.NuevaVersion(3.0m, Desde2026, DateTime.UtcNow);

        cerrada.Id.Should().Be(5);
        cerrada.VigenciaHasta.Should().Be(Desde2026.AddDays(-1));
        cerrada.Alicuota.Should().Be(2.5m, "alicuota predecesora no cambia");

        nueva.Id.Should().Be(0);
        nueva.PredecesorId.Should().Be(5);
        nueva.Alicuota.Should().Be(3.0m);
        nueva.VigenciaDesde.Should().Be(Desde2026);
        nueva.VigenciaHasta.Should().BeNull();
        nueva.Provincia.Should().Be(predecesora.Provincia, "hereda la provincia");
        nueva.Descripcion.Should().Be(predecesora.Descripcion, "hereda la descripción");
        nueva.Activo.Should().BeTrue();
    }

    [Fact]
    public void NuevaVersion_PredecesoraConVigenciaHasta_ThrowsInvalidOperationException()
    {
        var predecesora = MakeIIBB(
            vigenciaDesde: Desde2020,
            vigenciaHasta: new DateOnly(2025, 12, 31));

        var act = () => predecesora.NuevaVersion(4.0m, Desde2026, DateTime.UtcNow);

        act.Should().Throw<InvalidOperationException>();
    }

    [Fact]
    public void NuevaVersion_VigenciaDesdeIgualAPredecesora_ThrowsArgumentException()
    {
        var predecesora = MakeIIBB(vigenciaDesde: Desde2020, vigenciaHasta: null);

        var act = () => predecesora.NuevaVersion(4.0m, Desde2020, DateTime.UtcNow);

        act.Should().Throw<ArgumentException>()
            .WithParameterName("vigenciaDesde");
    }

    [Fact]
    public void NuevaVersion_NuevaAlicuotaNegativa_ThrowsArgumentException()
    {
        var predecesora = MakeIIBB(vigenciaDesde: Desde2020, vigenciaHasta: null);

        var act = () => predecesora.NuevaVersion(-1m, Desde2026, DateTime.UtcNow);

        act.Should().Throw<ArgumentException>()
            .WithParameterName("nuevaAlicuota");
    }

    [Fact]
    public void NuevaVersion_NuevaAlicuotaMayorA100_ThrowsArgumentException()
    {
        var predecesora = MakeIIBB(vigenciaDesde: Desde2020, vigenciaHasta: null);

        var act = () => predecesora.NuevaVersion(101m, Desde2026, DateTime.UtcNow);

        act.Should().Throw<ArgumentException>()
            .WithParameterName("nuevaAlicuota");
    }

    // ── T200.25: reflection — NO debe existir WithAlicuota ni WithProvincia ───

    [Fact]
    public void IngresosBrutos_No_Debe_Exponer_WithAlicuota()
    {
        var method = typeof(IibbDomain).GetMethod("WithAlicuota", BindingFlags.Public | BindingFlags.Instance);

        method.Should().BeNull("Alicuota es inmutable — usar NuevaVersion");
    }

    [Fact]
    public void IngresosBrutos_No_Debe_Exponer_WithProvincia()
    {
        var method = typeof(IibbDomain).GetMethod("WithProvincia", BindingFlags.Public | BindingFlags.Instance);

        method.Should().BeNull("Provincia es inmutable en IngresosBrutos");
    }

    // ── FromDb sets all properties ────────────────────────────────────────────

    [Fact]
    public void FromDb_SetsAllProperties()
    {
        var fechaCreacion = DateTime.UtcNow;
        var iibb = IibbDomain.FromDb(
            id: 10, provincia: ProvinciaArgentina.Tucuman, descripcion: "IIBB Tucuman",
            alicuota: 1.5m, activo: true,
            vigenciaDesde: Desde2020, vigenciaHasta: null,
            predecesorId: null, fechaCreacion: fechaCreacion, fechaModificacion: null);

        iibb.Id.Should().Be(10);
        iibb.Provincia.Should().Be(ProvinciaArgentina.Tucuman);
        iibb.Alicuota.Should().Be(1.5m);
        iibb.FechaCreacion.Should().Be(fechaCreacion);
    }
}
