using Dapper;
using SIGCM2.Application.Abstractions.Persistence;
using SIGCM2.Domain.Entities;

namespace SIGCM2.Infrastructure.Persistence;

public sealed class RolRepository : IRolRepository
{
    private readonly SqlConnectionFactory _connectionFactory;

    public RolRepository(SqlConnectionFactory connectionFactory)
    {
        _connectionFactory = connectionFactory;
    }

    public async Task<IReadOnlyList<Rol>> ListAsync(CancellationToken ct = default)
    {
        const string sql = """
            SELECT Id, Codigo, Nombre, Descripcion, Activo, FechaCreacion, FechaModificacion
            FROM dbo.Rol
            ORDER BY Id
            """;

        await using var connection = _connectionFactory.CreateConnection();
        await connection.OpenAsync(ct);

        var rows = await connection.QueryAsync<RolRow>(sql);
        return rows.Select(MapRow).ToList();
    }

    public async Task<Rol?> GetByCodigoAsync(string codigo, CancellationToken ct = default)
    {
        const string sql = """
            SELECT Id, Codigo, Nombre, Descripcion, Activo, FechaCreacion, FechaModificacion
            FROM dbo.Rol
            WHERE Codigo = @Codigo
            """;

        await using var connection = _connectionFactory.CreateConnection();
        await connection.OpenAsync(ct);

        var row = await connection.QuerySingleOrDefaultAsync<RolRow>(sql, new { Codigo = codigo });
        return row is null ? null : MapRow(row);
    }

    public async Task<bool> ExistsActiveByCodigoAsync(string codigo, CancellationToken ct = default)
    {
        const string sql = """
            SELECT COUNT(1) FROM dbo.Rol WHERE Codigo = @Codigo AND Activo = 1
            """;

        await using var connection = _connectionFactory.CreateConnection();
        await connection.OpenAsync(ct);

        var count = await connection.ExecuteScalarAsync<int>(sql, new { Codigo = codigo });
        return count > 0;
    }

    public async Task<int> AddAsync(Rol rol, CancellationToken ct = default)
    {
        // DF handles: Activo (1), FechaCreacion (SYSUTCDATETIME()).
        const string sql = """
            INSERT INTO dbo.Rol (Codigo, Nombre, Descripcion)
            OUTPUT INSERTED.Id
            VALUES (@Codigo, @Nombre, @Descripcion)
            """;

        await using var connection = _connectionFactory.CreateConnection();
        await connection.OpenAsync(ct);

        return await connection.ExecuteScalarAsync<int>(sql, new
        {
            rol.Codigo,
            rol.Nombre,
            rol.Descripcion,
        });
    }

    public async Task<bool> UpdateAsync(string codigo, string nombre, string? descripcion, bool activo, CancellationToken ct = default)
    {
        const string sql = """
            UPDATE dbo.Rol
            SET Nombre = @Nombre,
                Descripcion = @Descripcion,
                Activo = @Activo,
                FechaModificacion = SYSUTCDATETIME()
            WHERE Codigo = @Codigo;
            SELECT @@ROWCOUNT;
            """;

        await using var connection = _connectionFactory.CreateConnection();
        await connection.OpenAsync(ct);

        var rows = await connection.ExecuteScalarAsync<int>(sql, new { Codigo = codigo, Nombre = nombre, Descripcion = descripcion, Activo = activo });
        return rows > 0;
    }

    public async Task<bool> HasActiveUsuariosAsync(string codigo, CancellationToken ct = default)
    {
        const string sql = """
            SELECT COUNT(1) FROM dbo.Usuario WHERE Rol = @Codigo AND Activo = 1
            """;

        await using var connection = _connectionFactory.CreateConnection();
        await connection.OpenAsync(ct);

        var count = await connection.ExecuteScalarAsync<int>(sql, new { Codigo = codigo });
        return count > 0;
    }

    private static Rol MapRow(RolRow row)
        => new(
            id: row.Id,
            codigo: row.Codigo,
            nombre: row.Nombre,
            descripcion: row.Descripcion,
            activo: row.Activo,
            fechaCreacion: row.FechaCreacion,
            fechaModificacion: row.FechaModificacion);

    private sealed record RolRow(
        int Id,
        string Codigo,
        string Nombre,
        string? Descripcion,
        bool Activo,
        DateTime FechaCreacion,
        DateTime? FechaModificacion);
}
