using NSubstitute;
using SIGCM2.Application.Abstractions.Persistence;
using SIGCM2.Application.Audit;
using SIGCM2.Application.IngresosBrutos.Update;
using SIGCM2.Domain.Exceptions;
using SIGCM2.Domain.Fiscal;
using IibbEntity = SIGCM2.Domain.Entities.IngresosBrutos;

namespace SIGCM2.Application.Tests.IngresosBrutos.Update;

public class UpdateIngresosBrutosCommandHandlerTests
{
    private readonly IIngresosBrutosRepository _repo = Substitute.For<IIngresosBrutosRepository>();
    private readonly IAuditLogger _audit = Substitute.For<IAuditLogger>();
    private readonly UpdateIngresosBrutosCommandHandler _handler;

    private static IibbEntity MakeEntity(int id = 1) =>
        IibbEntity.FromDb(
            id: id, provincia: ProvinciaArgentina.BuenosAires, descripcion: "IIBB BA",
            alicuota: 3m, activo: true,
            vigenciaDesde: new DateOnly(2024, 1, 1),
            vigenciaHasta: null, predecesorId: null,
            fechaCreacion: DateTime.UtcNow, fechaModificacion: null);

    private static UpdateIngresosBrutosCommand ValidCommand(int id = 1) => new(
        Id: id, Descripcion: "IIBB BA actualizado", Activo: true);

    public UpdateIngresosBrutosCommandHandlerTests()
    {
        _handler = new UpdateIngresosBrutosCommandHandler(_repo, _audit, TimeProvider.System);
        _repo.GetByIdAsync(1, Arg.Any<CancellationToken>()).Returns(MakeEntity());
        _repo.UpdateCosmeticoAsync(Arg.Any<int>(), Arg.Any<string>(), Arg.Any<bool>(),
                                   Arg.Any<CancellationToken>()).Returns(true);
    }

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

        await Assert.ThrowsAsync<IngresosBrutosNotFoundException>(
            () => _handler.Handle(ValidCommand(99)));
    }

    [Fact]
    public async Task Handle_HappyPath_ReturnsDtoWithUpdatedDescription()
    {
        var result = await _handler.Handle(ValidCommand());

        Assert.Equal("IIBB BA actualizado", result.Descripcion);
    }

    [Fact]
    public async Task Handle_HappyPath_CallsUpdateCosmeticoOnce()
    {
        await _handler.Handle(ValidCommand());

        await _repo.Received(1).UpdateCosmeticoAsync(
            1, "IIBB BA actualizado", true, Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task Handle_HappyPath_CallsAuditWithUpdateAction()
    {
        await _handler.Handle(ValidCommand());

        await _audit.Received(1).LogAsync(
            action: "ingresos_brutos.update",
            targetType: "IngresosBrutos",
            targetId: "1",
            metadata: Arg.Any<object?>(),
            ct: 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(ValidCommand()));
    }
}
