using NSubstitute;
using SIGCM2.Application.Abstractions.Persistence;
using SIGCM2.Application.Usuarios.GetById;
using SIGCM2.Domain.Entities;
using SIGCM2.Domain.Exceptions;

namespace SIGCM2.Application.Tests.Usuarios;

public class GetUsuarioByIdQueryHandlerTests
{
    private readonly IUsuarioRepository _repo = Substitute.For<IUsuarioRepository>();
    private readonly GetUsuarioByIdQueryHandler _handler;

    public GetUsuarioByIdQueryHandlerTests()
    {
        _handler = new GetUsuarioByIdQueryHandler(_repo);
    }

    [Fact]
    public async Task Handle_Returns_UsuarioDetailDto_When_Found()
    {
        var usuario = new Usuario(5, "jperez", "$2a$12$hash", "Juan", "Pérez", "j@x.com", "cajero", "[]", true,
            fechaModificacion: null, ultimoLogin: null, mustChangePassword: false);

        _repo.GetDetailAsync(5, Arg.Any<CancellationToken>()).Returns(usuario);

        var result = await _handler.Handle(new GetUsuarioByIdQuery(5));

        Assert.Equal(5, result.Id);
        Assert.Equal("jperez", result.Username);
        Assert.Equal("Juan", result.Nombre);
        Assert.Equal("Pérez", result.Apellido);
        Assert.Equal("j@x.com", result.Email);
        Assert.Equal("cajero", result.Rol);
        Assert.True(result.Activo);
        Assert.False(result.MustChangePassword);
    }

    [Fact]
    public async Task Handle_DoesNotReturn_PasswordHash_In_Dto()
    {
        var usuario = new Usuario(5, "jperez", "$2a$12$SECRETHASH", "Juan", "Pérez", null, "cajero", "[]", true);
        _repo.GetDetailAsync(5, Arg.Any<CancellationToken>()).Returns(usuario);

        var result = await _handler.Handle(new GetUsuarioByIdQuery(5));

        // UsuarioDetailDto must not expose PasswordHash
        var props = typeof(UsuarioDetailDto).GetProperties().Select(p => p.Name);
        Assert.DoesNotContain("PasswordHash", props);
        Assert.DoesNotContain("PermisosJson", props);
    }

    [Fact]
    public async Task Handle_Throws_UsuarioNotFoundException_When_Not_Found()
    {
        _repo.GetDetailAsync(9999, Arg.Any<CancellationToken>()).Returns((Usuario?)null);

        await Assert.ThrowsAsync<UsuarioNotFoundException>(
            () => _handler.Handle(new GetUsuarioByIdQuery(9999)));
    }
}
