using GestionIntegral.Api.Data;
using GestionIntegral.Api.Data.Repositories.Distribucion;
using GestionIntegral.Api.Data.Repositories.Suscripciones;
using GestionIntegral.Api.Dtos.Suscripciones;
using GestionIntegral.Api.Models.Distribucion;
using GestionIntegral.Api.Models.Suscripciones;
using System.Data;
using System.Globalization;
using System.Text;
using GestionIntegral.Api.Services.Comunicaciones;
using GestionIntegral.Api.Data.Repositories.Comunicaciones;
using GestionIntegral.Api.Data.Repositories.Usuarios;
using GestionIntegral.Api.Dtos.Comunicaciones;
using GestionIntegral.Api.Models.Comunicaciones;

namespace GestionIntegral.Api.Services.Suscripciones
{
    public class FacturacionService : IFacturacionService
    {
        private readonly ILoteDeEnvioRepository _loteDeEnvioRepository;
        private readonly IUsuarioRepository _usuarioRepository;
        private readonly ISuscripcionRepository _suscripcionRepository;
        private readonly IFacturaRepository _facturaRepository;
        private readonly IEmpresaRepository _empresaRepository;
        private readonly IFacturaDetalleRepository _facturaDetalleRepository;
        private readonly IPrecioRepository _precioRepository;
        private readonly IPromocionRepository _promocionRepository;
        private readonly ISuscriptorRepository _suscriptorRepository;
        private readonly IAjusteRepository _ajusteRepository;
        private readonly IEmailService _emailService;
        private readonly IPublicacionRepository _publicacionRepository;
        private readonly DbConnectionFactory _connectionFactory;
        private readonly ILogger<FacturacionService> _logger;
        private readonly string _facturasPdfPath;
        private const string LogoUrl = "https://www.eldia.com/img/header/eldia.png";

        public FacturacionService(
            ISuscripcionRepository suscripcionRepository,
            IFacturaRepository facturaRepository,
            IEmpresaRepository empresaRepository,
            IFacturaDetalleRepository facturaDetalleRepository,
            IPrecioRepository precioRepository,
            IPromocionRepository promocionRepository,
            ISuscriptorRepository suscriptorRepository,
            IAjusteRepository ajusteRepository,
            IEmailService emailService,
            IPublicacionRepository publicacionRepository,
            DbConnectionFactory connectionFactory,
            ILogger<FacturacionService> logger,
            IConfiguration configuration,
            ILoteDeEnvioRepository loteDeEnvioRepository,
             IUsuarioRepository usuarioRepository)
        {
            _loteDeEnvioRepository = loteDeEnvioRepository;
            _usuarioRepository = usuarioRepository;
            _suscripcionRepository = suscripcionRepository;
            _facturaRepository = facturaRepository;
            _empresaRepository = empresaRepository;
            _facturaDetalleRepository = facturaDetalleRepository;
            _precioRepository = precioRepository;
            _promocionRepository = promocionRepository;
            _suscriptorRepository = suscriptorRepository;
            _ajusteRepository = ajusteRepository;
            _emailService = emailService;
            _publicacionRepository = publicacionRepository;
            _connectionFactory = connectionFactory;
            _logger = logger;
            _facturasPdfPath = configuration.GetValue<string>("AppSettings:FacturasPdfPath") ?? "C:\\FacturasPDF";
        }

