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 string _casesPath;
    private const string CACHE_KEY = "cases_retail";
    private const int CACHE_MINUTES = 60;

    public CaseService(IMarkdownService markdownService, IMemoryCache cache, IWebHostEnvironment environment)
    {
        _markdownService = markdownService;
        _cache = cache;
        _environment = environment;
        _casesPath = Path.Combine(_environment.ContentRootPath, "Content", "Cases", "Retail");
    }

    public async Task<List<CaseModel>> GetAllCasesAsync()
    {
        if (_cache.TryGetValue(CACHE_KEY, out List<CaseModel>? cachedCases) && cachedCases != null)
        {
            return cachedCases;
        }

        var cases = new List<CaseModel>();

        if (!Directory.Exists(_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);
            }
        }

        cases = cases.OrderBy(c => c.Metadata.Order)
                     .ThenByDescending(c => c.Metadata.Date)
                     .ToList();

        _cache.Set(CACHE_KEY, cases, TimeSpan.FromMinutes(CACHE_MINUTES));

        return cases;
    }

    public async Task<List<CaseModel>> GetFeaturedCasesAsync()
    {
        var allCases = await GetAllCasesAsync();
        return allCases.Where(c => c.Metadata.Featured).ToList();
    }

    public async Task<CaseModel?> GetCaseBySlugAsync(string slug)
    {
        var allCases = await GetAllCasesAsync();
        return allCases.FirstOrDefault(c => c.Metadata.Slug.Equals(slug, StringComparison.OrdinalIgnoreCase));
    }

    public async Task<List<string>> GetAllTagsAsync()
    {
        var allCases = await GetAllCasesAsync();
        return allCases.SelectMany(c => c.Metadata.Tags).Distinct().OrderBy(t => t).ToList();
    }

    public async Task<List<CaseModel>> GetCasesByTagAsync(string tag)
    {
        var allCases = await GetAllCasesAsync();
        return allCases.Where(c => c.Metadata.Tags.Contains(tag, StringComparer.OrdinalIgnoreCase)).ToList();
    }

    private async Task<CaseModel?> 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"),
                DeviceModel = GetStringValue(frontMatter, "device_model"),
                EstimatedSavings = GetDecimalValue(frontMatter, "estimated_savings"),
                Category = GetStringValue(frontMatter, "category"),
                Thumbnail = GetStringValue(frontMatter, "thumbnail"),
                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")
            };

            return new CaseModel
            {
                Metadata = metadata,
                ContentHtml = _markdownService.ConvertToHtml(bodyContent),
                ContentMarkdown = bodyContent
            };
        }
        catch { return null; }
    }

    private string GetStringValue(Dictionary<string, object> dict, string key) => 
        dict.ContainsKey(key) ? dict[key]?.ToString() ?? string.Empty : string.Empty;

    private decimal GetDecimalValue(Dictionary<string, object> dict, string key) =>
        dict.ContainsKey(key) && decimal.TryParse(dict[key]?.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out decimal res) ? res : 0;

    private List<string> GetListValue(Dictionary<string, object> dict, string key) =>
        dict.ContainsKey(key) && dict[key] is List<object> list ? list.Select(i => i?.ToString() ?? "").ToList() : new();

    private bool GetBoolValue(Dictionary<string, object> dict, string key) =>
        dict.ContainsKey(key) && (dict[key]?.ToString()?.ToLower() == "true" || dict[key]?.ToString() == "1");

    private int GetIntValue(Dictionary<string, object> dict, string key) =>
        dict.ContainsKey(key) && int.TryParse(dict[key]?.ToString(), out int res) ? res : 0;

    private DateTime GetDateValue(Dictionary<string, object> dict, string key) =>
        dict.ContainsKey(key) && DateTime.TryParse(dict[key]?.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime res) ? res : DateTime.MinValue;
}
