using System.Transactions;
using SIGCM2.Application.Abstractions;
using SIGCM2.Application.Abstractions.Persistence;
using SIGCM2.Application.Abstractions.Security;
using SIGCM2.Application.Audit;
using SIGCM2.Application.Common;
using SIGCM2.Domain.Exceptions;

namespace SIGCM2.Application.Usuarios.ChangeMyPassword;

public sealed class ChangeMyPasswordCommandHandler : ICommandHandler<ChangeMyPasswordCommand, Unit>
{
    private readonly IUsuarioRepository _repository;
    private readonly IPasswordHasher _hasher;
    private readonly IAuditLogger _audit;

    public ChangeMyPasswordCommandHandler(
        IUsuarioRepository repository,
        IPasswordHasher hasher,
        IAuditLogger audit)
    {
        _repository = repository;
        _hasher = hasher;
        _audit = audit;
    }

    public async Task<Unit> Handle(ChangeMyPasswordCommand cmd)
    {
        var user = await _repository.GetByIdAsync(cmd.UsuarioId)
            ?? throw new UsuarioNotFoundException(cmd.UsuarioId);

        if (!_hasher.Verify(cmd.OldPassword, user.PasswordHash))
            throw new InvalidOldPasswordException();

        var newHash = _hasher.Hash(cmd.NewPassword);

        using var tx = new TransactionScope(
            TransactionScopeOption.Required,
            new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted },
            TransactionScopeAsyncFlowOption.Enabled);

        await _repository.UpdatePasswordAsync(cmd.UsuarioId, newHash, mustChangePassword: false);

        await _audit.LogAsync(
            action: "usuario.password_change",
            targetType: "Usuario",
            targetId: cmd.UsuarioId.ToString());

        tx.Complete();

        // NOTE: intentionally does NOT revoke own refresh tokens (spec REQ-BCP-05)
        return Unit.Value;
    }
}