        public async Task<(bool Exito, string? Mensaje, LoteDeEnvioResumenDto? ResultadoEnvio)> GenerarFacturacionMensual(int anio, int mes, int idUsuario)
        {
            var periodoActual = new DateTime(anio, mes, 1);
            var periodoActualStr = periodoActual.ToString("yyyy-MM");
            _logger.LogInformation("Iniciando generación de facturación para el período {Periodo} por usuario {IdUsuario}", periodoActualStr, idUsuario);

            // --- INICIO: Creación del Lote de Envío ---
            var lote = await _loteDeEnvioRepository.CreateAsync(new LoteDeEnvio
            {
                FechaInicio = DateTime.Now,
                Periodo = periodoActualStr,
                Origen = "FacturacionMensual",
                Estado = "Iniciado",
                IdUsuarioDisparo = idUsuario
            });
            // --- FIN: Creación del Lote de Envío ---

            var ultimoPeriodoFacturadoStr = await _facturaRepository.GetUltimoPeriodoFacturadoAsync();
            if (ultimoPeriodoFacturadoStr != null)
            {
                var ultimoPeriodo = DateTime.ParseExact(ultimoPeriodoFacturadoStr, "yyyy-MM", CultureInfo.InvariantCulture);
                if (periodoActual != ultimoPeriodo.AddMonths(1))
                {
                    var periodoEsperado = ultimoPeriodo.AddMonths(1).ToString("MMMM 'de' yyyy", new CultureInfo("es-ES"));
                    return (false, $"Error: No se puede generar la facturación de {periodoActual:MMMM 'de' yyyy}. El siguiente período a generar es {periodoEsperado}.", null);
                }
            }

            var facturasCreadas = new List<Factura>();
            int facturasGeneradas = 0;
            int emailsEnviados = 0;
            int emailsFallidos = 0;
            var erroresDetallados = new List<EmailLogDto>();

            using var connection = _connectionFactory.CreateConnection();
            await (connection as System.Data.Common.DbConnection)!.OpenAsync();
            using var transaction = connection.BeginTransaction();

            try
            {
                var suscripcionesActivas = await _suscripcionRepository.GetAllActivasParaFacturacion(periodoActualStr, transaction);
                if (!suscripcionesActivas.Any())
                {
                    // Si no hay nada que facturar, consideramos el proceso exitoso pero sin resultados.
                    lote.Estado = "Completado";
                    lote.FechaFin = DateTime.Now;
                    await _loteDeEnvioRepository.UpdateAsync(lote);
                    return (true, "No se encontraron suscripciones activas para facturar en el período especificado.", null);
                }

                var suscripcionesConEmpresa = new List<(Suscripcion Suscripcion, int IdEmpresa)>();
                foreach (var s in suscripcionesActivas)
                {
                    var pub = await _publicacionRepository.GetByIdSimpleAsync(s.IdPublicacion);
                    if (pub != null)
                    {
                        suscripcionesConEmpresa.Add((s, pub.IdEmpresa));
                    }
                }

                var gruposParaFacturar = suscripcionesConEmpresa.GroupBy(s => new { s.Suscripcion.IdSuscriptor, s.IdEmpresa });

                foreach (var grupo in gruposParaFacturar)
                {
                    int idSuscriptor = grupo.Key.IdSuscriptor;
                    int idEmpresa = grupo.Key.IdEmpresa;
                    decimal importeBrutoTotal = 0;
                    decimal descuentoPromocionesTotal = 0;
                    var detallesParaFactura = new List<FacturaDetalle>();
                    foreach (var item in grupo)
                    {
                        var suscripcion = item.Suscripcion;
                        decimal importeBrutoSusc = await CalcularImporteParaSuscripcion(suscripcion, anio, mes, transaction);
                        var promociones = await _promocionRepository.GetPromocionesActivasParaSuscripcion(suscripcion.IdSuscripcion, periodoActual, transaction);
                        decimal descuentoSusc = CalcularDescuentoPromociones(importeBrutoSusc, promociones);
                        importeBrutoTotal += importeBrutoSusc;
                        descuentoPromocionesTotal += descuentoSusc;
                        var publicacion = await _publicacionRepository.GetByIdSimpleAsync(suscripcion.IdPublicacion);
                        detallesParaFactura.Add(new FacturaDetalle
                        {
                            IdSuscripcion = suscripcion.IdSuscripcion,
                            Descripcion = $"Corresponde a {publicacion?.Nombre ?? "N/A"}",
                            ImporteBruto = importeBrutoSusc,
                            DescuentoAplicado = descuentoSusc,
                            ImporteNeto = importeBrutoSusc - descuentoSusc
                        });
                    }
                    var ultimoDiaDelMes = periodoActual.AddMonths(1).AddDays(-1);
                    var ajustesPendientes = await _ajusteRepository.GetAjustesPendientesHastaFechaAsync(idSuscriptor, idEmpresa, ultimoDiaDelMes, transaction);
                    decimal totalAjustes = ajustesPendientes.Sum(a => a.TipoAjuste == "Credito" ? -a.Monto : a.Monto);
                    var importeFinal = importeBrutoTotal - descuentoPromocionesTotal + totalAjustes;
                    if (importeFinal < 0) importeFinal = 0;
                    if (importeBrutoTotal <= 0 && descuentoPromocionesTotal <= 0 && totalAjustes == 0) continue;
                    var nuevaFactura = new Factura
                    {
                        IdSuscriptor = idSuscriptor,
                        Periodo = periodoActualStr,
                        FechaEmision = DateTime.Now.Date,
                        FechaVencimiento = new DateTime(anio, mes, 10),
                        ImporteBruto = importeBrutoTotal,
                        DescuentoAplicado = descuentoPromocionesTotal,
                        ImporteFinal = importeFinal,
                        EstadoPago = "Pendiente",
                        EstadoFacturacion = "Pendiente de Facturar",
                        TipoFactura = "Mensual"
                    };

                    var facturaCreada = await _facturaRepository.CreateAsync(nuevaFactura, transaction);
                    if (facturaCreada == null) throw new DataException($"No se pudo crear la factura para suscriptor ID {idSuscriptor} y empresa ID {idEmpresa}");

                    facturasCreadas.Add(facturaCreada);
                    foreach (var detalle in detallesParaFactura)
                    {
                        detalle.IdFactura = facturaCreada.IdFactura;
                        await _facturaDetalleRepository.CreateAsync(detalle, transaction);
                    }
                    if (ajustesPendientes.Any())
                    {
                        await _ajusteRepository.MarcarAjustesComoAplicadosAsync(ajustesPendientes.Select(a => a.IdAjuste), facturaCreada.IdFactura, transaction);
                    }
                    facturasGeneradas++;
                }

                transaction.Commit();
                _logger.LogInformation("Finalizada la generación de {FacturasGeneradas} facturas para {Periodo}.", facturasGeneradas, periodoActualStr);

                if (facturasCreadas.Any())
                {
                    var suscriptoresAnotificar = facturasCreadas.Select(f => f.IdSuscriptor).Distinct().ToList();
                    _logger.LogInformation("Iniciando envío automático de avisos para {Count} suscriptores.", suscriptoresAnotificar.Count);

                    foreach (var idSuscriptor in suscriptoresAnotificar)
                    {
                        var suscriptor = await _suscriptorRepository.GetByIdAsync(idSuscriptor); // Necesitamos el objeto suscriptor
                        if (suscriptor == null || string.IsNullOrEmpty(suscriptor.Email))
                        {
                            emailsFallidos++;
                            erroresDetallados.Add(new EmailLogDto { DestinatarioEmail = suscriptor?.NombreCompleto ?? $"ID Suscriptor {idSuscriptor}", Error = "Suscriptor sin email válido." });
                            continue;
                        }

                        try
                        {
                            await EnviarAvisoCuentaPorEmail(anio, mes, idSuscriptor, lote.IdLoteDeEnvio, idUsuario);
                            emailsEnviados++;
                        }
                        catch (Exception exEmail)
                        {
                            emailsFallidos++;
                            erroresDetallados.Add(new EmailLogDto { DestinatarioEmail = suscriptor.Email, Error = exEmail.Message });
                            _logger.LogError(exEmail, "Falló el envío automático de email para el suscriptor ID {IdSuscriptor}", idSuscriptor);
                        }
                    }
                    _logger.LogInformation("{EmailsEnviados} avisos de vencimiento enviados automáticamente.", emailsEnviados);
                }

                lote.Estado = "Completado";
            }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch { }
                lote.Estado = "Fallido";
                _logger.LogError(ex, "Error crítico durante la generación de facturación para el período {Periodo}", periodoActualStr);
                return (false, "Error interno del servidor al generar la facturación.", null);
            }
            finally
            {
                lote.FechaFin = DateTime.Now;
                lote.TotalCorreos = emailsEnviados + emailsFallidos;
                lote.TotalEnviados = emailsEnviados;
                lote.TotalFallidos = emailsFallidos;
                await _loteDeEnvioRepository.UpdateAsync(lote);
            }

