using Dapper;
using SIGCM2.Infrastructure.Persistence;
using SIGCM2.TestSupport;

namespace SIGCM2.Application.Tests.Integration;

/// <summary>
/// Integration tests for IUsuarioRepository.UpdatePermisosJsonAsync (UDT-009).
/// Uses SIGCM2_Test_App database via shared SqlTestFixture.
/// </summary>
[Collection("Database")]
public sealed class UsuarioRepository_PermisosTests : IAsyncLifetime
{
    private readonly SqlTestFixture _db;
    private UsuarioRepository _repository = null!;

    public UsuarioRepository_PermisosTests(SqlTestFixture db)
    {
        _db = db;
    }

    public async Task InitializeAsync()
    {
        await _db.ResetAndSeedAsync();

        var factory = new SqlConnectionFactory(TestConnectionStrings.AppTestDb);
        _repository = new UsuarioRepository(factory);

        // Seed a test user
        await _db.Connection.ExecuteAsync("""
            INSERT INTO dbo.Usuario (Username, PasswordHash, Nombre, Apellido, Rol, PermisosJson, Activo, MustChangePassword)
            VALUES ('testuser', '$2a$12$hash', 'Test', 'User', 'cajero', '{"grant":[],"deny":[]}', 1, 0)
            """);
    }

    public Task DisposeAsync() => Task.CompletedTask;

    // UPJ-01: UpdatePermisosJsonAsync persists PermisosJson and FechaModificacion
    [Fact]
    public async Task UpdatePermisosJsonAsync_PersistsJsonAndFechaModificacion()
    {
        // Arrange
        var userId = await _db.Connection.QuerySingleAsync<int>(
            "SELECT Id FROM dbo.Usuario WHERE Username = 'testuser'");
        var newJson = """{"grant":["textos:editar"],"deny":[]}""";
        var fechaMod = DateTime.UtcNow;

        // Act
        await _repository.UpdatePermisosJsonAsync(userId, newJson, fechaMod);

        // Assert
        var row = await _db.Connection.QuerySingleAsync<(string PermisosJson, DateTime? FechaModificacion)>(
            "SELECT PermisosJson, FechaModificacion FROM dbo.Usuario WHERE Id = @Id",
            new { Id = userId });

        Assert.Equal(newJson, row.PermisosJson);
        Assert.NotNull(row.FechaModificacion);
        // Allow 2-second tolerance for DB round-trip
        Assert.True(
            Math.Abs((row.FechaModificacion!.Value - fechaMod).TotalSeconds) < 2,
            $"FechaModificacion {row.FechaModificacion} is too far from {fechaMod}");
    }

    // UPJ-02: UpdatePermisosJsonAsync with non-existent id → no throw (UPDATE affects 0 rows)
    [Fact]
    public async Task UpdatePermisosJsonAsync_NonExistentId_NoThrow()
    {
        // Should not throw — UPDATE with 0 rows affected is a no-op
        await _repository.UpdatePermisosJsonAsync(99999, """{"grant":[],"deny":[]}""", DateTime.UtcNow);
    }

    // UPJ-03: GetByIdAsync after update reflects new PermisosJson
    [Fact]
    public async Task UpdatePermisosJsonAsync_GetByIdReflectsChange()
    {
        // Arrange
        var userId = await _db.Connection.QuerySingleAsync<int>(
            "SELECT Id FROM dbo.Usuario WHERE Username = 'testuser'");
        var newJson = """{"grant":["pauta:azanu:ver"],"deny":["ventas:contado:cobrar"]}""";

        // Act
        await _repository.UpdatePermisosJsonAsync(userId, newJson, DateTime.UtcNow);

        // Assert — read back through the repo
        var usuario = await _repository.GetByIdAsync(userId);

        Assert.NotNull(usuario);
        Assert.Equal(newJson, usuario!.PermisosJson);
    }
}
