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

namespace SIGCM2.Application.TiposDeIva.NuevaVersion;

public sealed class NuevaVersionTipoDeIvaCommandHandler
    : ICommandHandler<NuevaVersionTipoDeIvaCommand, NuevaVersionResultDto>
{
    private readonly ITipoDeIvaRepository _repo;
    private readonly IAuditLogger _audit;
    private readonly TimeProvider _timeProvider;

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

    public async Task<NuevaVersionResultDto> Handle(NuevaVersionTipoDeIvaCommand command)
    {
        // Step 1: load predecesora
        var predecesora = await _repo.GetByIdAsync(command.PredecesoraId)
            ?? throw new TipoDeIvaNotFoundException(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: delegate validation + tuple creation to domain (throws ArgumentException on invalid vigencia)
        var (predecesoraCerrada, nuevaVersion) = predecesora.NuevaVersion(
            command.NuevoPorcentaje,
            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 — if this throws, tx is NOT completed)
        await _audit.LogAsync(
            action: "tipo_iva.nueva_version",
            targetType: "TipoDeIva",
            targetId: nuevoId.ToString(),
            metadata: new
            {
                predecesoraId = command.PredecesoraId,
                nuevoId,
                porcentajeNuevo = command.NuevoPorcentaje,
                vigenciaDesde = command.VigenciaDesde,
            });

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

        // Step 9: return result
        return new NuevaVersionResultDto(command.PredecesoraId, nuevoId);
    }
}