            var resultadoEnvio = new LoteDeEnvioResumenDto
            {
                IdLoteDeEnvio = lote.IdLoteDeEnvio,
                Periodo = periodoActualStr,
                TotalCorreos = lote.TotalCorreos,
                TotalEnviados = lote.TotalEnviados,
                TotalFallidos = lote.TotalFallidos,
                ErroresDetallados = erroresDetallados
            };

            return (true, $"Proceso completado. Se generaron {facturasGeneradas} facturas.", resultadoEnvio);
        }

        public async Task<IEnumerable<LoteDeEnvioHistorialDto>> ObtenerHistorialLotesEnvio(int? anio, int? mes)
        {
            var lotes = await _loteDeEnvioRepository.GetAllAsync(anio, mes);
            if (!lotes.Any())
            {
                return Enumerable.Empty<LoteDeEnvioHistorialDto>();
            }

            var idsUsuarios = lotes.Select(l => l.IdUsuarioDisparo).Distinct();
            var usuarios = (await _usuarioRepository.GetByIdsAsync(idsUsuarios)).ToDictionary(u => u.Id);

            return lotes.Select(l => new LoteDeEnvioHistorialDto
            {
                IdLoteDeEnvio = l.IdLoteDeEnvio,
                FechaInicio = l.FechaInicio,
                Periodo = l.Periodo,
                Estado = l.Estado,
                TotalCorreos = l.TotalCorreos,
                TotalEnviados = l.TotalEnviados,
                TotalFallidos = l.TotalFallidos,
                NombreUsuarioDisparo = usuarios.TryGetValue(l.IdUsuarioDisparo, out var user)
                        ? $"{user.Nombre} {user.Apellido}"
                        : "Usuario Desconocido"
            });
        }

