using GestionIntegral.Api.Data.Repositories.Comunicaciones;
using GestionIntegral.Api.Models.Comunicaciones;
using MailKit.Net.Smtp;
using MailKit.Security;
using Microsoft.Extensions.Options;
using MimeKit;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

namespace GestionIntegral.Api.Services.Comunicaciones
{
    public class EmailService : IEmailService
    {
        private readonly MailSettings _mailSettings;
        private readonly ILogger<EmailService> _logger;
        private readonly IEmailLogRepository _emailLogRepository;

        public EmailService(
            IOptions<MailSettings> mailSettings, 
            ILogger<EmailService> logger,
            IEmailLogRepository emailLogRepository)
        {
            _mailSettings = mailSettings.Value;
            _logger = logger;
            _emailLogRepository = emailLogRepository;
        }

        public async Task EnviarEmailAsync(
            string destinatarioEmail, string destinatarioNombre, string asunto, string cuerpoHtml, 
            byte[]? attachment = null, string? attachmentName = null,
            string? origen = null, string? referenciaId = null, int? idUsuarioDisparo = null,
            int? idLoteDeEnvio = null)
        {
            var email = new MimeMessage();
            email.Sender = new MailboxAddress(_mailSettings.SenderName, _mailSettings.SenderEmail);
            email.From.Add(email.Sender);
            email.To.Add(new MailboxAddress(destinatarioNombre, destinatarioEmail));
            email.Subject = asunto;

            var builder = new BodyBuilder { HtmlBody = cuerpoHtml };
            if (attachment != null && !string.IsNullOrEmpty(attachmentName))
            {
                builder.Attachments.Add(attachmentName, attachment, ContentType.Parse("application/pdf"));
            }
            email.Body = builder.ToMessageBody();

            await SendAndLogEmailAsync(email, origen, referenciaId, idUsuarioDisparo, idLoteDeEnvio);
        }

        public async Task EnviarEmailConsolidadoAsync(
            string destinatarioEmail, string destinatarioNombre, string asunto, string cuerpoHtml, 
            List<(byte[] content, string name)> adjuntos,
            string? origen = null, string? referenciaId = null, int? idUsuarioDisparo = null,
            int? idLoteDeEnvio = null)
        {
            var email = new MimeMessage();
            email.Sender = new MailboxAddress(_mailSettings.SenderName, _mailSettings.SenderEmail);
            email.From.Add(email.Sender);
            email.To.Add(new MailboxAddress(destinatarioNombre, destinatarioEmail));
            email.Subject = asunto;

            var builder = new BodyBuilder { HtmlBody = cuerpoHtml };
            if (adjuntos != null)
            {
                foreach (var adjunto in adjuntos)
                {
                    builder.Attachments.Add(adjunto.name, adjunto.content, ContentType.Parse("application/pdf"));
                }
            }
            email.Body = builder.ToMessageBody();

            await SendAndLogEmailAsync(email, origen, referenciaId, idUsuarioDisparo, idLoteDeEnvio);
        }

        private async Task SendAndLogEmailAsync(MimeMessage emailMessage, string? origen, string? referenciaId, int? idUsuarioDisparo, int? idLoteDeEnvio)
        {
            var destinatario = emailMessage.To.Mailboxes.FirstOrDefault()?.Address ?? "desconocido";

            var log = new EmailLog
            {
                FechaEnvio = DateTime.Now,
                DestinatarioEmail = destinatario,
                Asunto = emailMessage.Subject,
                Origen = origen,
                ReferenciaId = referenciaId,
                IdUsuarioDisparo = idUsuarioDisparo,
                IdLoteDeEnvio = idLoteDeEnvio
            };

            using var smtp = new SmtpClient();
            try
            {
                // Se añade una política de validación de certificado personalizada.
                // Esto es necesario para entornos de desarrollo o redes internas donde
                // el nombre del host al que nos conectamos (ej. una IP) no coincide
                // con el nombre en el certificado SSL (ej. mail.eldia.com).
                smtp.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
                {
                    // Si no hay errores, el certificado es válido.
                    if (sslPolicyErrors == SslPolicyErrors.None)
                        return true;

                    // Si el único error es que el nombre no coincide (RemoteCertificateNameMismatch)
                    // Y el certificado es el que esperamos (emitido para "mail.eldia.com"),
                    // entonces lo aceptamos como válido.
                    if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch) && certificate != null && certificate.Subject.Contains("CN=mail.eldia.com"))
                    {
                        _logger.LogWarning("Se aceptó un certificado SSL con 'Name Mismatch' para el host de confianza 'mail.eldia.com'.");
                        return true;
                    }

                    // Para cualquier otro error, rechazamos el certificado.
                    _logger.LogError("Error de validación de certificado SSL: {Errors}", sslPolicyErrors);
                    return false;
                };

                await smtp.ConnectAsync(_mailSettings.SmtpHost, _mailSettings.SmtpPort, SecureSocketOptions.StartTls);
                await smtp.AuthenticateAsync(_mailSettings.SmtpUser, _mailSettings.SmtpPass);
                await smtp.SendAsync(emailMessage);
                
                log.Estado = "Enviado";
                _logger.LogInformation("Email enviado exitosamente a {Destinatario}. Asunto: {Asunto}", destinatario, emailMessage.Subject);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error general al enviar email a {Destinatario}. Asunto: {Asunto}", destinatario, emailMessage.Subject);
                log.Estado = "Fallido";
                log.Error = ex.Message;
                throw;
            }
            finally
            {
                if (smtp.IsConnected)
                {
                    await smtp.DisconnectAsync(true);
                }

                try
                {
                    await _emailLogRepository.CreateAsync(log);
                }
                catch (Exception logEx)
                {
                    _logger.LogError(logEx, "FALLO CRÍTICO: No se pudo guardar el log del email para {Destinatario}", destinatario);
                }
            }
        }
    }
}