using HandlebarsDotNet; using StackExchange.Redis; namespace GB5Shared.Export.HybridReport { public class TemplateEngine { private readonly IHandlebars _hb; private readonly IDatabase _cache; public TemplateEngine(string redisConnection) { _hb = Handlebars.Create(); RegisterHelpers(); var mux = ConnectionMultiplexer.Connect(redisConnection); _cache = mux.GetDatabase(); } public async Task RenderTemplateAsync(string templateName, object context, string tenantId, int version = 1) { var cacheKey = $"tpl:{templateName}:v{version}"; // Try get template from Redis var templateSource = await _cache.StringGetAsync(cacheKey); if (templateSource.IsNullOrEmpty) { var path = Path.Combine("reports", "templates", templateName + ".hbs"); if (!File.Exists(path)) throw new FileNotFoundException($"Template file not found: {path}"); templateSource = await File.ReadAllTextAsync(path); await _cache.StringSetAsync(cacheKey, templateSource, TimeSpan.FromHours(6)); } var template = _hb.Compile(templateSource); // Get tenant metadata from Redis var tenantJson = await _cache.StringGetAsync($"tenant:meta:{tenantId}"); object? tenantObj = null; if (!tenantJson.IsNullOrEmpty) { tenantObj = Newtonsoft.Json.JsonConvert.DeserializeObject(tenantJson!); } // Build the context for Handlebars var finalContext = new { Tenant = tenantObj, Data = context, Now = DateTime.UtcNow }; return template(finalContext); } private void RegisterHelpers() { _hb.RegisterHelper("formatDate", (w, c, p) => { if (p.Length >= 1 && DateTime.TryParse(p[0]?.ToString(), out var dt)) { var fmt = p.Length > 1 ? p[1]?.ToString() : "dd-MMM-yyyy"; w.WriteSafeString(dt.ToString(fmt)); } }); _hb.RegisterHelper("formatCurrency", (w, c, p) => { if (decimal.TryParse(p[0]?.ToString(), out var val)) w.WriteSafeString(val.ToString("N2")); }); // sum helper _hb.RegisterHelper("sum", (w, c, p) => { var arr = p[0] as IEnumerable; var fld = p[1]?.ToString(); decimal total = 0; if (arr != null) { foreach (var it in arr) { var dict = it as IDictionary; if (dict != null && dict.ContainsKey(fld!)) { total += Convert.ToDecimal(dict[fld!]); } } } w.WriteSafeString(total.ToString("N2")); }); } } }