using Markdig;
using Microsoft.Extensions.Caching.Memory;
using QRRapidoApp.Models;
using QRRapidoApp.Models.ViewModels;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

namespace QRRapidoApp.Services
{
    public class MarkdownService : IMarkdownService
    {
        private readonly IMemoryCache _cache;
        private readonly IWebHostEnvironment _env;
        private readonly ILogger<MarkdownService> _logger;
        private readonly MarkdownPipeline _pipeline;
        private readonly IDeserializer _yamlDeserializer;
        private const string CACHE_KEY_PREFIX = "Tutorial_";
        private const string CACHE_KEY_ALL = "AllTutorials_";
        private const int CACHE_DURATION_HOURS = 1;

        public MarkdownService(IMemoryCache cache, IWebHostEnvironment env, ILogger<MarkdownService> logger)
        {
            _cache = cache;
            _env = env;
            _logger = logger;

            // Configure Markdig pipeline for advanced markdown features
            _pipeline = new MarkdownPipelineBuilder()
                .UseAdvancedExtensions()
                .UseAutoLinks()
                .UseEmojiAndSmiley()
                .Build();

            // Configure YAML deserializer
            _yamlDeserializer = new DeserializerBuilder()
                .WithNamingConvention(CamelCaseNamingConvention.Instance)
                .IgnoreUnmatchedProperties()
                .Build();
        }

        public async Task<ArticleViewModel?> GetArticleAsync(string slug, string culture, string contentFolder = "Tutoriais")
        {
            var cacheKey = $"{CACHE_KEY_PREFIX}{contentFolder}_{culture}_{slug}";

            // Try get from cache
            if (_cache.TryGetValue(cacheKey, out ArticleViewModel? cachedArticle))
            {
                _logger.LogInformation("Article served from cache: {Slug} ({Culture})", slug, culture);
                return cachedArticle;
            }

            try
            {
                var contentPath = Path.Combine(_env.ContentRootPath, "Content", contentFolder);
                var fileName = $"{slug}.{culture}.md";
                var filePath = Path.Combine(contentPath, fileName);

                if (!File.Exists(filePath))
                {
                    _logger.LogWarning("Article file not found: {FilePath}", filePath);
                    return null;
                }

                var fileContent = await File.ReadAllTextAsync(filePath);
                var article = ParseMarkdownWithFrontmatter(fileContent, slug);

                if (article == null)
                {
                    _logger.LogError("Failed to parse article: {Slug} ({Culture})", slug, culture);
                    return null;
                }

                // Set file metadata
                var fileInfo = new FileInfo(filePath);
                article.LastModified = fileInfo.LastWriteTimeUtc;
                article.Metadata.Culture = culture;

                // Cache the article
                var cacheOptions = new MemoryCacheEntryOptions()
                    .SetAbsoluteExpiration(TimeSpan.FromHours(CACHE_DURATION_HOURS));

                _cache.Set(cacheKey, article, cacheOptions);

                _logger.LogInformation("Article loaded and cached: {Slug} ({Culture})", slug, culture);
                return article;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error loading article: {Slug} ({Culture})", slug, culture);
                return null;
            }
        }

        public async Task<List<ArticleMetadata>> GetAllArticlesAsync(string culture, string contentFolder = "Tutoriais")
        {
            var cacheKey = $"{CACHE_KEY_ALL}{contentFolder}_{culture}";

            // Try get from cache
            if (_cache.TryGetValue(cacheKey, out List<ArticleMetadata>? cachedList))
            {
                _logger.LogInformation("Articles list served from cache ({Culture})", culture);
                return cachedList ?? new List<ArticleMetadata>();
            }

            try
            {
                var articles = new List<ArticleMetadata>();
                var contentPath = Path.Combine(_env.ContentRootPath, "Content", contentFolder);

                if (!Directory.Exists(contentPath))
                {
                    _logger.LogWarning("Tutoriais directory not found: {Path}", contentPath);
                    return articles;
                }

                var pattern = $"*.{culture}.md";
                var files = Directory.GetFiles(contentPath, pattern);

                foreach (var file in files)
                {
                    try
                    {
                        var fileContent = await File.ReadAllTextAsync(file);
                        var slug = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(file));
                        var article = ParseMarkdownWithFrontmatter(fileContent, slug);

                        if (article?.Metadata != null)
                        {
                            article.Metadata.Culture = culture;
                            article.Metadata.Slug = slug;
                            articles.Add(article.Metadata);
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "Error parsing article file: {File}", file);
                    }
                }

                // Sort by date (newest first)
                articles = articles.OrderByDescending(a => a.Date).ToList();

                // Cache the list
                var cacheOptions = new MemoryCacheEntryOptions()
                    .SetAbsoluteExpiration(TimeSpan.FromHours(CACHE_DURATION_HOURS));

                _cache.Set(cacheKey, articles, cacheOptions);

                _logger.LogInformation("Loaded {Count} articles for culture {Culture}", articles.Count, culture);
                return articles;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error loading articles list for culture {Culture}", culture);
                return new List<ArticleMetadata>();
            }
        }