        public async Task<IEnumerable<ResumenCuentaSuscriptorDto>> ObtenerResumenesDeCuentaPorPeriodo(
        int anio, int mes, string? nombreSuscriptor, string? estadoPago, string? estadoFacturacion, string? tipoFactura)
        {
            var periodo = $"{anio}-{mes:D2}";
            var facturasData = await _facturaRepository.GetByPeriodoEnrichedAsync(periodo, nombreSuscriptor, estadoPago, estadoFacturacion, tipoFactura);
            var detallesData = await _facturaDetalleRepository.GetDetallesPorPeriodoAsync(periodo);
            var empresas = await _empresaRepository.GetAllAsync(null, null);

            var resumenes = facturasData
                .GroupBy(data => data.Factura.IdSuscriptor)
                .Select(grupo =>
                {
                    var primerItem = grupo.First();
                    var facturasConsolidadas = grupo.Select(itemFactura =>
                    {
                        var empresa = empresas.FirstOrDefault(e => e.IdEmpresa == itemFactura.IdEmpresa);
                        return new FacturaConsolidadaDto
                        {
                            IdFactura = itemFactura.Factura.IdFactura,
                            NombreEmpresa = empresa?.Nombre ?? "N/A",
                            ImporteFinal = itemFactura.Factura.ImporteFinal,
                            EstadoPago = itemFactura.Factura.EstadoPago,
                            EstadoFacturacion = itemFactura.Factura.EstadoFacturacion,
                            NumeroFactura = itemFactura.Factura.NumeroFactura,
                            TotalPagado = itemFactura.TotalPagado,

                            // Faltaba esta línea para pasar el tipo de factura al frontend.
                            TipoFactura = itemFactura.Factura.TipoFactura,

                            Detalles = detallesData
                                .Where(d => d.IdFactura == itemFactura.Factura.IdFactura)
                                .Select(d => new FacturaDetalleDto { Descripcion = d.Descripcion, ImporteNeto = d.ImporteNeto })
                                .ToList(),

                            // Pasamos el id del suscriptor para facilitar las cosas en el frontend
                            IdSuscriptor = itemFactura.Factura.IdSuscriptor
                        };
                    }).ToList();

                    return new ResumenCuentaSuscriptorDto
                    {
                        IdSuscriptor = primerItem.Factura.IdSuscriptor,
                        NombreSuscriptor = primerItem.NombreSuscriptor,
                        Facturas = facturasConsolidadas,
                        ImporteTotal = facturasConsolidadas.Sum(f => f.ImporteFinal),
                        SaldoPendienteTotal = facturasConsolidadas.Sum(f => f.ImporteFinal - f.TotalPagado)
                    };
                });

            return resumenes.ToList();
        }

