using CarneiroTech.Models; using Microsoft.Extensions.Caching.Memory; using System.Globalization; namespace CarneiroTech.Services; public class CaseService : ICaseService { private readonly IMarkdownService _markdownService; private readonly IMemoryCache _cache; private readonly IWebHostEnvironment _environment; private readonly ILanguageService _languageService; private readonly IHttpContextAccessor _httpContextAccessor; private readonly string _casesBasePath; private const string CACHE_KEY_PREFIX = "cases_"; private const int CACHE_MINUTES = 60; public CaseService(IMarkdownService markdownService, IMemoryCache cache, IWebHostEnvironment environment, ILanguageService languageService, IHttpContextAccessor httpContextAccessor) { _markdownService = markdownService; _cache = cache; _environment = environment; _languageService = languageService; _httpContextAccessor = httpContextAccessor; _casesBasePath = Path.Combine(_environment.ContentRootPath, "Content", "Cases"); } private string GetCurrentLanguage() { if (_httpContextAccessor.HttpContext != null) { return _languageService.GetCurrentLanguage(_httpContextAccessor.HttpContext); } return "pt"; // Default fallback } private string GetCasesPath(string language) { return Path.Combine(_casesBasePath, language); } public async Task> GetAllCasesAsync() { var language = GetCurrentLanguage(); var cacheKey = $"{CACHE_KEY_PREFIX}{language}"; if (_cache.TryGetValue(cacheKey, out List? cachedCases) && cachedCases != null) { return cachedCases; } var cases = new List(); var casesPath = GetCasesPath(language); if (!Directory.Exists(casesPath)) { // Try Portuguese as fallback casesPath = GetCasesPath("pt"); if (!Directory.Exists(casesPath)) { Directory.CreateDirectory(casesPath); return cases; } } var markdownFiles = Directory.GetFiles(casesPath, "*.md"); foreach (var file in markdownFiles) { var caseModel = await ParseCaseFileAsync(file); if (caseModel != null) { cases.Add(caseModel); } } // Sort by order (ascending) and date (descending) cases = cases.OrderBy(c => c.Metadata.Order) .ThenByDescending(c => c.Metadata.Date) .ToList(); _cache.Set(cacheKey, cases, TimeSpan.FromMinutes(CACHE_MINUTES)); return cases; } public async Task> GetFeaturedCasesAsync() { var allCases = await GetAllCasesAsync(); return allCases.Where(c => c.Metadata.Featured).ToList(); } public async Task GetCaseBySlugAsync(string slug) { var allCases = await GetAllCasesAsync(); return allCases.FirstOrDefault(c => c.Metadata.Slug.Equals(slug, StringComparison.OrdinalIgnoreCase)); } public async Task> GetAllTagsAsync() { var allCases = await GetAllCasesAsync(); var tags = allCases.SelectMany(c => c.Metadata.Tags) .Distinct() .OrderBy(t => t) .ToList(); return tags; } public async Task> GetCasesByTagAsync(string tag) { var allCases = await GetAllCasesAsync(); return allCases.Where(c => c.Metadata.Tags.Contains(tag, StringComparer.OrdinalIgnoreCase)) .ToList(); } private async Task ParseCaseFileAsync(string filePath) { try { var content = await File.ReadAllTextAsync(filePath); var frontMatter = _markdownService.ParseFrontMatter(content, out string bodyContent); var metadata = new CaseMetadata { Title = GetStringValue(frontMatter, "title"), Slug = GetStringValue(frontMatter, "slug"), Summary = GetStringValue(frontMatter, "summary"), Client = GetStringValue(frontMatter, "client"), Industry = GetStringValue(frontMatter, "industry"), Timeline = GetStringValue(frontMatter, "timeline"), Role = GetStringValue(frontMatter, "role"), Image = GetStringValue(frontMatter, "image"), Tags = GetListValue(frontMatter, "tags"), Featured = GetBoolValue(frontMatter, "featured"), Order = GetIntValue(frontMatter, "order"), Date = GetDateValue(frontMatter, "date"), SeoTitle = GetStringValue(frontMatter, "seo_title"), SeoDescription = GetStringValue(frontMatter, "seo_description"), SeoKeywords = GetStringValue(frontMatter, "seo_keywords") }; var htmlContent = _markdownService.ConvertToHtml(bodyContent); return new CaseModel { Metadata = metadata, ContentHtml = htmlContent, ContentMarkdown = bodyContent }; } catch (Exception) { // Log error here if needed return null; } } private string GetStringValue(Dictionary dict, string key) { return dict.ContainsKey(key) ? dict[key]?.ToString() ?? string.Empty : string.Empty; } private List GetListValue(Dictionary dict, string key) { if (!dict.ContainsKey(key)) return new List(); if (dict[key] is List list) { return list.Select(item => item?.ToString() ?? string.Empty).ToList(); } return new List(); } private bool GetBoolValue(Dictionary dict, string key) { if (!dict.ContainsKey(key)) return false; var value = dict[key]?.ToString()?.ToLower(); return value == "true" || value == "yes" || value == "1"; } private int GetIntValue(Dictionary dict, string key) { if (!dict.ContainsKey(key)) return 0; if (int.TryParse(dict[key]?.ToString(), out int result)) { return result; } return 0; } private DateTime GetDateValue(Dictionary dict, string key) { if (!dict.ContainsKey(key)) return DateTime.MinValue; var dateString = dict[key]?.ToString(); if (DateTime.TryParse(dateString, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime result)) { return result; } return DateTime.MinValue; } }