        public async Task<List<ArticleMetadata>> GetAllArticlesForSitemapAsync()
        {
            try
            {
                var allArticles = new List<ArticleMetadata>();
                var contentPath = Path.Combine(_env.ContentRootPath, "Content", "Tutoriais");

                if (!Directory.Exists(contentPath))
                {
                    _logger.LogWarning("Tutoriais directory not found for sitemap: {Path}", contentPath);
                    return allArticles;
                }

                var files = Directory.GetFiles(contentPath, "*.md");

                foreach (var file in files)
                {
                    try
                    {
                        var fileContent = await File.ReadAllTextAsync(file);
                        var fileName = Path.GetFileName(file);

                        // Extract slug and culture from filename (e.g., "como-criar-qr.pt-BR.md")
                        var parts = fileName.Replace(".md", "").Split('.');
                        if (parts.Length < 2) continue;

                        var slug = parts[0];
                        var culture = parts[1];

                        var article = ParseMarkdownWithFrontmatter(fileContent, slug);

                        if (article?.Metadata != null)
                        {
                            article.Metadata.Culture = culture;
                            article.Metadata.Slug = slug;
                            var fileInfo = new FileInfo(file);
                            article.Metadata.LastMod = fileInfo.LastWriteTimeUtc;
                            allArticles.Add(article.Metadata);
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "Error parsing article for sitemap: {File}", file);
                    }
                }

                _logger.LogInformation("Loaded {Count} total articles for sitemap", allArticles.Count);
                return allArticles;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error loading articles for sitemap");
                return new List<ArticleMetadata>();
            }
        }

        private ArticleViewModel? ParseMarkdownWithFrontmatter(string content, string slug)
        {
            try
            {
                // Extract frontmatter
                if (!content.StartsWith("---"))
                {
                    _logger.LogWarning("Article missing frontmatter: {Slug}", slug);
                    return null;
                }

                var endOfFrontmatter = content.IndexOf("---", 3);
                if (endOfFrontmatter == -1)
                {
                    _logger.LogWarning("Article frontmatter not closed: {Slug}", slug);
                    return null;
                }

                var frontmatterYaml = content.Substring(3, endOfFrontmatter - 3).Trim();
                var markdownContent = content.Substring(endOfFrontmatter + 3).Trim();

                // Parse YAML frontmatter
                var metadata = _yamlDeserializer.Deserialize<ArticleMetadata>(frontmatterYaml);

                if (metadata == null)
                {
                    _logger.LogWarning("Failed to deserialize frontmatter: {Slug}", slug);
                    return null;
                }

                // Calculate reading time (average 200 words per minute)
                var wordCount = markdownContent.Split(new[] { ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries).Length;
                metadata.ReadingTimeMinutes = Math.Max(1, (int)Math.Ceiling(wordCount / 200.0));

                // Convert Markdown to HTML
                var htmlContent = Markdown.ToHtml(markdownContent, _pipeline);

                return new ArticleViewModel
                {
                    Metadata = metadata,
                    HtmlContent = htmlContent,
                    Slug = slug,
                    LastModified = metadata.LastMod
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error parsing markdown for {Slug}", slug);
                return null;
            }
        }
    }
}
