using Dapper;
using GestionIntegral.Api.Models.Contables;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Threading.Tasks;

namespace GestionIntegral.Api.Data.Repositories.Contables
{
    public class TipoPagoRepository : ITipoPagoRepository
    {
        private readonly DbConnectionFactory _connectionFactory;
        private readonly ILogger<TipoPagoRepository> _logger; // Para logging

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

        public async Task<IEnumerable<TipoPago>> GetAllAsync(string? nombreFilter)
        {
            // Construcción segura de la cláusula WHERE
            var sqlBuilder = new System.Text.StringBuilder("SELECT Id_TipoPago AS IdTipoPago, Nombre, Detalle FROM dbo.cue_dtTipopago WHERE 1=1");
            var parameters = new DynamicParameters();

            if (!string.IsNullOrWhiteSpace(nombreFilter))
            {
                sqlBuilder.Append(" AND Nombre LIKE @NombreFilter");
                parameters.Add("NombreFilter", $"%{nombreFilter}%");
            }
            sqlBuilder.Append(" ORDER BY Nombre;");

            try
            {
                using (var connection = _connectionFactory.CreateConnection())
                {
                    return await connection.QueryAsync<TipoPago>(sqlBuilder.ToString(), parameters);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al obtener todos los Tipos de Pago. Filtro: {NombreFilter}", nombreFilter);
                return Enumerable.Empty<TipoPago>(); // Devolver lista vacía en caso de error
            }
        }

        public async Task<TipoPago?> GetByIdAsync(int id)
        {
            var sql = "SELECT Id_TipoPago AS IdTipoPago, Nombre, Detalle FROM dbo.cue_dtTipopago WHERE Id_TipoPago = @Id";
            try
            {
                using (var connection = _connectionFactory.CreateConnection())
                {
                    return await connection.QuerySingleOrDefaultAsync<TipoPago>(sql, new { Id = id });
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al obtener Tipo de Pago por ID: {IdTipoPago}", id);
                return null;
            }
        }

        public async Task<bool> ExistsByNameAsync(string nombre, int? excludeId = null)
        {
            var sqlBuilder = new System.Text.StringBuilder("SELECT COUNT(1) FROM dbo.cue_dtTipopago WHERE Nombre = @Nombre");
            var parameters = new DynamicParameters();
            parameters.Add("Nombre", nombre);

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

            try
            {
                using (var connection = _connectionFactory.CreateConnection())
                {
                    var count = await connection.ExecuteScalarAsync<int>(sqlBuilder.ToString(), parameters);
                    return count > 0;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error en ExistsByNameAsync para Tipo de Pago con nombre: {Nombre}", nombre);
                return true; // Asumir que existe para prevenir duplicados si hay error de BD
            }
        }

        public async Task<TipoPago?> CreateAsync(TipoPago nuevoTipoPago, int idUsuario)
        {
            var sqlInsertPrincipal = @"
        INSERT INTO dbo.cue_dtTipopago (Nombre, Detalle)
        VALUES (@Nombre, @Detalle);
        SELECT CAST(SCOPE_IDENTITY() as int);"; // Obtener el ID generado

            var sqlInsertHistorico = @"
        INSERT INTO dbo.cue_dtTipopago_H (Id_TipoPago, Nombre, Detalle, Id_Usuario, FechaMod, TipoMod)
        VALUES (@IdTipoPago, @Nombre, @Detalle, @IdUsuario, @FechaMod, @TipoMod);";

            try
            {
                using (var connection = _connectionFactory.CreateConnection())
                {
                    connection.Open();
                    using (var transaction = connection.BeginTransaction())
                    {
                        try
                        {
                            // Insertar en la tabla principal y obtener el ID
                            int nuevoId = await connection.ExecuteScalarAsync<int>(
                                sqlInsertPrincipal,
                                new { nuevoTipoPago.Nombre, nuevoTipoPago.Detalle },
                                transaction: transaction
                            );

                            if (nuevoId > 0)
                            {
                                nuevoTipoPago.IdTipoPago = nuevoId; // Asignar el ID al objeto

                                // Insertar en la tabla de historial
                                await connection.ExecuteAsync(sqlInsertHistorico, new
                                {
                                    IdTipoPago = nuevoId, // Usar el ID obtenido
                                    nuevoTipoPago.Nombre,
                                    nuevoTipoPago.Detalle,
                                    IdUsuario = idUsuario,
                                    FechaMod = DateTime.Now,
                                    TipoMod = "Insertada"
                                }, transaction: transaction);

                                transaction.Commit();
                                return nuevoTipoPago; // Devolver el objeto completo con ID
                            }
                            else
                            {
                                transaction.Rollback();
                                _logger.LogError("SCOPE_IDENTITY() devolvió 0 o menos después de insertar TipoPago. Nombre: {Nombre}", nuevoTipoPago.Nombre);
                                return null;
                            }
                        }
                        catch (Exception exTrans)
                        {
                            transaction.Rollback();
                            _logger.LogError(exTrans, "Error en transacción CreateAsync para TipoPago. Nombre: {Nombre}", nuevoTipoPago.Nombre);
                            return null;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error general en CreateAsync para TipoPago. Nombre: {Nombre}", nuevoTipoPago.Nombre);
                return null;
            }
        }

        public async Task<bool> UpdateAsync(TipoPago tipoPagoAActualizar, int idUsuario)
        {
            // Primero, obtenemos el estado actual para el historial
            var tipoPagoActual = await GetByIdAsync(tipoPagoAActualizar.IdTipoPago);
            if (tipoPagoActual == null) return false; // No se encontró para actualizar

            var sqlUpdate = @"
                UPDATE dbo.cue_dtTipopago
                SET Nombre = @Nombre, Detalle = @Detalle
                WHERE Id_TipoPago = @IdTipoPago;";

            var sqlInsertHistorico = @"
                INSERT INTO dbo.cue_dtTipopago_H (Id_TipoPago, Nombre, Detalle, Id_Usuario, FechaMod, TipoMod)
                VALUES (@IdTipoPago, @NombreActual, @DetalleActual, @IdUsuario, @FechaMod, @TipoMod);";

            try
            {
                using (var connection = _connectionFactory.CreateConnection())
                {
                    connection.Open();
                    using (var transaction = connection.BeginTransaction())
                    {
                        try
                        {
                            // Insertar en historial CON LOS VALORES ANTERIORES
                            await connection.ExecuteAsync(sqlInsertHistorico, new
                            {
                                IdTipoPago = tipoPagoActual.IdTipoPago,
                                NombreActual = tipoPagoActual.Nombre, // Valor antes del update
                                DetalleActual = tipoPagoActual.Detalle, // Valor antes del update
                                IdUsuario = idUsuario,
                                FechaMod = DateTime.Now,
                                TipoMod = "Modificada"
                            }, transaction: transaction);

                            // Actualizar la tabla principal
                            var rowsAffected = await connection.ExecuteAsync(sqlUpdate, new
                            {
                                tipoPagoAActualizar.Nombre,
                                tipoPagoAActualizar.Detalle,
                                tipoPagoAActualizar.IdTipoPago
                            }, transaction: transaction);

                            transaction.Commit();
                            return rowsAffected == 1;
                        }
                        catch (Exception exTrans)
                        {
                            transaction.Rollback();
                            _logger.LogError(exTrans, "Error en transacción UpdateAsync para TipoPago ID: {IdTipoPago}", tipoPagoAActualizar.IdTipoPago);
                            return false;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error general en UpdateAsync para TipoPago ID: {IdTipoPago}", tipoPagoAActualizar.IdTipoPago);
                return false;
            }
        }

        public async Task<bool> DeleteAsync(int id, int idUsuario)
        {
            var tipoPagoAEliminar = await GetByIdAsync(id);
            if (tipoPagoAEliminar == null) return false; // No existe

            var sqlDelete = "DELETE FROM dbo.cue_dtTipopago WHERE Id_TipoPago = @Id";
            var sqlInsertHistorico = @"
                INSERT INTO dbo.cue_dtTipopago_H (Id_TipoPago, Nombre, Detalle, Id_Usuario, FechaMod, TipoMod)
                VALUES (@IdTipoPago, @Nombre, @Detalle, @IdUsuario, @FechaMod, @TipoMod);";
            try
            {
                using (var connection = _connectionFactory.CreateConnection())
                {
                    connection.Open();
                    using (var transaction = connection.BeginTransaction())
                    {
                        try
                        {
                            // Insertar en historial ANTES de eliminar
                            await connection.ExecuteAsync(sqlInsertHistorico, new
                            {
                                IdTipoPago = tipoPagoAEliminar.IdTipoPago,
                                tipoPagoAEliminar.Nombre,
                                tipoPagoAEliminar.Detalle,
                                IdUsuario = idUsuario,
                                FechaMod = DateTime.Now,
                                TipoMod = "Eliminada"
                            }, transaction: transaction);

                            var rowsAffected = await connection.ExecuteAsync(sqlDelete, new { Id = id }, transaction: transaction);
                            transaction.Commit();
                            return rowsAffected == 1;
                        }
                        catch (Exception exTrans)
                        {
                            transaction.Rollback();
                            _logger.LogError(exTrans, "Error en transacción DeleteAsync para TipoPago ID: {IdTipoPago}", id);
                            return false;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error general en DeleteAsync para TipoPago ID: {IdTipoPago}", id);
                return false;
            }
        }

        public async Task<bool> IsInUseAsync(int id)
        {
            var sql = "SELECT COUNT(1) FROM dbo.cue_PagosDistribuidor WHERE Id_TipoPago = @IdTipoPago";
            try
            {
                using (var connection = _connectionFactory.CreateConnection())
                {
                    var count = await connection.ExecuteScalarAsync<int>(sql, new { IdTipoPago = id });
                    return count > 0;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error en IsInUseAsync para TipoPago ID: {IdTipoPago}", id);
                return true; // Asumir que está en uso si hay error para prevenir borrado incorrecto
            }
        }

        public async Task<IEnumerable<(TipoPagoHistorico Historial, string NombreUsuarioModifico)>> GetHistorialAsync(
        DateTime? fechaDesde, DateTime? fechaHasta,
        int? idUsuarioModifico, string? tipoModificacion,
        int? idTipoPagoOriginal)
    {
        using var connection = _connectionFactory.CreateConnection(); // Asumiendo _cf es tu DbConnectionFactory
        var sqlBuilder = new StringBuilder(@"
            SELECT 
                h.Id_TipoPago, h.Nombre, h.Detalle,
                h.Id_Usuario, h.FechaMod, h.TipoMod,
                u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico
            FROM dbo.cue_dtTipopago_H h
            JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id
            WHERE 1=1");
        
        var parameters = new DynamicParameters();

        if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); }
        if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); }
        if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); }
        if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); }
        if (idTipoPagoOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_TipoPago = @IdTipoPagoOriginalParam"); parameters.Add("IdTipoPagoOriginalParam", idTipoPagoOriginal.Value); }
        
        sqlBuilder.Append(" ORDER BY h.FechaMod DESC;");

        try
        {
            var result = await connection.QueryAsync<TipoPagoHistorico, string, (TipoPagoHistorico, string)>(
                sqlBuilder.ToString(),
                (hist, userName) => (hist, userName),
                parameters,
                splitOn: "NombreUsuarioModifico"
            );
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error al obtener historial de Tipos de Pago."); // Asumiendo _log
            return Enumerable.Empty<(TipoPagoHistorico, string)>();
        }
    }
    }
}