using System.Transactions;
using SIGCM2.Application.Abstractions;
using SIGCM2.Application.Abstractions.Persistence;
using SIGCM2.Application.Audit;
using SIGCM2.Application.IngresosBrutos.Dtos;
using SIGCM2.Domain.Exceptions;

namespace SIGCM2.Application.IngresosBrutos.NuevaVersion;

public sealed class NuevaVersionIngresosBrutosCommandHandler
    : ICommandHandler<NuevaVersionIngresosBrutosCommand, NuevaVersionIibbResultDto>
{
    private readonly IIngresosBrutosRepository _repo;
    private readonly IAuditLogger _audit;
    private readonly TimeProvider _timeProvider;

    public NuevaVersionIngresosBrutosCommandHandler(IIngresosBrutosRepository repo, IAuditLogger audit, TimeProvider timeProvider)
    {
        _repo = repo;
        _audit = audit;
        _timeProvider = timeProvider;
    }

    public async Task<NuevaVersionIibbResultDto> Handle(NuevaVersionIngresosBrutosCommand command)
    {
        // Step 1: load predecesora
        var predecesora = await _repo.GetByIdAsync(command.PredecesoraId)
            ?? throw new IngresosBrutosNotFoundException(command.PredecesoraId);

        // Step 2: guard — predecesora must be open and active
        if (!predecesora.Activo || predecesora.VigenciaHasta is not null)
            throw new PredecesorYaCerradoException(command.PredecesoraId);

        var now = _timeProvider.GetUtcNow().UtcDateTime;

        // Steps 3–4: domain validation + tuple creation (throws ArgumentException if vigencia invalid)
        var (predecesoraCerrada, nuevaVersion) = predecesora.NuevaVersion(
            command.NuevaAlicuota,
            command.VigenciaDesde,
            now);

        using var tx = new TransactionScope(
            TransactionScopeOption.Required,
            new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted },
            TransactionScopeAsyncFlowOption.Enabled);

        // Step 5: optimistic close — race guard
        var closed = await _repo.UpdateCierreVigenciaAsync(
            command.PredecesoraId,
            predecesoraCerrada.VigenciaHasta!.Value);

        if (!closed)
            throw new PredecesorYaCerradoException(command.PredecesoraId);

        // Step 6: insert new version
        var nuevoId = await _repo.InsertAsync(nuevaVersion);

        // Step 7: audit (fail-closed)
        await _audit.LogAsync(
            action: "ingresos_brutos.nueva_version",
            targetType: "IngresosBrutos",
            targetId: nuevoId.ToString(),
            metadata: new
            {
                predecesoraId = command.PredecesoraId,
                nuevoId,
                alicuotaNueva = command.NuevaAlicuota,
                vigenciaDesde = command.VigenciaDesde,
            });

        // Step 8: commit
        tx.Complete();

        return new NuevaVersionIibbResultDto(command.PredecesoraId, nuevoId);
    }
}
