using FrameworkBLL.MessageHub.MessageHubPlatforms; using FrameworkDAL.CustomCode.DirectAction; using FrameworkDAL.DTO.EIPConversation; using FrameworkDAL.DTO.MessageHub.MessageHubGenerator; using GB5Shared.DTO.Framework.Login; using Microsoft.Extensions.Logging; using Polly; using Polly.Retry; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using static FrameworkBLL.MessageHub.MessageHubPlatforms.IMessageHubPlatform; namespace FrameworkBLL.MessageHub.MessageHubEngine { public static class MessageHubActionType { public const int Text = 0; public const int WhatsApp = 7; public const int Teams = 8; public const int Telegram = 9; public const int Slack = 10; } public class MessageHubGeneratorEngine : IMessageHubGeneratorEngine { private readonly IPostmanPlatform _postmanPlatform; private readonly IWhatsAppPlatform _whatsAppPlatform; private readonly ITeamsPlatform _teamsPlatform; private readonly ITelegramPlatform _telegramPlatform; private readonly ISlackPlatform _slackPlatform; private readonly IDirectActionConfigDAL _configDAL; private readonly ILogger _logger; private readonly AsyncRetryPolicy _retryPolicy; public MessageHubGeneratorEngine( IPostmanPlatform postmanPlatform, IWhatsAppPlatform whatsAppPlatform, ITeamsPlatform teamsPlatform, ITelegramPlatform telegramPlatform, ISlackPlatform slackPlatform, IDirectActionConfigDAL configDAL, ILogger logger) { _postmanPlatform = postmanPlatform ?? throw new ArgumentNullException(nameof(postmanPlatform)); _whatsAppPlatform = whatsAppPlatform ?? throw new ArgumentNullException(nameof(whatsAppPlatform)); _teamsPlatform = teamsPlatform ?? throw new ArgumentNullException(nameof(teamsPlatform)); _telegramPlatform = telegramPlatform ?? throw new ArgumentNullException(nameof(telegramPlatform)); _slackPlatform = slackPlatform ?? throw new ArgumentNullException(nameof(slackPlatform)); _configDAL = configDAL ?? throw new ArgumentNullException(nameof(configDAL)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _retryPolicy = Policy .Handle() .WaitAndRetryAsync( 3, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)), (result, delay, retryCount, context) => { _logger.LogWarning(result.Exception, "[Retry] Attempt {Retry}/3 | Delay={Delay}s | ActionType={ActionType} | Recipient={Recipient} | TemplateId={TemplateId}", retryCount, delay.TotalSeconds, context.TryGetValue("ActionType", out var act) ? act : "N/A", context.TryGetValue("Recipient", out var rec) ? rec : "N/A", context.TryGetValue("TemplateId", out var tid) ? tid : "N/A"); }); } // ====================================================== // MAIN ENTRY // ====================================================== public async Task ProcessAsync(MessageHubDTO message, LoginDTO login, CancellationToken ct) { if (message == null) throw new ArgumentNullException(nameof(message)); if (login == null) throw new ArgumentNullException(nameof(login)); var sw = Stopwatch.StartNew(); try { // Validate required fields ValidateMessage(message); LogMessageDetails(message); var context = new Context { ["ActionType"] = message.ActionType, ["Recipient"] = message.Recipient, ["TemplateId"] = message.TemplateId }; // Execute platform with retry var response = await _retryPolicy.ExecuteAsync( async ctx => await ExecuteByActionType(message, login, ct), context); sw.Stop(); _logger.LogInformation("[Engine] Completed | Success={Success} | Duration={Duration}ms", response?.Success, sw.ElapsedMilliseconds); return response ?? CreateFailure("Null response from engine."); } catch (OperationCanceledException) { sw.Stop(); _logger.LogWarning("[Engine] ProcessAsync was cancelled | Duration={Duration}ms", sw.ElapsedMilliseconds); return CreateFailure("Operation cancelled."); } catch (Exception ex) { sw.Stop(); _logger.LogError(ex, "[Engine] Fatal error | Duration={Duration}ms", sw.ElapsedMilliseconds); return CreateFailure(ex.Message); } } // ====================================================== // ROUTER BY ACTION TYPE // ====================================================== private async Task ExecuteByActionType( MessageHubDTO message, LoginDTO login, CancellationToken ct) { switch (message.ActionType) { case MessageHubActionType.Text: return await ExecuteSafe(() => _postmanPlatform.SendPostmanTemplate(message, login, ct)); case MessageHubActionType.WhatsApp: message.DefaultEmptyParametersToNone(); message.MessageText = message.GetSafeMessageText(); await InjectDirectActionButtonsAsync(message, login, ct); _logger.LogDebug("[WhatsApp] Template={Template} | Recipient={Recipient} | Payload={Payload}", message.Subject, message.Recipient, JsonConvert.SerializeObject(message, Formatting.Indented)); return await ExecuteSafe(() => _whatsAppPlatform.SendWhatsappTemplate(message, login, ct)); case MessageHubActionType.Teams: await InjectDirectActionButtonsAsync(message, login, ct); return await ExecuteSafe(() => _teamsPlatform.SendTeamsTemplate(message, login, ct)); case MessageHubActionType.Telegram: return await ExecuteSafe(() => _telegramPlatform.SendTelegramTemplate(message, login, ct)); case MessageHubActionType.Slack: return await ExecuteSafe(() => _slackPlatform.SendSlackTemplate(message, login, ct)); default: return CreateFailure($"Unsupported ActionType {message.ActionType}"); } } // ====================================================== // SAFE EXECUTION WRAPPER // ====================================================== private async Task ExecuteSafe(Func> action) { try { var result = await action(); return result ?? CreateFailure("Platform returned null response."); } catch (Exception ex) { _logger.LogError(ex, "[Engine] Platform execution error"); return CreateFailure(ex.Message); } } // ====================================================== // DIRECT ACTION BUTTONS // ====================================================== /// /// Populates message.Buttons from MDIRECTACTIONDETAIL when DirectActionId > -1. /// Buttons already set on the message are preserved and merged. /// private async Task InjectDirectActionButtonsAsync( MessageHubDTO message, LoginDTO login, CancellationToken ct) { if (message.DirectActionId <= -1) return; try { var details = await _configDAL .GetDetailsByDirectActionIdAsync(message.DirectActionId, login, ct) .ConfigureAwait(false); var actionButtons = details .Where(d => !string.IsNullOrWhiteSpace(d.ButtonPayloadPrefix)) .Select(d => new TemplateButtonDTO { Id = $"{d.ButtonPayloadPrefix}_{message.ContextId}", Title = d.ActionLabel }) .ToList(); if (actionButtons.Count == 0) return; message.Buttons ??= new List(); message.Buttons.AddRange(actionButtons); _logger.LogInformation( "[Engine] DirectAction buttons injected | DirectActionId={Id} ContextId={Ctx} Count={Count}", message.DirectActionId, message.ContextId, actionButtons.Count); } catch (Exception ex) { _logger.LogWarning(ex, "[Engine] Failed to inject DirectAction buttons | DirectActionId={Id}", message.DirectActionId); } } // ====================================================== // VALIDATION // ====================================================== private void ValidateMessage(MessageHubDTO message) { if (string.IsNullOrWhiteSpace(message.Recipient)) throw new InvalidOperationException("Recipient is required."); if (message.ActionType < 0) throw new InvalidOperationException("Invalid ActionType."); if (message.ActionType == MessageHubActionType.WhatsApp && string.IsNullOrWhiteSpace(message.Subject)) throw new InvalidOperationException("WhatsApp TemplateName (Subject) is required."); } // ====================================================== // LOGGING // ====================================================== private void LogMessageDetails(MessageHubDTO message) { _logger.LogDebug("[Message] TemplateId={TemplateId} | Recipient={Recipient} | ActionType={ActionType}", message.TemplateId, message.Recipient, message.ActionType); } // ====================================================== // FAILURE RESPONSE // ====================================================== private static MessageHubResponseDTO CreateFailure(string error) { return new MessageHubResponseDTO { Success = false, ErrorMessage = error }; } } }