using SIGCM2.Application.Abstractions;
using SIGCM2.Application.Abstractions.Persistence;
using SIGCM2.Domain.Exceptions;
namespace SIGCM2.Application.PuntosDeVenta.Reservar;
///
/// Reserva el próximo número correlativo para (PdvId × TipoComprobante) ejecutando
/// usp_ReservarNumeroComprobante vía el repositorio.
///
/// NOTAS DE DISEÑO (AD4, AD9):
/// - NO se envuelve en TransactionScope: el SP ya es atómico bajo SERIALIZABLE.
/// Un TransactionScope ambiente aquí escalaría a DTC → innecesario.
/// - NO usa Polly: no está en el proyecto. Retry deadlock con bucle simple.
/// - Infrastructure traduce SqlException 1205 → DeadlockTransientException.
/// - Backoff en ms: [50, 150, 450] — 3 intentos máximo.
/// - La auditoría de reservas corre solo vía Temporal Tables (AD8).
///
public sealed class ReservarNumeroCommandHandler : ICommandHandler
{
private readonly IPuntoDeVentaRepository _repo;
private readonly int[] _deadlockBackoffMs;
private static readonly int[] DefaultBackoffMs = [50, 150, 450];
public ReservarNumeroCommandHandler(IPuntoDeVentaRepository repo)
: this(repo, DefaultBackoffMs) { }
/// Constructor with custom backoff for testing (e.g., [0,0,0] for fast tests).
public ReservarNumeroCommandHandler(IPuntoDeVentaRepository repo, int[] deadlockBackoffMs)
{
_repo = repo;
_deadlockBackoffMs = deadlockBackoffMs;
}
public async Task Handle(ReservarNumeroCommand command)
{
for (var i = 0; ; i++)
{
try
{
var numero = await _repo.ReservarNumeroAsync(command.PuntoDeVentaId, command.TipoComprobante);
return new ReservaNumeroDto(command.TipoComprobante, numero);
}
catch (DeadlockTransientException) when (i < _deadlockBackoffMs.Length)
{
// Deadlock — retry with backoff
await Task.Delay(_deadlockBackoffMs[i]);
}
// All other exceptions bubble up immediately
}
}
}