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

namespace SIGCM2.Application.Usuarios.Create;

public sealed class CreateUsuarioCommandHandler : ICommandHandler<CreateUsuarioCommand, UsuarioCreatedDto>
{
    private readonly IUsuarioRepository _repository;
    private readonly IPasswordHasher _hasher;
    private readonly IAuditLogger _audit;

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

    public async Task<UsuarioCreatedDto> Handle(CreateUsuarioCommand command)
    {
        // Check-then-insert: explicit check gives a clear 409 message.
        // SqlException 2627 (UQ violation) acts as race-condition fallback — caught in ExceptionFilter.
        var exists = await _repository.ExistsByUsernameAsync(command.Username);
        if (exists)
            throw new UsernameAlreadyExistsException(command.Username);

        var passwordHash = _hasher.Hash(command.Password);

        var usuario = Usuario.ForCreation(
            username: command.Username,
            passwordHash: passwordHash,
            nombre: command.Nombre,
            apellido: command.Apellido,
            email: command.Email,
            rol: command.Rol);

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

        var newId = await _repository.AddAsync(usuario);

        // UDT-010 (closes follow-up #6): record admin who created this user.
        await _audit.LogAsync(
            action: "usuario.create",
            targetType: "Usuario",
            targetId: newId.ToString(),
            metadata: new
            {
                after = new
                {
                    usuario.Username,
                    usuario.Nombre,
                    usuario.Apellido,
                    usuario.Email,
                    usuario.Rol,
                },
            });

        tx.Complete();

        return new UsuarioCreatedDto(
            Id: newId,
            Username: usuario.Username,
            Nombre: usuario.Nombre,
            Apellido: usuario.Apellido,
            Email: usuario.Email,
            Rol: usuario.Rol,
            Activo: usuario.Activo);
    }
}
