using FrameworkBLL.EIPConversation.EIPHandlers.EIPConversationBLL; using FrameworkDAL.DTO.EIPConversation; using FrameworkDAL.DTO.MessageHub; using GB5Shared.DTO.Framework.Login; using Microsoft.Extensions.Logging; namespace FrameworkBLL.EIPConversation.EIPHandlers.EIPWebhook { /// /// Webhook Orchestrator /// Handles incoming external messages (WhatsApp, etc.) /// and forwards to Conversation Engine /// public class EIPWebhookBLL : IEIPWebhookBLL { private readonly IEIPConversationBLL _conversationBLL; private readonly ILogger _logger; public EIPWebhookBLL( IEIPConversationBLL conversationBLL, ILogger logger) { _conversationBLL = conversationBLL ?? throw new ArgumentNullException(nameof(conversationBLL)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } /// /// Main webhook entry point /// public async Task HandleWebhookAsync( MessageHubWebhookDTO webhookDTO, LoginDTO loginDTO, CancellationToken cancellationToken) { if (webhookDTO == null) throw new ArgumentNullException(nameof(webhookDTO)); if (loginDTO == null) throw new ArgumentNullException(nameof(loginDTO)); try { var messages = webhookDTO.Messages ?? new List(); _logger.LogInformation( "WEBHOOK START | MessageCount={Count} | Tenant={Tenant}", messages.Count, loginDTO.ClientId); foreach (var msg in messages) { if (msg == null) continue; var content = ExtractMessageContent(msg); if (string.IsNullOrWhiteSpace(msg.From)) { _logger.LogWarning("Skipping message with empty sender."); continue; } if (string.IsNullOrWhiteSpace(content)) { _logger.LogWarning( "Skipping empty message | From={From} | Type={Type}", msg.From, msg.Type); continue; } // ✅ FIXED DTO MAPPING var conversationDTO = new EIPConversationDTO { TenantId = loginDTO.ClientId, // ✅ FIX 1 ChannelType = EIPChannelType.WhatsApp, // ✅ FIX 2 Channel = "WHATSAPP", // ✅ string channel for handlers UserIdentifier = msg.From, Message = content }; _logger.LogInformation( "FORWARD TO ENGINE | User={User} | Message={Message}", conversationDTO.UserIdentifier, conversationDTO.Message); var result = await _conversationBLL.HandleIncomingConversationAsync( conversationDTO, loginDTO, cancellationToken); _logger.LogInformation( "ENGINE RESPONSE | User={User} | Step={Step}", conversationDTO.UserIdentifier, result?.CurrentStepCode); } _logger.LogInformation("WEBHOOK COMPLETE"); } catch (Exception ex) { _logger.LogError(ex, "WEBHOOK ERROR | Tenant={Tenant}", loginDTO.ClientId); throw; } } /// /// Extract message content from webhook /// Supports: /// - text /// - button /// - interactive (list / reply) /// private static string ExtractMessageContent(MessageWebhookDTO msg) { if (msg == null) return string.Empty; return msg.Type?.ToLower() switch { "text" => msg.Text?.Body ?? string.Empty, "button" => msg.Button?.Text ?? msg.Button?.Payload ?? string.Empty, "interactive" => msg.Interactive?.ButtonReply?.Title ?? msg.Interactive?.ListReply?.Title ?? string.Empty, _ => string.Empty }; } } }