        public async Task<(bool Exito, string? Error, string? EmailDestino)> EnviarFacturaPdfPorEmail(int idFactura, int idUsuario)
        {
            try
            {
                var factura = await _facturaRepository.GetByIdAsync(idFactura);
                if (factura == null) return (false, "Factura no encontrada.", null);
                if (string.IsNullOrEmpty(factura.NumeroFactura)) return (false, "La factura no tiene un número asignado.", null);
                if (factura.EstadoPago == "Anulada") return (false, "No se puede enviar email de una factura anulada.", null);

                var suscriptor = await _suscriptorRepository.GetByIdAsync(factura.IdSuscriptor);
                if (suscriptor == null || string.IsNullOrEmpty(suscriptor.Email)) return (false, "El suscriptor no es válido o no tiene email.", null);

                byte[]? pdfAttachment = null;
                string? pdfFileName = null;
                var rutaCompleta = Path.Combine(_facturasPdfPath, $"{factura.NumeroFactura}.pdf");

                if (File.Exists(rutaCompleta))
                {
                    pdfAttachment = await File.ReadAllBytesAsync(rutaCompleta);
                    pdfFileName = $"Factura_{factura.NumeroFactura}.pdf";
                }
                else
                {
                    _logger.LogWarning("No se encontró el PDF para la factura {NumeroFactura}", factura.NumeroFactura);
                    return (false, "No se encontró el archivo PDF correspondiente en el servidor.", null);
                }

                string asunto = $"Factura Electrónica - Período {factura.Periodo}";
                string cuerpoHtml = ConstruirCuerpoEmailFacturaPdf(suscriptor, factura);

                // Pasamos los nuevos parámetros de contexto al EmailService.
                await _emailService.EnviarEmailAsync(
                    destinatarioEmail: suscriptor.Email,
                    destinatarioNombre: suscriptor.NombreCompleto,
                    asunto: asunto,
                    cuerpoHtml: cuerpoHtml,
                    attachment: pdfAttachment,
                    attachmentName: pdfFileName,
                    origen: "EnvioManualPDF",
                    referenciaId: $"Factura-{idFactura}",
                    idUsuarioDisparo: idUsuario);

                _logger.LogInformation("Email con factura PDF ID {IdFactura} enviado para Suscriptor ID {IdSuscriptor}", idFactura, suscriptor.IdSuscriptor);

                return (true, null, suscriptor.Email);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Falló el envío de email con PDF para la factura ID {IdFactura}", idFactura);
                // El error ya será logueado por EmailService, pero lo relanzamos para que el controller lo maneje.
                // En este caso, simplemente devolvemos la tupla de error.
                return (false, "Ocurrió un error al intentar enviar el email con la factura.", null);
            }
        }

        /// <summary>
        /// Construye y envía un email consolidado con el resumen de todas las facturas de un suscriptor para un período.
        /// Este método está diseñado para ser llamado desde un proceso masivo como la facturación mensual.
        /// </summary>
        private async Task EnviarAvisoCuentaPorEmail(int anio, int mes, int idSuscriptor, int idLoteDeEnvio, int idUsuarioDisparo)
        {
            var periodo = $"{anio}-{mes:D2}";

            // La lógica de try/catch ahora está en el método llamador (GenerarFacturacionMensual)
            // para poder contar los fallos y actualizar el lote de envío.

            var facturasConEmpresa = await _facturaRepository.GetFacturasConEmpresaAsync(idSuscriptor, periodo);
            if (!facturasConEmpresa.Any())
            {
                // Si no hay facturas, no hay nada que enviar. Esto no debería ocurrir si se llama desde GenerarFacturacionMensual.
                _logger.LogWarning("Se intentó enviar aviso para Suscriptor ID {IdSuscriptor} en período {Periodo}, pero no se encontraron facturas.", idSuscriptor, periodo);
                return;
            }

            var suscriptor = await _suscriptorRepository.GetByIdAsync(idSuscriptor);
            // La validación de si el suscriptor tiene email ya se hace en el método llamador.
            if (suscriptor == null || string.IsNullOrEmpty(suscriptor.Email))
            {
                // Lanzamos una excepción para que el método llamador la capture y la cuente como un fallo.
                throw new InvalidOperationException($"El suscriptor ID {idSuscriptor} no es válido o no tiene una dirección de email registrada.");
            }

            var resumenHtml = new StringBuilder();
            var adjuntos = new List<(byte[] content, string name)>();

            foreach (var item in facturasConEmpresa.Where(f => f.Factura.EstadoPago != "Anulada"))
            {
                var factura = item.Factura;
                var nombreEmpresa = item.NombreEmpresa;
                var detalles = await _facturaDetalleRepository.GetDetallesPorFacturaIdAsync(factura.IdFactura);

                resumenHtml.Append($"<h4 style='margin-top: 20px; margin-bottom: 10px; color: #34515e;'>Resumen para {nombreEmpresa}</h4>");
                resumenHtml.Append("<table style='width: 100%; border-collapse: collapse; font-size: 0.9em;'>");

                foreach (var detalle in detalles)
                {
                    resumenHtml.Append($"<tr><td style='padding: 5px; border-bottom: 1px solid #eee;'>{detalle.Descripcion}</td><td style='padding: 5px; border-bottom: 1px solid #eee; text-align: right;'>${detalle.ImporteNeto:N2}</td></tr>");
                }

                var ajustes = await _ajusteRepository.GetAjustesPorIdFacturaAsync(factura.IdFactura);
                if (ajustes.Any())
                {
                    foreach (var ajuste in ajustes)
                    {
                        bool esCredito = ajuste.TipoAjuste == "Credito";
                        string colorMonto = esCredito ? "#5cb85c" : "#d9534f";
                        string signo = esCredito ? "-" : "+";
                        resumenHtml.Append($"<tr><td style='padding: 5px; border-bottom: 1px solid #eee; font-style: italic;'>Ajuste: {ajuste.Motivo}</td><td style='padding: 5px; border-bottom: 1px solid #eee; text-align: right; color: {colorMonto}; font-style: italic;'>{signo} ${ajuste.Monto:N2}</td></tr>");
                    }
                }

                resumenHtml.Append($"<tr style='font-weight: bold;'><td style='padding: 5px;'>Subtotal</td><td style='padding: 5px; text-align: right;'>${factura.ImporteFinal:N2}</td></tr>");
                resumenHtml.Append("</table>");

                if (!string.IsNullOrEmpty(factura.NumeroFactura))
                {
                    var rutaCompleta = Path.Combine(_facturasPdfPath, $"{factura.NumeroFactura}.pdf");
                    if (File.Exists(rutaCompleta))
                    {
                        byte[] pdfBytes = await File.ReadAllBytesAsync(rutaCompleta);
                        string pdfFileName = $"Factura_{nombreEmpresa.Replace(" ", "")}_{factura.NumeroFactura}.pdf";
                        adjuntos.Add((pdfBytes, pdfFileName));
                        _logger.LogInformation("PDF adjuntado para envío a Suscriptor ID {IdSuscriptor}: {FileName}", idSuscriptor, pdfFileName);
                    }
                    else
                    {
                        _logger.LogWarning("No se encontró el PDF para la factura {NumeroFactura} en la ruta: {Ruta}", factura.NumeroFactura, rutaCompleta);
                    }
                }
            }

            var totalGeneral = facturasConEmpresa.Where(f => f.Factura.EstadoPago != "Anulada").Sum(f => f.Factura.ImporteFinal);
            string asunto = $"Resumen de Cuenta - Período {periodo}";
            string cuerpoHtml = ConstruirCuerpoEmailConsolidado(suscriptor, periodo, resumenHtml.ToString(), totalGeneral);

            await _emailService.EnviarEmailConsolidadoAsync(
                destinatarioEmail: suscriptor.Email,
                destinatarioNombre: suscriptor.NombreCompleto,
                asunto: asunto,
                cuerpoHtml: cuerpoHtml,
                adjuntos: adjuntos,
                origen: "FacturacionMensual",
                referenciaId: $"Suscriptor-{idSuscriptor}",
                idUsuarioDisparo: idUsuarioDisparo, // Se pasa el ID del usuario que inició el cierre
                idLoteDeEnvio: idLoteDeEnvio // Se pasa el ID del lote
            );

            // El logging de éxito o fallo ahora lo hace el EmailService, por lo que este log ya no es estrictamente necesario,
            // pero lo mantenemos para tener un registro de alto nivel en el log del FacturacionService.
            _logger.LogInformation("Llamada a EmailService completada para Suscriptor ID {IdSuscriptor} en el período {Periodo}.", idSuscriptor, periodo);
        }

