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 { private readonly IIngresosBrutosRepository _repo; private readonly IAuditLogger _audit; public NuevaVersionIngresosBrutosCommandHandler(IIngresosBrutosRepository repo, IAuditLogger audit) { _repo = repo; _audit = audit; } public async Task 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); // Steps 3–4: domain validation + tuple creation (throws ArgumentException if vigencia invalid) var (predecesoraCerrada, nuevaVersion) = predecesora.NuevaVersion( command.NuevaAlicuota, command.VigenciaDesde); 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); } }