using System.Transactions; using SIGCM2.Application.Abstractions; using SIGCM2.Application.Abstractions.Persistence; using SIGCM2.Application.Audit; using SIGCM2.Application.Common; using SIGCM2.Application.Usuarios.GetById; using SIGCM2.Domain.Exceptions; namespace SIGCM2.Application.Usuarios.Deactivate; public sealed class DeactivateUsuarioCommandHandler : ICommandHandler { private readonly IUsuarioRepository _repository; private readonly IRefreshTokenRepository _refreshTokenRepository; private readonly IAuditLogger _audit; public DeactivateUsuarioCommandHandler( IUsuarioRepository repository, IRefreshTokenRepository refreshTokenRepository, IAuditLogger audit) { _repository = repository; _refreshTokenRepository = refreshTokenRepository; _audit = audit; } public async Task Handle(DeactivateUsuarioCommand cmd) { var target = await _repository.GetByIdAsync(cmd.UsuarioId) ?? throw new UsuarioNotFoundException(cmd.UsuarioId); // Idempotent: already inactive → return as-is without touching FechaModificacion if (!target.Activo) { return new UsuarioDetailDto( target.Id, target.Username, target.Nombre, target.Apellido, target.Email, target.Rol, target.Activo, target.MustChangePassword, target.UltimoLogin, target.FechaModificacion); } // Guard: anti-lockout if (target.Rol == "admin" && await _repository.CountActiveAdminsAsync() <= 1) throw new LastAdminLockoutException(); var fields = new UpdateUsuarioFields(target.Nombre, target.Apellido, target.Email, target.Rol, false); var now = DateTime.UtcNow; using (var tx = new TransactionScope( TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }, TransactionScopeAsyncFlowOption.Enabled)) { await _repository.UpdateAsync(cmd.UsuarioId, fields, now); await _refreshTokenRepository.RevokeAllActiveForUserAsync(cmd.UsuarioId, now); await _audit.LogAsync( action: "usuario.deactivate", targetType: "Usuario", targetId: cmd.UsuarioId.ToString()); tx.Complete(); } var updated = await _repository.GetDetailAsync(cmd.UsuarioId) ?? throw new UsuarioNotFoundException(cmd.UsuarioId); return new UsuarioDetailDto( updated.Id, updated.Username, updated.Nombre, updated.Apellido, updated.Email, updated.Rol, updated.Activo, updated.MustChangePassword, updated.UltimoLogin, updated.FechaModificacion); } }