        private string ConstruirCuerpoEmailConsolidado(Suscriptor suscriptor, string periodo, string resumenHtml, decimal totalGeneral)
        {
            return $@"
            <div style='font-family: Arial, sans-serif; background-color: #f9f9f9; padding: 20px;'>
                <div style='max-width: 600px; margin: auto; background-color: #ffffff; border: 1px solid #ddd; border-radius: 8px; overflow: hidden;'>
                    <div style='background-color: #34515e; color: #ffffff; padding: 20px; text-align: center;'>
                        <img src='{LogoUrl}' alt='El Día' style='max-width: 150px; margin-bottom: 10px;'>
                        <h2>Resumen de su Cuenta</h2>
                    </div>
                    <div style='padding: 20px; color: #333;'>
                        <h3 style='color: #34515e;'>Hola {suscriptor.NombreCompleto},</h3>
                        <p>Le enviamos el resumen de su cuenta para el período <strong>{periodo}</strong>.</p>
                        
                        <!-- Aquí se insertan las tablas de resumen generadas dinámicamente -->
                        {resumenHtml}
                        
                        <hr style='border: none; border-top: 1px solid #eee; margin: 20px 0;'/>
                        <table style='width: 100%;'>
                            <tr>
                                <td style='font-size: 1.2em; font-weight: bold;'>TOTAL A ABONAR:</td>
                                <td style='font-size: 1.4em; font-weight: bold; text-align: right; color: #34515e;'>${totalGeneral:N2}</td>
                            </tr>
                        </table>
                        <p style='margin-top: 25px;'>Si su pago es por débito automático, los importes se debitarán de su cuenta. Si utiliza otro medio de pago, por favor, regularice su situación.</p>
                        <p>Gracias por ser parte de nuestra comunidad de lectores.</p>
                    </div>
                    <div style='background-color: #f2f2f2; padding: 15px; text-align: center; font-size: 0.8em; color: #777;'>
                        <p>Este es un correo electrónico generado automáticamente. Por favor, no responda a este mensaje.</p>
                        <p>&copy; {DateTime.Now.Year} Diario El Día. Todos los derechos reservados.</p>
                    </div>
                </div>
            </div>";
        }

