using FrameworkDAL.DTO.MessageHub.MessageHubGenerator; using FrameworkDAL.Query.MessageHub; using GB5Shared.DTO.Framework.Login; using GB5Shared.QueryExecutor; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace FrameworkDAL.CustomCode.MessageHub.MessageHubGenerator { public class MessageHubGeneratorDAL : IMessageHubGeneratorDAL { private readonly IQueryExecutor _queryExecutor; private readonly ILogger _logger; public MessageHubGeneratorDAL( IQueryExecutor queryExecutor, ILogger logger) { _queryExecutor = queryExecutor ?? throw new ArgumentNullException(nameof(queryExecutor)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger.LogInformation("[MessageHubGeneratorDAL] Initialized successfully"); } public async Task> GenerateMessagesByEntityAsync(int entityId, LoginDTO loginDTO, CancellationToken ct) { _logger.LogInformation("[Start] GenerateMessagesByEntityAsync | EntityId={EntityId}, User={User}", entityId, loginDTO.UserName); try { // Fetch templates from DB var templates = await _queryExecutor.QueryAsync( loginDTO, MessageHubGeneratorQB.GET_TEMPLATES_BY_ENTITYID, new { EntityId = entityId }); if (!templates.Any()) { _logger.LogWarning("[Step 1] No templates found for EntityId={EntityId}", entityId); return new List(); } _logger.LogInformation("[Step 1] Found {Count} templates", templates.Count()); // Resolve full payload from DB var payload = await ResolvePayloadAsync(loginDTO, entityId); var result = new List(); foreach (var template in templates) { if (ct.IsCancellationRequested) { _logger.LogWarning("[Cancelled] GenerateMessagesByEntityAsync was cancelled"); ct.ThrowIfCancellationRequested(); } try { // Filter only variables used in template var usedVariables = Regex.Matches(template.BodyHtml ?? string.Empty, @"##(.*?)##") .Cast() .Select(m => m.Groups[1].Value) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); // Ensure recipient keys are always included var filteredPayload = payload .Where(kv => usedVariables.Contains(kv.Key, StringComparer.OrdinalIgnoreCase) || kv.Key.Equals("UserPrimaryMobile", StringComparison.OrdinalIgnoreCase) || kv.Key.Equals("UserPrimaryMail", StringComparison.OrdinalIgnoreCase)) .ToDictionary(kv => kv.Key, kv => kv.Value, StringComparer.OrdinalIgnoreCase); // Patch template JSON with filtered payload var patchedJson = PatchTemplateJson(template.BodyHtml, filteredPayload); // Extract recipient var recipient = ExtractRecipientFromJson(patchedJson) ?? ResolveRecipientFromPayload(filteredPayload); if (string.IsNullOrWhiteSpace(recipient)) { _logger.LogWarning("[Step 2] Recipient not found for TemplateId={TemplateId}", template.MailTemplateId); } // Convert JSON to safe text var safeMessageText = ConvertJsonToText(patchedJson); var messageDto = new MessageHubDTO { ActionId = template.ActionId, ActionType = template.ActionType, TemplateId = template.MailTemplateId, TemplateName = template.MailTemplateName, Subject = PatchTemplate(template.Subject, filteredPayload), MessageText = safeMessageText, Recipient = recipient, Parameters = filteredPayload, Payload = filteredPayload, TenantId = loginDTO.ClientId, CreatedById = loginDTO.UserId, CreatedOn = DateTime.UtcNow, ModifiedById = loginDTO.UserId, ModifiedOn = DateTime.UtcNow, Status = 1 }; result.Add(messageDto); // Log _logger.LogInformation( "[Template Prepared] TemplateId={TemplateId} | Recipient={Recipient}\nFiltered Payload:\n{FilteredPayload}\nMessageText:\n{MessageText}", template.MailTemplateId, recipient, JsonSerializer.Serialize(filteredPayload, new JsonSerializerOptions { WriteIndented = true }), safeMessageText ); } catch (Exception ex) { _logger.LogError(ex, "Error processing TemplateId={TemplateId}", template.MailTemplateId); } } _logger.LogInformation("[End] Total messages generated: {Count}", result.Count); return result; } catch (OperationCanceledException) { _logger.LogWarning("[Cancelled] GenerateMessagesByEntityAsync was cancelled during execution"); throw; } catch (Exception ex) { _logger.LogError(ex, "GenerateMessagesByEntityAsync failed for EntityId={EntityId}", entityId); throw; } } private async Task> ResolvePayloadAsync(LoginDTO loginDTO, int? partyId) { var dto = await _queryExecutor.QuerySingleAsync( loginDTO, MessageHubGeneratorQB.GET_MESSAGE_HUB_PATCH_QUERY, new { UserId = loginDTO.UserId, PartyId = partyId }); var payload = ConvertDtoToDictionary(dto); NormalizeMobileNumbers(payload); return payload; } private static Dictionary ConvertDtoToDictionary(MessageHubContextDTO dto) { var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); if (dto == null) return dict; foreach (var prop in typeof(MessageHubContextDTO).GetProperties()) dict[prop.Name] = prop.GetValue(dto); return dict; } private static string PatchTemplateJson(string templateJson, Dictionary payload) { if (string.IsNullOrWhiteSpace(templateJson)) return "{}"; return Regex.Replace(templateJson, @"##(.*?)##", match => { var key = match.Groups[1].Value; return payload.TryGetValue(key, out var val) ? val?.ToString() ?? string.Empty : string.Empty; }, RegexOptions.IgnoreCase); } private static string PatchTemplate(string template, Dictionary payload) { if (string.IsNullOrWhiteSpace(template)) return template; return Regex.Replace(template, @"##(.*?)##", match => { var key = match.Groups[1].Value; return payload.TryGetValue(key, out var val) ? val?.ToString() ?? string.Empty : string.Empty; }, RegexOptions.IgnoreCase); } private static string ExtractRecipientFromJson(string json) { try { using var doc = JsonDocument.Parse(json); var root = doc.RootElement; if (root.TryGetProperty("to", out var toProp)) return toProp.GetString(); } catch { } return null; } private static string ResolveRecipientFromPayload(Dictionary payload) { if (payload.TryGetValue("UserPrimaryMobile", out var mobile)) return mobile?.ToString(); if (payload.TryGetValue("UserPrimaryMail", out var mail)) return mail?.ToString(); return null; } private static void NormalizeMobileNumbers(Dictionary payload) { var mobileKeys = new[] { "UserPrimaryMobile", "EmployeePrimaryMobile", "VisitorMobileNo" }; foreach (var key in mobileKeys) { if (!payload.TryGetValue(key, out var value) || value == null) continue; payload[key] = NormalizeIndianMobile(value.ToString()); } } private static string NormalizeIndianMobile(string input) { if (string.IsNullOrWhiteSpace(input)) return string.Empty; var digits = Regex.Replace(input, @"\D", ""); if (digits.StartsWith("00")) digits = digits.Substring(2); if (digits.StartsWith("0") && digits.Length == 11) digits = digits.Substring(1); if (digits.Length == 10) digits = "91" + digits; if (digits.StartsWith("91") && digits.Length == 12) return digits; return digits; } private static string ConvertJsonToText(string json) { try { using var doc = JsonDocument.Parse(json); var root = doc.RootElement; var body = root.TryGetProperty("body", out var b) ? b.GetString() : ""; var buttons = root.TryGetProperty("buttons", out var btns) ? string.Join(", ", btns.EnumerateArray().Select(x => x.GetProperty("title").GetString())) : ""; return string.IsNullOrEmpty(buttons) ? body : $"{body}\nButtons: {buttons}"; } catch { return json; } } } }