using GestionIntegral.Api.Data.Repositories.Comunicaciones; using GestionIntegral.Api.Models.Comunicaciones; using MailKit.Net.Smtp; using MailKit.Security; using Microsoft.Extensions.Options; using MimeKit; namespace GestionIntegral.Api.Services.Comunicaciones { public class EmailService : IEmailService { private readonly MailSettings _mailSettings; private readonly ILogger _logger; private readonly IEmailLogRepository _emailLogRepository; public EmailService( IOptions mailSettings, ILogger logger, IEmailLogRepository emailLogRepository) // Inyectar el nuevo repositorio { _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) { 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(); // Llamar al método centralizado de envío y logging await SendAndLogEmailAsync(email, origen, referenciaId, idUsuarioDisparo); } 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) { 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(); // Llamar al método centralizado de envío y logging await SendAndLogEmailAsync(email, origen, referenciaId, idUsuarioDisparo); } /// /// Método privado que centraliza el envío de correo y el registro de logs. /// private async Task SendAndLogEmailAsync(MimeMessage emailMessage, string? origen, string? referenciaId, int? idUsuarioDisparo) { 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 }; using var smtp = new SmtpClient(); try { 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); } // Capturamos excepciones específicas de MailKit para obtener errores más detallados. catch (SmtpCommandException scEx) { // Este error ocurre cuando el servidor SMTP rechaza un comando. // Es el caso más común para direcciones de email inválidas que son rechazadas inmediatamente. _logger.LogError(scEx, "Error de comando SMTP al enviar a {Destinatario}. StatusCode: {StatusCode}", destinatario, scEx.StatusCode); log.Estado = "Fallido"; log.Error = $"Error del servidor: ({scEx.StatusCode}) {scEx.Message}"; throw; } catch (AuthenticationException authEx) { // Error específico de autenticación. _logger.LogError(authEx, "Error de autenticación con el servidor SMTP."); log.Estado = "Fallido"; log.Error = "Error de autenticación. Revise las credenciales de correo."; throw; } catch (Exception ex) // Captura genérica para cualquier otro problema (conexión, etc.) { _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); } // Guardar el log en la base de datos, sin importar el resultado del envío 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); } } } } }