using Dapper;
using GestionIntegral.Api.Models.Contables;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GestionIntegral.Api.Data.Repositories.Contables
{
    public class PagoDistribuidorRepository : IPagoDistribuidorRepository
    {
        private readonly DbConnectionFactory _cf;
        private readonly ILogger<PagoDistribuidorRepository> _log;

        public PagoDistribuidorRepository(DbConnectionFactory cf, ILogger<PagoDistribuidorRepository> log)
        {
            _cf = cf;
            _log = log;
        }

        private string SelectQueryBase() => @"
            SELECT 
                Id_Pago AS IdPago, Id_Distribuidor AS IdDistribuidor, Fecha, TipoMovimiento, Recibo, Monto,
                Id_TipoPago AS IdTipoPago, Detalle, Id_Empresa AS IdEmpresa
            FROM dbo.cue_PagosDistribuidor";

        public async Task<IEnumerable<PagoDistribuidor>> GetAllAsync(
            DateTime? fechaDesde, DateTime? fechaHasta,
            int? idDistribuidor, int? idEmpresa, string? tipoMovimiento)
        {
            var sqlBuilder = new StringBuilder(SelectQueryBase());
            sqlBuilder.Append(" WHERE 1=1");
            var parameters = new DynamicParameters();

            if (fechaDesde.HasValue) { sqlBuilder.Append(" AND Fecha >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); }
            if (fechaHasta.HasValue) { sqlBuilder.Append(" AND Fecha <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date); }
            if (idDistribuidor.HasValue) { sqlBuilder.Append(" AND Id_Distribuidor = @IdDistribuidorParam"); parameters.Add("IdDistribuidorParam", idDistribuidor.Value); }
            if (idEmpresa.HasValue) { sqlBuilder.Append(" AND Id_Empresa = @IdEmpresaParam"); parameters.Add("IdEmpresaParam", idEmpresa.Value); }
            if (!string.IsNullOrWhiteSpace(tipoMovimiento)) { sqlBuilder.Append(" AND TipoMovimiento = @TipoMovParam"); parameters.Add("TipoMovParam", tipoMovimiento); }

            sqlBuilder.Append(" ORDER BY Fecha DESC, Id_Pago DESC;");

            try
            {
                using var connection = _cf.CreateConnection();
                return await connection.QueryAsync<PagoDistribuidor>(sqlBuilder.ToString(), parameters);
            }
            catch (Exception ex)
            {
                _log.LogError(ex, "Error al obtener todos los Pagos de Distribuidores.");
                return Enumerable.Empty<PagoDistribuidor>();
            }
        }

        public async Task<PagoDistribuidor?> GetByIdAsync(int idPago)
        {
            var sql = SelectQueryBase() + " WHERE Id_Pago = @IdPagoParam";
            try
            {
                using var connection = _cf.CreateConnection();
                return await connection.QuerySingleOrDefaultAsync<PagoDistribuidor>(sql, new { IdPagoParam = idPago });
            }
            catch (Exception ex)
            {
                _log.LogError(ex, "Error al obtener PagoDistribuidor por ID: {IdPago}", idPago);
                return null;
            }
        }

        public async Task<PagoDistribuidor?> GetByReciboAndTipoMovimientoAsync(int recibo, string tipoMovimiento, int? excludeIdPago = null)
        {
            var sqlBuilder = new StringBuilder(SelectQueryBase()); // Reutiliza la consulta base
            sqlBuilder.Append(" WHERE Recibo = @ReciboParam AND TipoMovimiento = @TipoMovParam");
            var parameters = new DynamicParameters();
            parameters.Add("ReciboParam", recibo);
            parameters.Add("TipoMovParam", tipoMovimiento);

            if (excludeIdPago.HasValue)
            {
                sqlBuilder.Append(" AND Id_Pago != @ExcludeIdPagoParam");
                parameters.Add("ExcludeIdPagoParam", excludeIdPago.Value);
            }
            try
            {
                using var connection = _cf.CreateConnection();
                return await connection.QuerySingleOrDefaultAsync<PagoDistribuidor>(sqlBuilder.ToString(), parameters);
            }
            catch (Exception ex)
            {
                _log.LogError(ex, "Error en GetByReciboAndTipoMovimientoAsync. Recibo: {Recibo}, Tipo: {Tipo}", recibo, tipoMovimiento);
                throw; // Relanzar para que el servicio lo maneje
            }
        }

        public async Task<PagoDistribuidor?> CreateAsync(PagoDistribuidor nuevoPago, int idUsuario, IDbTransaction transaction)
        {
            const string sqlInsert = @"
                INSERT INTO dbo.cue_PagosDistribuidor (Id_Distribuidor, Fecha, TipoMovimiento, Recibo, Monto, Id_TipoPago, Detalle, Id_Empresa)
                OUTPUT INSERTED.Id_Pago AS IdPago, INSERTED.Id_Distribuidor AS IdDistribuidor, INSERTED.Fecha, INSERTED.TipoMovimiento, 
                       INSERTED.Recibo, INSERTED.Monto, INSERTED.Id_TipoPago AS IdTipoPago, INSERTED.Detalle, INSERTED.Id_Empresa AS IdEmpresa
                VALUES (@IdDistribuidor, @Fecha, @TipoMovimiento, @Recibo, @Monto, @IdTipoPago, @Detalle, @IdEmpresa);";
            const string sqlHistorico = @"
                INSERT INTO dbo.cue_PagosDistribuidor_H 
                    (Id_Pago, Id_Distribuidor, Fecha, TipoMovimiento, Recibo, Monto, Id_TipoPago, Detalle, Id_Empresa, Id_Usuario, FechaMod, TipoMod)
                VALUES (@IdPagoHist, @IdDistribuidorHist, @FechaHist, @TipoMovimientoHist, @ReciboHist, @MontoHist, @IdTipoPagoHist, @DetalleHist, @IdEmpresaHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);";

            var inserted = await transaction.Connection!.QuerySingleAsync<PagoDistribuidor>(sqlInsert, nuevoPago, transaction);
            if (inserted == null || inserted.IdPago == 0) throw new DataException("Error al crear pago o ID no generado.");

            await transaction.Connection!.ExecuteAsync(sqlHistorico, new
            {
                IdPagoHist = inserted.IdPago,
                IdDistribuidorHist = inserted.IdDistribuidor,
                FechaHist = inserted.Fecha,
                TipoMovimientoHist = inserted.TipoMovimiento,
                ReciboHist = inserted.Recibo,
                MontoHist = inserted.Monto,
                IdTipoPagoHist = inserted.IdTipoPago,
                DetalleHist = inserted.Detalle,
                IdEmpresaHist = inserted.IdEmpresa,
                IdUsuarioHist = idUsuario,
                FechaModHist = DateTime.Now,
                TipoModHist = "Creado"
            }, transaction);
            return inserted;
        }

        public async Task<bool> UpdateAsync(PagoDistribuidor pagoAActualizar, int idUsuario, IDbTransaction transaction)
        {
            var actual = await transaction.Connection!.QuerySingleOrDefaultAsync<PagoDistribuidor>(
                SelectQueryBase() + " WHERE Id_Pago = @IdPagoParam",
                new { IdPagoParam = pagoAActualizar.IdPago }, transaction);
            if (actual == null) throw new KeyNotFoundException("Pago no encontrado.");

            // Campos que se permiten actualizar: Monto, Id_TipoPago, Detalle
            // Otros campos como IdDistribuidor, Fecha, TipoMovimiento, Recibo, IdEmpresa no deberían cambiar
            // para un pago ya registrado. Si necesitan cambiar, se debería anular/eliminar y crear uno nuevo.
            const string sqlUpdate = @"
                UPDATE dbo.cue_PagosDistribuidor SET
                    Monto = @Monto, Id_TipoPago = @IdTipoPago, Detalle = @Detalle
                WHERE Id_Pago = @IdPago;";
            const string sqlHistorico = @"
                INSERT INTO dbo.cue_PagosDistribuidor_H 
                    (Id_Pago, Id_Distribuidor, Fecha, TipoMovimiento, Recibo, Monto, Id_TipoPago, Detalle, Id_Empresa, Id_Usuario, FechaMod, TipoMod)
                VALUES (@IdPagoHist, @IdDistribuidorHist, @FechaHist, @TipoMovimientoHist, @ReciboHist, @MontoHist, @IdTipoPagoHist, @DetalleHist, @IdEmpresaHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);";

            await transaction.Connection!.ExecuteAsync(sqlHistorico, new
            {
                IdPagoHist = actual.IdPago,
                IdDistribuidorHist = actual.IdDistribuidor,
                FechaHist = actual.Fecha,
                TipoMovimientoHist = actual.TipoMovimiento,
                ReciboHist = actual.Recibo,
                MontoHist = actual.Monto, // Valor ANTERIOR
                IdTipoPagoHist = actual.IdTipoPago,
                DetalleHist = actual.Detalle,
                IdEmpresaHist = actual.IdEmpresa, // Valores ANTERIORES
                IdUsuarioHist = idUsuario,
                FechaModHist = DateTime.Now,
                TipoModHist = "Actualizado"
            }, transaction);

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

        public async Task<bool> DeleteAsync(int idPago, int idUsuario, IDbTransaction transaction)
        {
            var actual = await transaction.Connection!.QuerySingleOrDefaultAsync<PagoDistribuidor>(
               SelectQueryBase() + " WHERE Id_Pago = @IdPagoParam",
               new { IdPagoParam = idPago }, transaction);
            if (actual == null) throw new KeyNotFoundException("Pago no encontrado para eliminar.");

            const string sqlDelete = "DELETE FROM dbo.cue_PagosDistribuidor WHERE Id_Pago = @IdPagoParam";
            const string sqlHistorico = @"
                 INSERT INTO dbo.cue_PagosDistribuidor_H 
                    (Id_Pago, Id_Distribuidor, Fecha, TipoMovimiento, Recibo, Monto, Id_TipoPago, Detalle, Id_Empresa, Id_Usuario, FechaMod, TipoMod)
                VALUES (@IdPagoHist, @IdDistribuidorHist, @FechaHist, @TipoMovimientoHist, @ReciboHist, @MontoHist, @IdTipoPagoHist, @DetalleHist, @IdEmpresaHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);";

            await transaction.Connection!.ExecuteAsync(sqlHistorico, new
            {
                IdPagoHist = actual.IdPago,
                IdDistribuidorHist = actual.IdDistribuidor,
                FechaHist = actual.Fecha,
                TipoMovimientoHist = actual.TipoMovimiento,
                ReciboHist = actual.Recibo,
                MontoHist = actual.Monto,
                IdTipoPagoHist = actual.IdTipoPago,
                DetalleHist = actual.Detalle,
                IdEmpresaHist = actual.IdEmpresa,
                IdUsuarioHist = idUsuario,
                FechaModHist = DateTime.Now,
                TipoModHist = "Eliminado"
            }, transaction);

            var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdPagoParam = idPago }, transaction);
            return rowsAffected == 1;
        }

        public async Task<IEnumerable<(PagoDistribuidorHistorico Historial, string NombreUsuarioModifico)>> GetHistorialAsync(
        DateTime? fechaDesde, DateTime? fechaHasta,
        int? idUsuarioModifico, string? tipoModificacion,
        int? idPagoOriginal)
        {
            using var connection = _cf.CreateConnection();
            var sqlBuilder = new StringBuilder(@"
            SELECT 
                h.Id_Pago, h.Id_Distribuidor, h.Fecha, h.TipoMovimiento, h.Recibo, h.Monto,
                h.Id_TipoPago, h.Detalle, h.Id_Empresa,
                h.Id_Usuario, h.FechaMod, h.TipoMod,
                u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico
            FROM dbo.cue_PagosDistribuidor_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 (idPagoOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Pago = @IdPagoOriginalParam"); parameters.Add("IdPagoOriginalParam", idPagoOriginal.Value); }

            sqlBuilder.Append(" ORDER BY h.FechaMod DESC;");

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