using NSubstitute;
using SIGCM2.Application.Abstractions.Persistence;
using SIGCM2.Application.Audit;
using SIGCM2.Application.TiposDeIva.Deactivate;
using SIGCM2.Domain.Entities;
using SIGCM2.Domain.Exceptions;

namespace SIGCM2.Application.Tests.TiposDeIva.Deactivate;

public class DeactivateTipoDeIvaCommandHandlerTests
{
    private readonly ITipoDeIvaRepository _repo = Substitute.For<ITipoDeIvaRepository>();
    private readonly IAuditLogger _audit = Substitute.For<IAuditLogger>();
    private readonly DeactivateTipoDeIvaCommandHandler _handler;

    private static TipoDeIva MakeEntity(bool activo = true) => TipoDeIva.FromDb(
        id: 1, codigo: "IVA_21", descripcion: "IVA 21%", porcentaje: 21m, aplicaIVA: true,
        activo: activo, vigenciaDesde: new DateOnly(2024, 1, 1), vigenciaHasta: null,
        predecesorId: null, fechaCreacion: DateTime.UtcNow, fechaModificacion: null);

    public DeactivateTipoDeIvaCommandHandlerTests()
    {
        _handler = new DeactivateTipoDeIvaCommandHandler(_repo, _audit, TimeProvider.System);
        _repo.GetByIdAsync(1, Arg.Any<CancellationToken>()).Returns(MakeEntity());
        _repo.SetActivoAsync(Arg.Any<int>(), Arg.Any<bool>(), Arg.Any<CancellationToken>()).Returns(true);
    }

    [Fact]
    public async Task Handle_NotFound_ThrowsTipoDeIvaNotFoundException()
    {
        _repo.GetByIdAsync(99, Arg.Any<CancellationToken>()).Returns((TipoDeIva?)null);

        await Assert.ThrowsAsync<TipoDeIvaNotFoundException>(
            () => _handler.Handle(new DeactivateTipoDeIvaCommand(99)));
    }

    [Fact]
    public async Task Handle_HappyPath_CallsSetActivoFalse()
    {
        await _handler.Handle(new DeactivateTipoDeIvaCommand(1));

        await _repo.Received(1).SetActivoAsync(1, false, Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task Handle_HappyPath_CallsAuditDeactivate()
    {
        await _handler.Handle(new DeactivateTipoDeIvaCommand(1));

        await _audit.Received(1).LogAsync(
            action: "tipo_iva.deactivate",
            targetType: "TipoDeIva",
            targetId: "1",
            metadata: Arg.Any<object?>(),
            ct: Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task Handle_AlreadyInactive_IsIdempotent_NoAudit()
    {
        _repo.GetByIdAsync(1, Arg.Any<CancellationToken>()).Returns(MakeEntity(activo: false));

        await _handler.Handle(new DeactivateTipoDeIvaCommand(1));

        await _audit.DidNotReceive().LogAsync(
            Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(),
            Arg.Any<object?>(), Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task Handle_AuditLoggerThrows_ExceptionBubblesUp()
    {
        _audit.LogAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(),
                        Arg.Any<object?>(), Arg.Any<CancellationToken>())
              .Returns(Task.FromException(new InvalidOperationException("audit fail")));

        await Assert.ThrowsAsync<InvalidOperationException>(
            () => _handler.Handle(new DeactivateTipoDeIvaCommand(1)));
    }
}
