using FluentAssertions;
using Microsoft.Extensions.Time.Testing;
using SIGCM2.Domain.Entities;

namespace SIGCM2.Application.Tests.Domain.Rubros;

public class RubroTests
{
    private static readonly FakeTimeProvider FakeTime = new(new DateTimeOffset(2026, 4, 18, 12, 0, 0, TimeSpan.Zero));

    // ── ForCreation: happy path ──────────────────────────────────────────────

    [Fact]
    public void Create_con_datos_validos_crea_rubro_activo_con_orden_cero_como_default()
    {
        var rubro = Rubro.ForCreation("Autos", parentId: null, orden: 0, tarifarioBaseId: null, FakeTime);

        rubro.Nombre.Should().Be("Autos");
        rubro.ParentId.Should().BeNull();
        rubro.Orden.Should().Be(0);
        rubro.Activo.Should().BeTrue();
        rubro.TarifarioBaseId.Should().BeNull();
        rubro.Id.Should().Be(0);
        rubro.FechaCreacion.Should().Be(FakeTime.GetUtcNow().UtcDateTime);
        rubro.FechaModificacion.Should().BeNull();
    }

    [Fact]
    public void Create_root_con_parentId_null_es_valido()
    {
        var rubro = Rubro.ForCreation("Autos", parentId: null, orden: 0, tarifarioBaseId: null, FakeTime);

        rubro.ParentId.Should().BeNull();
    }

    // ── ForCreation: validations ─────────────────────────────────────────────

    [Fact]
    public void Create_con_nombre_vacio_lanza_ArgumentException()
    {
        var act = () => Rubro.ForCreation("", parentId: null, orden: 0, tarifarioBaseId: null, FakeTime);

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

    [Fact]
    public void Create_con_nombre_solo_whitespace_lanza_ArgumentException()
    {
        var act = () => Rubro.ForCreation("   ", parentId: null, orden: 0, tarifarioBaseId: null, FakeTime);

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

    [Fact]
    public void Create_con_nombre_excediendo_200_chars_lanza_ArgumentException()
    {
        var nombre = new string('A', 201);

        var act = () => Rubro.ForCreation(nombre, parentId: null, orden: 0, tarifarioBaseId: null, FakeTime);

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

    [Fact]
    public void Create_con_parentId_menor_o_igual_a_cero_lanza_ArgumentException()
    {
        var act = () => Rubro.ForCreation("Autos", parentId: 0, orden: 0, tarifarioBaseId: null, FakeTime);

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

    [Fact]
    public void Create_con_tarifarioBaseId_menor_a_cero_lanza_ArgumentException()
    {
        var act = () => Rubro.ForCreation("Autos", parentId: null, orden: 0, tarifarioBaseId: -1, FakeTime);

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

    // ── WithRenamed ──────────────────────────────────────────────────────────

    [Fact]
    public void Rename_con_nombre_valido_devuelve_nueva_instancia_con_FechaModificacion()
    {
        var rubro = Rubro.ForCreation("Autos", parentId: null, orden: 0, tarifarioBaseId: null, FakeTime);
        var laterTime = new FakeTimeProvider(new DateTimeOffset(2026, 4, 18, 14, 0, 0, TimeSpan.Zero));

        var renamed = rubro.WithRenamed("Vehiculos", laterTime);

        renamed.Nombre.Should().Be("Vehiculos");
        renamed.FechaModificacion.Should().Be(laterTime.GetUtcNow().UtcDateTime);
    }

    [Fact]
    public void Rename_con_nombre_invalido_lanza_ArgumentException()
    {
        var rubro = Rubro.ForCreation("Autos", parentId: null, orden: 0, tarifarioBaseId: null, FakeTime);

        var act = () => rubro.WithRenamed("", FakeTime);

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

    [Fact]
    public void Rename_no_muta_la_instancia_original()
    {
        var rubro = Rubro.ForCreation("Autos", parentId: null, orden: 0, tarifarioBaseId: null, FakeTime);

        rubro.WithRenamed("Vehiculos", FakeTime);

        rubro.Nombre.Should().Be("Autos");
    }

    // ── WithMoved ────────────────────────────────────────────────────────────

    [Fact]
    public void Move_a_nuevo_parent_devuelve_nueva_instancia_con_parentId_actualizado()
    {
        var rubro = Rubro.ForCreation("Autos", parentId: null, orden: 0, tarifarioBaseId: null, FakeTime);

        var moved = rubro.WithMoved(nuevoParentId: 5, nuevoOrden: 2, FakeTime);

        moved.ParentId.Should().Be(5);
        moved.Orden.Should().Be(2);
    }

    [Fact]
    public void Move_a_root_permite_parentId_null()
    {
        var rubro = Rubro.ForCreation("Autos", parentId: 3, orden: 0, tarifarioBaseId: null, FakeTime);

        var moved = rubro.WithMoved(nuevoParentId: null, nuevoOrden: 0, FakeTime);

        moved.ParentId.Should().BeNull();
    }

    [Fact]
    public void Move_no_muta_la_instancia_original()
    {
        var rubro = Rubro.ForCreation("Autos", parentId: null, orden: 0, tarifarioBaseId: null, FakeTime);

        rubro.WithMoved(nuevoParentId: 5, nuevoOrden: 1, FakeTime);

        rubro.ParentId.Should().BeNull();
        rubro.Orden.Should().Be(0);
    }

    // ── WithActivo (Deactivate / Reactivate) ────────────────────────────────

    [Fact]
    public void Deactivate_flip_Activo_a_false()
    {
        var rubro = Rubro.ForCreation("Autos", parentId: null, orden: 0, tarifarioBaseId: null, FakeTime);

        var deactivated = rubro.WithActivo(false, FakeTime);

        deactivated.Activo.Should().BeFalse();
        deactivated.FechaModificacion.Should().Be(FakeTime.GetUtcNow().UtcDateTime);
    }

    [Fact]
    public void Reactivate_flip_Activo_a_true()
    {
        var rubro = Rubro.ForCreation("Autos", parentId: null, orden: 0, tarifarioBaseId: null, FakeTime);
        var deactivated = rubro.WithActivo(false, FakeTime);

        var reactivated = deactivated.WithActivo(true, FakeTime);

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