        private string ConstruirCuerpoEmailFacturaPdf(Suscriptor suscriptor, Factura factura)
        {
            return $@"
            <div style='font-family: Arial, sans-serif; background-color: #f9f9f9; padding: 20px;'>
                <div style='max-width: 600px; margin: auto; background-color: #ffffff; border: 1px solid #ddd; border-radius: 8px; overflow: hidden;'>
                    <div style='background-color: #34515e; color: #ffffff; padding: 20px; text-align: center;'>
                        <img src='{LogoUrl}' alt='El Día' style='max-width: 150px; margin-bottom: 10px;'>
                        <h2>Factura Electrónica Adjunta</h2>
                    </div>
                    <div style='padding: 20px; color: #333;'>
                        <h3 style='color: #34515e;'>Hola {suscriptor.NombreCompleto},</h3>
                        <p>Le enviamos adjunta su factura correspondiente al período <strong>{factura.Periodo}</strong>.</p>
                        <h4 style='border-bottom: 2px solid #34515e; padding-bottom: 5px; margin-top: 30px;'>Resumen de la Factura</h4>
                        <table style='width: 100%; border-collapse: collapse; margin-top: 15px;'>
                            <tr style='border-bottom: 1px solid #eee;'><td style='padding: 8px; font-weight: bold;'>Número de Factura:</td><td style='padding: 8px; text-align: right;'>{factura.NumeroFactura}</td></tr>
                            <tr style='border-bottom: 1px solid #eee;'><td style='padding: 8px; font-weight: bold;'>Período:</td><td style='padding: 8px; text-align: right;'>{factura.Periodo}</td></tr>
                            <tr style='border-bottom: 1px solid #eee;'><td style='padding: 8px; font-weight: bold;'>Fecha de Envío:</td><td style='padding: 8px; text-align: right;'>{factura.FechaEmision:dd/MM/yyyy}</td></tr>
                            <tr style='background-color: #f2f2f2;'><td style='padding: 12px; font-weight: bold; font-size: 1.1em;'>IMPORTE TOTAL:</td><td style='padding: 12px; text-align: right; font-weight: bold; font-size: 1.2em; color: #34515e;'>${factura.ImporteFinal:N2}</td></tr>
                        </table>
                        <p style='margin-top: 30px;'>Puede descargar y guardar el archivo PDF adjunto para sus registros.</p>
                        <p>Gracias por ser parte de nuestra comunidad de lectores.</p>
                    </div>
                    <div style='background-color: #f2f2f2; padding: 15px; text-align: center; font-size: 0.8em; color: #777;'>
                        <p>Este es un correo electrónico generado automáticamente. Por favor, no responda a este mensaje.</p>
                        <p>&copy; {DateTime.Now.Year} Diario El Día. Todos los derechos reservados.</p>
                    </div>
                </div>
            </div>";
        }

