using FluentValidation.TestHelper;
using NSubstitute;
using SIGCM2.Application.Abstractions.Persistence;
using SIGCM2.Application.Auth;
using SIGCM2.Application.Usuarios.Create;

namespace SIGCM2.Application.Tests.Usuarios.Create;

public class CreateUsuarioCommandValidatorTests
{
    private readonly IRolRepository _roles = Substitute.For<IRolRepository>();

    public CreateUsuarioCommandValidatorTests()
    {
        // Default mock behavior: canonical seeds are active; unknown codes are not.
        var canonical = new[] { "admin", "cajero", "operador_ctacte", "picadora",
                                "jefe_publicidad", "productor", "diagramacion", "reportes" };
        foreach (var code in canonical)
            _roles.ExistsActiveByCodigoAsync(code, Arg.Any<CancellationToken>()).Returns(true);
    }

    private CreateUsuarioCommandValidator BuildValidator(AuthOptions? opts = null) =>
        new(opts ?? new AuthOptions(), _roles);

    private static CreateUsuarioCommand ValidCommand() => new(
        Username: "operador1",
        Password: "Secreto123",
        Nombre: "Juan",
        Apellido: "Pérez",
        Email: null,
        Rol: "cajero");

    // ── Happy paths ──────────────────────────────────────────────────────────

    [Fact]
    public async Task Validate_ValidCommand_NoErrors()
    {
        var result = await BuildValidator().TestValidateAsync(ValidCommand());
        result.ShouldNotHaveAnyValidationErrors();
    }

    [Fact]
    public async Task Validate_NullEmail_IsValid()
    {
        var cmd = ValidCommand() with { Email = null };
        var result = await BuildValidator().TestValidateAsync(cmd);
        result.ShouldNotHaveAnyValidationErrors();
    }

    [Fact]
    public async Task Validate_ValidEmailPresent_NoErrors()
    {
        var cmd = ValidCommand() with { Email = "juan@example.com" };
        var result = await BuildValidator().TestValidateAsync(cmd);
        result.ShouldNotHaveAnyValidationErrors();
    }

    // ── Username ─────────────────────────────────────────────────────────────

    [Fact]
    public async Task Validate_EmptyUsername_HasError()
    {
        var cmd = ValidCommand() with { Username = "" };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Username);
    }

    [Fact]
    public async Task Validate_UsernameTooShort_HasError()
    {
        var cmd = ValidCommand() with { Username = "ab" };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Username);
    }

    [Fact]
    public async Task Validate_UsernameTooLong_HasError()
    {
        var cmd = ValidCommand() with { Username = new string('a', 51) };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Username);
    }

    [Theory]
    [InlineData("abc")]
    [InlineData("user.name")]
    [InlineData("user-name")]
    [InlineData("user_name")]
    [InlineData("user123")]
    public async Task Validate_UsernameValidFormats_NoError(string username)
    {
        var cmd = ValidCommand() with { Username = username };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldNotHaveValidationErrorFor(c => c.Username);
    }

    [Theory]
    [InlineData("user name")]
    [InlineData("user@name")]
    [InlineData("user#1")]
    public async Task Validate_UsernameInvalidChars_HasError(string username)
    {
        var cmd = ValidCommand() with { Username = username };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Username);
    }

    // ── Password ─────────────────────────────────────────────────────────────

    [Fact]
    public async Task Validate_EmptyPassword_HasError()
    {
        var cmd = ValidCommand() with { Password = "" };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Password);
    }

    [Fact]
    public async Task Validate_PasswordTooShort_HasError()
    {
        var cmd = ValidCommand() with { Password = "Ab1cd5" };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Password);
    }

    [Fact]
    public async Task Validate_PasswordNoLetter_HasError()
    {
        var cmd = ValidCommand() with { Password = "12345678" };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Password);
    }

    [Fact]
    public async Task Validate_PasswordNoDigit_HasError()
    {
        var cmd = ValidCommand() with { Password = "abcdefgh" };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Password);
    }

    [Fact]
    public async Task Validate_PasswordExactMinLength_NoError()
    {
        var cmd = ValidCommand() with { Password = "Secre123" };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldNotHaveValidationErrorFor(c => c.Password);
    }

    // ── Nombre / Apellido ────────────────────────────────────────────────────

    [Fact]
    public async Task Validate_EmptyNombre_HasError()
    {
        var cmd = ValidCommand() with { Nombre = "" };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Nombre);
    }

    [Fact]
    public async Task Validate_EmptyApellido_HasError()
    {
        var cmd = ValidCommand() with { Apellido = "" };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Apellido);
    }

    [Fact]
    public async Task Validate_NombreTooLong_HasError()
    {
        var cmd = ValidCommand() with { Nombre = new string('a', 101) };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Nombre);
    }

    [Fact]
    public async Task Validate_ApellidoTooLong_HasError()
    {
        var cmd = ValidCommand() with { Apellido = new string('a', 101) };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Apellido);
    }

    // ── Rol ──────────────────────────────────────────────────────────────────

    [Theory]
    [InlineData("admin")]
    [InlineData("cajero")]
    [InlineData("operador_ctacte")]
    [InlineData("picadora")]
    [InlineData("jefe_publicidad")]
    [InlineData("productor")]
    [InlineData("diagramacion")]
    [InlineData("reportes")]
    public async Task Validate_CanonicalActiveRoles_NoError(string rol)
    {
        var cmd = ValidCommand() with { Rol = rol };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldNotHaveValidationErrorFor(c => c.Rol);
    }

    [Fact]
    public async Task Validate_RolInexistente_HasError()
    {
        _roles.ExistsActiveByCodigoAsync("superuser", Arg.Any<CancellationToken>()).Returns(false);

        var cmd = ValidCommand() with { Rol = "superuser" };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Rol);
    }

    [Fact]
    public async Task Validate_RolInactivo_HasError()
    {
        // The repository reports NOT active (soft-deleted rol) → validator rejects.
        _roles.ExistsActiveByCodigoAsync("picadora", Arg.Any<CancellationToken>()).Returns(false);

        var cmd = ValidCommand() with { Rol = "picadora" };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Rol);
    }

    [Fact]
    public async Task Validate_RolEmptyString_HasError()
    {
        var cmd = ValidCommand() with { Rol = "" };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Rol);
    }

    [Fact]
    public async Task Validate_RolCaseSensitive_HasError()
    {
        // 'ADMIN' uppercase is not a canonical code; mock returns false by default.
        _roles.ExistsActiveByCodigoAsync("ADMIN", Arg.Any<CancellationToken>()).Returns(false);

        var cmd = ValidCommand() with { Rol = "ADMIN" };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Rol);
    }

    // ── Email ────────────────────────────────────────────────────────────────

    [Fact]
    public async Task Validate_InvalidEmail_HasError()
    {
        var cmd = ValidCommand() with { Email = "not-an-email" };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Email);
    }

    [Fact]
    public async Task Validate_EmailTooLong_HasError()
    {
        var cmd = ValidCommand() with { Email = new string('a', 145) + "@b.com" };
        (await BuildValidator().TestValidateAsync(cmd)).ShouldHaveValidationErrorFor(c => c.Email);
    }
}
