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<DeactivateUsuarioCommand, UsuarioDetailDto>
{
    private readonly IUsuarioRepository _repository;
    private readonly IRefreshTokenRepository _refreshTokenRepository;
    private readonly IAuditLogger _audit;
    private readonly TimeProvider _timeProvider;

    public DeactivateUsuarioCommandHandler(
        IUsuarioRepository repository,
        IRefreshTokenRepository refreshTokenRepository,
        IAuditLogger audit,
        TimeProvider timeProvider)
    {
        _repository = repository;
        _refreshTokenRepository = refreshTokenRepository;
        _audit = audit;
        _timeProvider = timeProvider;
    }

    public async Task<UsuarioDetailDto> 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 = _timeProvider.GetUtcNow().UtcDateTime;

        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);
    }
}