        public async Task<(bool Exito, string? Error)> ActualizarNumeroFactura(int idFactura, string numeroFactura, int idUsuario)
        {
            if (string.IsNullOrWhiteSpace(numeroFactura))
            {
                return (false, "El número de factura no puede estar vacío.");
            }

            using var connection = _connectionFactory.CreateConnection();
            await (connection as System.Data.Common.DbConnection)!.OpenAsync();
            using var transaction = connection.BeginTransaction();

            try
            {
                var factura = await _facturaRepository.GetByIdAsync(idFactura);
                if (factura == null)
                {
                    return (false, "La factura especificada no existe.");
                }
                if (factura.EstadoPago == "Anulada")
                {
                    return (false, "No se puede modificar una factura anulada.");
                }

                var actualizado = await _facturaRepository.UpdateNumeroFacturaAsync(idFactura, numeroFactura, transaction);
                if (!actualizado)
                {
                    throw new DataException("La actualización del número de factura falló en el repositorio.");
                }

                transaction.Commit();
                _logger.LogInformation("Número de factura para Factura ID {IdFactura} actualizado a {NumeroFactura} por Usuario ID {IdUsuario}", idFactura, numeroFactura, idUsuario);
                return (true, null);
            }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch { }
                _logger.LogError(ex, "Error al actualizar número de factura para Factura ID {IdFactura}", idFactura);
                return (false, "Error interno al actualizar el número de factura.");
            }
        }

        public async Task<decimal> CalcularImporteParaSuscripcion(Suscripcion suscripcion, int anio, int mes, IDbTransaction transaction)
        {
            decimal importeTotal = 0;
            var diasDeEntrega = suscripcion.DiasEntrega.Split(',').ToHashSet();
            var fechaActual = new DateTime(anio, mes, 1);
            var promociones = await _promocionRepository.GetPromocionesActivasParaSuscripcion(suscripcion.IdSuscripcion, fechaActual, transaction);
            var promocionesDeBonificacion = promociones.Where(p => p.TipoEfecto == "BonificarEntregaDia").ToList();

            while (fechaActual.Month == mes)
            {
                if (fechaActual.Date >= suscripcion.FechaInicio.Date && (suscripcion.FechaFin == null || fechaActual.Date <= suscripcion.FechaFin.Value.Date))
                {
                    var diaSemanaChar = GetCharDiaSemana(fechaActual.DayOfWeek);
                    if (diasDeEntrega.Contains(diaSemanaChar))
                    {
                        decimal precioDelDia = 0;
                        var precioActivo = await _precioRepository.GetActiveByPublicacionAndDateAsync(suscripcion.IdPublicacion, fechaActual, transaction);
                        if (precioActivo != null)
                        {
                            precioDelDia = GetPrecioDelDia(precioActivo, fechaActual.DayOfWeek);
                        }
                        else
                        {
                            _logger.LogWarning("No se encontró precio para la publicación ID {IdPublicacion} en la fecha {Fecha}", suscripcion.IdPublicacion, fechaActual.Date);
                        }

                        bool diaBonificado = promocionesDeBonificacion.Any(promo => EvaluarCondicionPromocion(promo, fechaActual));
                        if (diaBonificado)
                        {
                            precioDelDia = 0;
                            _logger.LogInformation("Día {Fecha} bonificado para suscripción {IdSuscripcion} por promoción.", fechaActual.ToShortDateString(), suscripcion.IdSuscripcion);
                        }
                        importeTotal += precioDelDia;
                    }
                }
                fechaActual = fechaActual.AddDays(1);
            }
            return importeTotal;
        }

        private bool EvaluarCondicionPromocion(Promocion promocion, DateTime fecha)
        {
            switch (promocion.TipoCondicion)
            {
                case "Siempre": return true;
                case "DiaDeSemana":
                    int diaSemanaActual = (int)fecha.DayOfWeek == 0 ? 7 : (int)fecha.DayOfWeek;
                    return promocion.ValorCondicion.HasValue && promocion.ValorCondicion.Value == diaSemanaActual;
                case "PrimerDiaSemanaDelMes":
                    int diaSemanaActualMes = (int)fecha.DayOfWeek == 0 ? 7 : (int)fecha.DayOfWeek;
                    return promocion.ValorCondicion.HasValue && promocion.ValorCondicion.Value == diaSemanaActualMes && fecha.Day <= 7;
                default: return false;
            }
        }

        private string GetCharDiaSemana(DayOfWeek dia) => dia switch
        {
            DayOfWeek.Sunday => "Dom",
            DayOfWeek.Monday => "Lun",
            DayOfWeek.Tuesday => "Mar",
            DayOfWeek.Wednesday => "Mie",
            DayOfWeek.Thursday => "Jue",
            DayOfWeek.Friday => "Vie",
            DayOfWeek.Saturday => "Sab",
            _ => ""
        };

        private decimal GetPrecioDelDia(Precio precio, DayOfWeek dia) => dia switch
        {
            DayOfWeek.Sunday => precio.Domingo ?? 0,
            DayOfWeek.Monday => precio.Lunes ?? 0,
            DayOfWeek.Tuesday => precio.Martes ?? 0,
            DayOfWeek.Wednesday => precio.Miercoles ?? 0,
            DayOfWeek.Thursday => precio.Jueves ?? 0,
            DayOfWeek.Friday => precio.Viernes ?? 0,
            DayOfWeek.Saturday => precio.Sabado ?? 0,
            _ => 0
        };

        private decimal CalcularDescuentoPromociones(decimal importeBruto, IEnumerable<Promocion> promociones)
        {
            return promociones.Where(p => p.TipoEfecto.Contains("Descuento")).Sum(p =>
               p.TipoEfecto == "DescuentoPorcentajeTotal"
               ? (importeBruto * p.ValorEfecto) / 100
               : p.ValorEfecto
            );
        }
    }
}