using FrameworkBLL.ActionProcessor; using FrameworkBLL.EIPConversation.EIPHandlers.EIPEngine.FlowEngine; using FrameworkDAL.DTO.EIPConversation; using GB5Shared.DTO.Framework.Login; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; using System.Net; using System.Net.Mail; using System.Threading; using System.Threading.Tasks; namespace FrameworkBLL.EIPConversation.EIPHandlers.ChannelHandler { /// /// Real SMTP send via the same Office365 account already configured for /// FrameworkBLL.ActionProcessor.EmailActionHandler (Configure<SmtpSettings> in Program.cs). /// The EIP-side FrameworkBLL.MessageHub.MessageHubPlatforms.EmailSettings/EmailPlatform is a /// separate, never-configured pipeline — intentionally not reused here. /// public class EmailChannelHandler : IEIPChannelHandler { public string ChannelName => "EMAIL"; private readonly ILogger _logger; private readonly SmtpSettings _settings; public EmailChannelHandler(ILogger logger, IOptions settings) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _settings = settings?.Value ?? throw new ArgumentNullException(nameof(settings)); } public bool CanHandle(string channel) => string.Equals(channel, ChannelName, StringComparison.OrdinalIgnoreCase); public async Task SendAsync( EIPResponseContext context, LoginDTO loginDTO, CancellationToken cancellationToken) { if (context == null) throw new ArgumentNullException(nameof(context)); if (loginDTO == null) throw new ArgumentNullException(nameof(loginDTO)); if (context.StructuredData != null) { var formatted = EIPTextFormatter.FormatAsText(context.StructuredData); context.Message = string.IsNullOrWhiteSpace(context.Message) ? formatted : $"{context.Message}\n{formatted}"; } if (string.IsNullOrWhiteSpace(context.Recipient)) throw new ArgumentException("Email recipient cannot be empty."); if (string.IsNullOrWhiteSpace(_settings.Host)) return new EIPChannelResponseDTO { IsSent = false, Status = "SMTP host not configured" }; var stopwatch = System.Diagnostics.Stopwatch.StartNew(); try { _logger.LogInformation("EMAIL SEND START | Tenant={Tenant} | To={Recipient}", context.TenantId, context.Recipient); cancellationToken.ThrowIfCancellationRequested(); using var smtpClient = new SmtpClient(_settings.Host, _settings.Port) { Credentials = new NetworkCredential(_settings.User, _settings.Password), EnableSsl = _settings.EnableSsl }; using var mailMessage = new MailMessage { From = new MailAddress(_settings.User), Subject = "GB5 Notification", Body = context.Message ?? string.Empty, IsBodyHtml = false }; mailMessage.To.Add(context.Recipient); await smtpClient.SendMailAsync(mailMessage, cancellationToken).ConfigureAwait(false); stopwatch.Stop(); var response = new EIPChannelResponseDTO { IsSent = true, ExternalMessageId = Guid.NewGuid().ToString(), Status = "SENT" }; _logger.LogInformation( "EMAIL SEND SUCCESS | Tenant={Tenant} | MessageId={MessageId} | DurationMs={Duration}", context.TenantId, response.ExternalMessageId, stopwatch.ElapsedMilliseconds); return response; } catch (OperationCanceledException) { stopwatch.Stop(); _logger.LogWarning("EMAIL SEND CANCELLED | Tenant={Tenant} | DurationMs={Duration}", context?.TenantId, stopwatch.ElapsedMilliseconds); throw; } catch (Exception ex) { stopwatch.Stop(); _logger.LogError(ex, "EMAIL SEND FAILED | Tenant={Tenant} | DurationMs={Duration}", context?.TenantId, stopwatch.ElapsedMilliseconds); return new EIPChannelResponseDTO { IsSent = false, Status = "FAILED", ErrorMessage = ex.Message }; } } } }