using Dapper;
using GestionIntegral.Api.Models.Suscripciones;
using System.Data;
using System.Text;

namespace GestionIntegral.Api.Data.Repositories.Suscripciones
{
    public class SuscriptorRepository : ISuscriptorRepository
    {
        private readonly DbConnectionFactory _connectionFactory;
        private readonly ILogger<SuscriptorRepository> _logger;

        public SuscriptorRepository(DbConnectionFactory connectionFactory, ILogger<SuscriptorRepository> logger)
        {
            _connectionFactory = connectionFactory;
            _logger = logger;
        }

        public async Task<IEnumerable<Suscriptor>> GetAllAsync(string? nombreFilter, string? nroDocFilter, bool soloActivos)
        {
            var sqlBuilder = new StringBuilder(@"
                SELECT IdSuscriptor, NombreCompleto, Email, Telefono, Direccion, TipoDocumento, 
                       NroDocumento, CBU, IdFormaPagoPreferida, Observaciones, Activo, 
                       IdUsuarioAlta, FechaAlta, IdUsuarioMod, FechaMod
                FROM dbo.susc_Suscriptores WHERE 1=1");
            
            var parameters = new DynamicParameters();

            if (soloActivos)
            {
                sqlBuilder.Append(" AND Activo = 1");
            }
            if (!string.IsNullOrWhiteSpace(nombreFilter))
            {
                sqlBuilder.Append(" AND NombreCompleto LIKE @NombreFilter");
                parameters.Add("NombreFilter", $"%{nombreFilter}%");
            }
            if (!string.IsNullOrWhiteSpace(nroDocFilter))
            {
                sqlBuilder.Append(" AND NroDocumento LIKE @NroDocFilter");
                parameters.Add("NroDocFilter", $"%{nroDocFilter}%");
            }
            sqlBuilder.Append(" ORDER BY NombreCompleto;");

            try
            {
                using var connection = _connectionFactory.CreateConnection();
                return await connection.QueryAsync<Suscriptor>(sqlBuilder.ToString(), parameters);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al obtener todos los Suscriptores.");
                return Enumerable.Empty<Suscriptor>();
            }
        }

        public async Task<Suscriptor?> GetByIdAsync(int id)
        {
            const string sql = @"
                SELECT IdSuscriptor, NombreCompleto, Email, Telefono, Direccion, TipoDocumento, 
                       NroDocumento, CBU, IdFormaPagoPreferida, Observaciones, Activo, 
                       IdUsuarioAlta, FechaAlta, IdUsuarioMod, FechaMod
                FROM dbo.susc_Suscriptores WHERE IdSuscriptor = @Id;";
            try
            {
                using var connection = _connectionFactory.CreateConnection();
                return await connection.QuerySingleOrDefaultAsync<Suscriptor>(sql, new { Id = id });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al obtener Suscriptor por ID: {IdSuscriptor}", id);
                return null;
            }
        }
        
        public async Task<bool> ExistsByDocumentoAsync(string tipoDocumento, string nroDocumento, int? excludeId = null)
        {
            var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.susc_Suscriptores WHERE TipoDocumento = @TipoDocumento AND NroDocumento = @NroDocumento");
            var parameters = new DynamicParameters();
            parameters.Add("TipoDocumento", tipoDocumento);
            parameters.Add("NroDocumento", nroDocumento);

            if (excludeId.HasValue)
            {
                sqlBuilder.Append(" AND IdSuscriptor != @ExcludeId");
                parameters.Add("ExcludeId", excludeId.Value);
            }

            try
            {
                using var connection = _connectionFactory.CreateConnection();
                return await connection.ExecuteScalarAsync<bool>(sqlBuilder.ToString(), parameters);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error en ExistsByDocumentoAsync para Suscriptor.");
                return true; // Asumir que existe si hay error para prevenir duplicados.
            }
        }

        public async Task<bool> IsInUseAsync(int id)
        {
            // Un suscriptor está en uso si tiene suscripciones activas.
            const string sql = "SELECT TOP 1 1 FROM dbo.susc_Suscripciones WHERE IdSuscriptor = @Id AND Estado = 'Activa'";
            try
            {
                using var connection = _connectionFactory.CreateConnection();
                var inUse = await connection.ExecuteScalarAsync<int?>(sql, new { Id = id });
                return inUse.HasValue && inUse.Value == 1;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error en IsInUseAsync para Suscriptor ID: {IdSuscriptor}", id);
                return true; // Asumir en uso si hay error.
            }
        }

        public async Task<Suscriptor?> CreateAsync(Suscriptor nuevoSuscriptor, IDbTransaction transaction)
        {
            if (transaction == null || transaction.Connection == null)
            {
                throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas.");
            }

            const string sqlInsert = @"
                INSERT INTO dbo.susc_Suscriptores 
                    (NombreCompleto, Email, Telefono, Direccion, TipoDocumento, NroDocumento, 
                     CBU, IdFormaPagoPreferida, Observaciones, Activo, IdUsuarioAlta, FechaAlta)
                OUTPUT INSERTED.*
                VALUES 
                    (@NombreCompleto, @Email, @Telefono, @Direccion, @TipoDocumento, @NroDocumento, 
                     @CBU, @IdFormaPagoPreferida, @Observaciones, 1, @IdUsuarioAlta, GETDATE());";

            var suscriptorCreado = await transaction.Connection.QuerySingleAsync<Suscriptor>(
                sqlInsert,
                nuevoSuscriptor,
                transaction: transaction
            );

            // No se necesita historial para la creación, ya que la propia tabla tiene campos de auditoría.
            // Si se necesitara una tabla _H, aquí iría el INSERT a esa tabla.

            return suscriptorCreado;
        }

        public async Task<bool> UpdateAsync(Suscriptor suscriptor, IDbTransaction transaction)
        {
            // El servicio ya ha poblado IdUsuarioMod y FechaMod en la entidad.
            const string sqlUpdate = @"
                UPDATE dbo.susc_Suscriptores SET
                    NombreCompleto = @NombreCompleto,
                    Email = @Email,
                    Telefono = @Telefono,
                    Direccion = @Direccion,
                    TipoDocumento = @TipoDocumento,
                    NroDocumento = @NroDocumento,
                    CBU = @CBU,
                    IdFormaPagoPreferida = @IdFormaPagoPreferida,
                    Observaciones = @Observaciones,
                    IdUsuarioMod = @IdUsuarioMod,
                    FechaMod = @FechaMod
                WHERE IdSuscriptor = @IdSuscriptor;";

            if (transaction == null || transaction.Connection == null)
            {
                throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas.");
            }

            var rowsAffected = await transaction.Connection.ExecuteAsync(sqlUpdate, suscriptor, transaction: transaction);
            return rowsAffected == 1;
        }

        public async Task<bool> ToggleActivoAsync(int id, bool activar, int idUsuario, IDbTransaction transaction)
        {
            const string sqlToggle = @"
                UPDATE dbo.susc_Suscriptores SET
                    Activo = @Activar,
                    IdUsuarioMod = @IdUsuario,
                    FechaMod = GETDATE()
                WHERE IdSuscriptor = @Id;";

            if (transaction == null || transaction.Connection == null)
            {
                throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas.");
            }

            var rowsAffected = await transaction.Connection.ExecuteAsync(
                sqlToggle, 
                new { Activar = activar, IdUsuario = idUsuario, Id = id }, 
                transaction: transaction
            );
            return rowsAffected == 1;
        }
    }
}