using ladaexpress.vn.Infrastructures.DTO.Models.Seo;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using Lada.Framework.Data.Repositories;
using Lada.Framework.Data.Domains.Cms;
using Lada.Framework.Data.Domains.Hr;
using Microsoft.EntityFrameworkCore;
using Lada.Framework.Data.Enums;
using Lada.Framework.Infrastructures.Services;

namespace ladaexpress.vn.Infrastructures.Services
{
    /// <summary>
    /// Implementation của ISitemapService
    /// </summary>
    public class SitemapService : ISitemapService
    {
        private readonly IAppConfigService _appConfigService;
        private readonly IMemoryCache _memoryCache;
        private readonly IConfiguration _configuration;
        private readonly ILogger<SitemapService> _logger;
        private readonly IRepository<UrlRecord> _urlRecordRepository;
        private readonly IImageService _imageService;
        private readonly string _baseUrl;
        private readonly string _cacheKeyPrefix = "Sitemap_";

        public SitemapService(
            IAppConfigService appConfigService,
            IMemoryCache memoryCache,
            IConfiguration configuration,
            ILogger<SitemapService> logger,
            IRepository<UrlRecord> urlRecordRepository,
            IImageService imageService)
        {
            _appConfigService = appConfigService;
            _memoryCache = memoryCache;
            _configuration = configuration;
            _logger = logger;
            _urlRecordRepository = urlRecordRepository;
            _imageService = imageService;
            _baseUrl = _configuration["SeoSettings:BaseUrl"] ?? "https://ladaexpress.vn";
        }

        public async Task<string> GenerateSitemapXmlAsync()
        {
            try
            {
                var cacheKey = $"{_cacheKeyPrefix}Main";
                if (_memoryCache.TryGetValue(cacheKey, out string? cached) && !string.IsNullOrEmpty(cached))
                {
                    _logger.LogInformation("📋 Returning cached sitemap");
                    return cached;
                }

                _logger.LogInformation("🚀 Generating new sitemap with image support...");

                var sitemapItems = new List<SitemapItem>();

                // Collect all sitemap items
                sitemapItems.AddRange(await GetStaticPagesSitemapAsync());
                sitemapItems.AddRange(await GetProductsSitemapAsync());
                sitemapItems.AddRange(await GetPostsSitemapAsync());
                sitemapItems.AddRange(await GetJobPostsSitemapAsync());
                sitemapItems.AddRange(await GetCategoriesSitemapAsync());
                // Tags are excluded from sitemap

                // Generate XML
                var baseUrl = await GetConfigValueAsync("SEO.BaseUrl", _baseUrl);
                var xml = GenerateSitemapXml(sitemapItems, baseUrl);

                // Cache for 6 hours
                _memoryCache.Set(cacheKey, xml, TimeSpan.FromHours(6));

                var totalImages = sitemapItems.Sum(item => item.Images.Count);
                _logger.LogInformation("✅ Generated sitemap with {Count} URLs and {ImageCount} images", sitemapItems.Count, totalImages);
                return xml;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error generating sitemap XML");
                throw;
            }
        }

        public async Task<string> GenerateSitemapIndexXmlAsync()
        {
            try
            {
                var baseUrl = await GetConfigValueAsync("SEO.BaseUrl", _baseUrl);
                var sitemaps = new List<(string Url, DateTime LastModified)>
                {
                    ($"{baseUrl}/sitemap-static.xml", DateTime.Now),
                    ($"{baseUrl}/sitemap-products.xml", DateTime.Now),
                    ($"{baseUrl}/sitemap-posts.xml", DateTime.Now),
                    ($"{baseUrl}/sitemap-jobposts.xml", DateTime.Now),
                    ($"{baseUrl}/sitemap-categories.xml", DateTime.Now)
                    // Tags sitemap excluded
                };

                var xml = GenerateSitemapIndexXml(sitemaps);
                return xml;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error generating sitemap index XML");
                throw;
            }
        }

        public async Task<List<SitemapItem>> GetStaticPagesSitemapAsync()
        {
            try
            {
                var cacheKey = $"{_cacheKeyPrefix}Static";
                if (_memoryCache.TryGetValue(cacheKey, out List<SitemapItem>? cached) && cached != null)
                {
                    return cached;
                }

                var baseUrl = await GetConfigValueAsync("SEO.BaseUrl", _baseUrl);
                var sitemapItems = new List<SitemapItem>();

                // Get static pages from UrlRecord where all foreign keys are null (static pages)
                var staticUrlRecords = await _urlRecordRepository.Query()
                    .Where(u => u.IncludeInSiteMap &&
                               u.PostId == null &&
                               u.ProductId == null &&
                               u.CategoryId == null &&
                               u.TagId == null &&
                               u.TopicId == null &&
                               u.JobPostId == null)
                    .ToListAsync();

                foreach (var urlRecord in staticUrlRecords)
                {
                    var item = new SitemapItem
                    {
                        Url = string.IsNullOrEmpty(urlRecord.Slug) ? "/" : urlRecord.Slug,
                        LastModified = (urlRecord.UpdatedTime ?? urlRecord.CreatedTime ?? DateTimeOffset.Now).DateTime,
                        ChangeFrequency = ConvertFrequencyToString(urlRecord.Frequency),
                        Priority = urlRecord.Priority
                    };

                    sitemapItems.Add(item);
                }

                // Cache for 24 hours
                _memoryCache.Set(cacheKey, sitemapItems, TimeSpan.FromHours(24));
                return sitemapItems;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error getting static pages sitemap");
                return [];
            }
        }

        public async Task<List<SitemapItem>> GetProductsSitemapAsync()
        {
            try
            {
                var cacheKey = $"{_cacheKeyPrefix}Products";
                if (_memoryCache.TryGetValue(cacheKey, out List<SitemapItem>? cached) && cached != null)
                {
                    return cached;
                }

                var baseUrl = await GetConfigValueAsync("SEO.BaseUrl", _baseUrl);
                var sitemapItems = new List<SitemapItem>();

                // Add products listing page (static page)
                sitemapItems.Add(new SitemapItem
                {
                    Url = "dich-vu",
                    LastModified = DateTime.Now.AddDays(-1),
                    ChangeFrequency = "daily",
                    Priority = 0.9
                });

                // Get all product UrlRecords
                var productUrlRecords = await _urlRecordRepository.Query()
                    .Where(u => u.IncludeInSiteMap && u.ProductId.HasValue)
                    .Include(u => u.Product)
                    .ToListAsync();

                foreach (var urlRecord in productUrlRecords)
                {
                    var item = new SitemapItem
                    {
                        Url = urlRecord.Slug,
                        LastModified = (urlRecord.UpdatedTime ?? urlRecord.CreatedTime ?? DateTimeOffset.Now).DateTime,
                        ChangeFrequency = ConvertFrequencyToString(urlRecord.Frequency),
                        Priority = urlRecord.Priority
                    };

                    // Add product image if available
                    if (!string.IsNullOrEmpty(urlRecord.Product?.Thumbnail))
                    {
                        try
                        {
                            var imageUrl = urlRecord.Product.Thumbnail;
                            if (!string.IsNullOrEmpty(imageUrl))
                            {
                                item.Images.Add(imageUrl);
                                _logger.LogInformation("✅ Added product image to sitemap: {ImageUrl} for product: {ProductName}",
                                    imageUrl, urlRecord.Product?.Name ?? "Unknown");
                            }
                            else
                            {
                                _logger.LogWarning("⚠️ ImageService returned empty URL for product thumbnail: {Thumbnail} for product: {ProductName}",
                                    urlRecord.Product.Thumbnail, urlRecord.Product?.Name ?? "Unknown");
                            }
                        }
                        catch (Exception ex)
                        {
                            _logger.LogError(ex, "❌ Error converting product thumbnail to URL: {Thumbnail} for product: {ProductName}",
                                urlRecord.Product.Thumbnail, urlRecord.Product?.Name ?? "Unknown");
                        }
                    }
                    else
                    {
                        _logger.LogInformation("ℹ️ Product has no thumbnail: {ProductName}", urlRecord.Product?.Name ?? "Unknown");
                    }

                    sitemapItems.Add(item);
                }

                // Cache for 2 hours
                _memoryCache.Set(cacheKey, sitemapItems, TimeSpan.FromHours(2));
                return sitemapItems;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error getting products sitemap");
                return [];
            }
        }

        public async Task<List<SitemapItem>> GetPostsSitemapAsync()
        {
            try
            {
                var cacheKey = $"{_cacheKeyPrefix}Posts";
                if (_memoryCache.TryGetValue(cacheKey, out List<SitemapItem>? cached) && cached != null)
                {
                    return cached;
                }

                var baseUrl = await GetConfigValueAsync("SEO.BaseUrl", _baseUrl);
                var sitemapItems = new List<SitemapItem>();

                // Add posts listing page (static page)
                sitemapItems.Add(new SitemapItem
                {
                    Url = "tin-tuc",
                    LastModified = DateTime.Now.AddDays(-1),
                    ChangeFrequency = "daily",
                    Priority = 0.9
                });

                // Get all post UrlRecords (only publicly accessible posts)
                var currentTime = DateTimeOffset.Now;
                var postUrlRecords = await _urlRecordRepository.Query()
                    .Where(u => u.IncludeInSiteMap && u.PostId.HasValue)
                    .Include(u => u.Post)
                    .Where(u => u.Post!.Status == PostStatus.Published)  // Must be published
                    .Where(u => u.Post!.Visibility == PostVisibility.Public)  // Must be public (not private/password protected)
                    .Where(u => !u.Post!.ValidFrom.HasValue || u.Post.ValidFrom <= currentTime)  // Must be effective (if ValidFrom is set)
                    .ToListAsync();

                foreach (var urlRecord in postUrlRecords)
                {
                    var item = new SitemapItem
                    {
                        Url = urlRecord.Slug,
                        LastModified = (urlRecord.UpdatedTime ?? urlRecord.CreatedTime ?? DateTimeOffset.Now).DateTime,
                        ChangeFrequency = ConvertFrequencyToString(urlRecord.Frequency),
                        Priority = urlRecord.Priority
                    };

                    // Add post image if available
                    if (!string.IsNullOrEmpty(urlRecord.Post?.Thumbnail))
                    {
                        try
                        {
                            var imageUrl = await _imageService.GetUrlBySystemPath(urlRecord.Post.Thumbnail);
                            if (!string.IsNullOrEmpty(imageUrl))
                            {
                                item.Images.Add(imageUrl);
                                _logger.LogInformation("✅ Added post image to sitemap: {ImageUrl} for post: {PostTitle}",
                                    imageUrl, urlRecord.Post?.Title ?? "Unknown");
                            }
                            else
                            {
                                _logger.LogWarning("⚠️ ImageService returned empty URL for post thumbnail: {Thumbnail} for post: {PostTitle}",
                                    urlRecord.Post.Thumbnail, urlRecord.Post?.Title ?? "Unknown");
                            }
                        }
                        catch (Exception ex)
                        {
                            _logger.LogError(ex, "❌ Error converting post thumbnail to URL: {Thumbnail} for post: {PostTitle}",
                                urlRecord.Post.Thumbnail, urlRecord.Post?.Title ?? "Unknown");
                        }
                    }
                    else
                    {
                        _logger.LogInformation("ℹ️ Post has no thumbnail: {PostTitle}", urlRecord.Post?.Title ?? "Unknown");
                    }

                    sitemapItems.Add(item);
                }

                // Cache for 1 hour
                _memoryCache.Set(cacheKey, sitemapItems, TimeSpan.FromHours(1));
                return sitemapItems;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error getting posts sitemap");
                return [];
            }
        }

        public async Task<List<SitemapItem>> GetJobPostsSitemapAsync()
        {
            try
            {
                var cacheKey = $"{_cacheKeyPrefix}JobPosts";
                if (_memoryCache.TryGetValue(cacheKey, out List<SitemapItem>? cached) && cached != null)
                {
                    return cached;
                }

                var baseUrl = await GetConfigValueAsync("SEO.BaseUrl", _baseUrl);
                var sitemapItems = new List<SitemapItem>();

                // Add job posts listing page (static page)
                sitemapItems.Add(new SitemapItem
                {
                    Url = "tuyen-dung",
                    LastModified = DateTime.Now.AddDays(-1),
                    ChangeFrequency = "daily",
                    Priority = 0.9
                });

                // Get all job post UrlRecords (only active job posts)
                var currentTime = DateTimeOffset.Now;
                var jobPostUrlRecords = await _urlRecordRepository.Query()
                    .Where(u => u.IncludeInSiteMap && u.JobPostId.HasValue)
                    .Include(u => u.JobPost)
                    .Where(u => u.JobPost!.JobStatus == JobStatus.Opening)  // Must be opening
                    .Where(u => !u.JobPost!.StartDate.HasValue || u.JobPost.StartDate <= currentTime)  // Must be started (if StartDate is set)
                    .Where(u => !u.JobPost!.EndDate.HasValue || u.JobPost.EndDate >= currentTime)  // Must not be expired (if EndDate is set)
                    .ToListAsync();

                foreach (var urlRecord in jobPostUrlRecords)
                {
                    var item = new SitemapItem
                    {
                        Url = urlRecord.Slug,
                        LastModified = (urlRecord.UpdatedTime ?? urlRecord.CreatedTime ?? DateTimeOffset.Now).DateTime,
                        ChangeFrequency = ConvertFrequencyToString(urlRecord.Frequency),
                        Priority = urlRecord.Priority
                    };

                    sitemapItems.Add(item);
                }

                // Cache for 1 hour
                _memoryCache.Set(cacheKey, sitemapItems, TimeSpan.FromHours(1));
                return sitemapItems;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error getting job posts sitemap");
                return [];
            }
        }

        public async Task<List<SitemapItem>> GetCategoriesSitemapAsync()
        {
            try
            {
                var cacheKey = $"{_cacheKeyPrefix}Categories";
                if (_memoryCache.TryGetValue(cacheKey, out List<SitemapItem>? cached) && cached != null)
                {
                    return cached;
                }

                var baseUrl = await GetConfigValueAsync("SEO.BaseUrl", _baseUrl);
                var sitemapItems = new List<SitemapItem>();

                // Get all category UrlRecords
                var categoryUrlRecords = await _urlRecordRepository.Query()
                    .Where(u => u.IncludeInSiteMap && u.CategoryId.HasValue)
                    .Include(u => u.Category)
                    .ToListAsync();

                foreach (var urlRecord in categoryUrlRecords)
                {
                    var item = new SitemapItem
                    {
                        Url = urlRecord.Slug,
                        LastModified = (urlRecord.UpdatedTime ?? urlRecord.CreatedTime ?? DateTimeOffset.Now).DateTime,
                        ChangeFrequency = ConvertFrequencyToString(urlRecord.Frequency),
                        Priority = urlRecord.Priority
                    };

                    sitemapItems.Add(item);
                }

                // Cache for 6 hours
                _memoryCache.Set(cacheKey, sitemapItems, TimeSpan.FromHours(6));
                return sitemapItems;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error getting categories sitemap");
                return [];
            }
        }

        // GetTagsSitemapAsync() method removed - tags are excluded from sitemap

        public List<string> ValidateSitemap(string sitemapXml)
        {
            var errors = new List<string>();

            try
            {
                var doc = XDocument.Parse(sitemapXml);
                var ns = XNamespace.Get("http://www.sitemaps.org/schemas/sitemap/0.9");

                // Check root element
                if (doc.Root?.Name != ns + "urlset")
                {
                    errors.Add("Root element must be 'urlset' with correct namespace");
                }

                // Check each URL
                var urls = doc.Descendants(ns + "url").ToList();
                if (urls.Count > 50000)
                {
                    errors.Add($"Sitemap contains {urls.Count} URLs, maximum is 50,000");
                }

                foreach (var url in urls)
                {
                    var loc = url.Element(ns + "loc")?.Value;
                    if (string.IsNullOrEmpty(loc))
                    {
                        errors.Add("URL element missing required 'loc' element");
                        continue;
                    }

                    if (!Uri.IsWellFormedUriString(loc, UriKind.Absolute))
                    {
                        errors.Add($"Invalid URL format: {loc}");
                    }

                    // Validate priority
                    var priorityElement = url.Element(ns + "priority");
                    if (priorityElement != null)
                    {
                        if (!double.TryParse(priorityElement.Value, out double priority) || priority < 0 || priority > 1)
                        {
                            errors.Add($"Invalid priority value: {priorityElement.Value}");
                        }
                    }

                    // Validate changefreq
                    var changefreqElement = url.Element(ns + "changefreq");
                    if (changefreqElement != null)
                    {
                        var validFreqs = new[] { "always", "hourly", "daily", "weekly", "monthly", "yearly", "never" };
                        if (!validFreqs.Contains(changefreqElement.Value))
                        {
                            errors.Add($"Invalid changefreq value: {changefreqElement.Value}");
                        }
                    }
                }

                // Check file size (should be under 50MB uncompressed)
                var sizeInBytes = Encoding.UTF8.GetBytes(sitemapXml).Length;
                if (sizeInBytes > 50 * 1024 * 1024)
                {
                    errors.Add($"Sitemap size {sizeInBytes / (1024 * 1024)}MB exceeds 50MB limit");
                }

                return errors;
            }
            catch (XmlException ex)
            {
                errors.Add($"XML parsing error: {ex.Message}");
                return errors;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error validating sitemap");
                errors.Add($"Validation error: {ex.Message}");
                return errors;
            }
        }

        public async Task<SitemapStats> GetSitemapStatsAsync()
        {
            try
            {
                var stats = new SitemapStats
                {
                    GeneratedAt = DateTime.Now
                };

                var staticPages = await GetStaticPagesSitemapAsync();
                var products = await GetProductsSitemapAsync();
                var posts = await GetPostsSitemapAsync();
                var categories = await GetCategoriesSitemapAsync();

                stats.StaticPages = staticPages.Count;
                stats.Products = products.Count;
                stats.Posts = posts.Count;
                stats.Categories = categories.Count;
                stats.Tags = 0; // Tags excluded from sitemap
                stats.TotalUrls = stats.StaticPages + stats.Products + stats.Posts + stats.Categories;

                // Count images
                var allItems = new List<SitemapItem>();
                allItems.AddRange(staticPages);
                allItems.AddRange(products);
                allItems.AddRange(posts);
                allItems.AddRange(categories);
                // Tags excluded from sitemap

                stats.Images = allItems.Sum(item => item.Images.Count);

                // Priority distribution
                foreach (var item in allItems)
                {
                    var priorityKey = item.Priority.ToString("F1");
                    if (stats.PriorityDistribution.ContainsKey(priorityKey))
                        stats.PriorityDistribution[priorityKey]++;
                    else
                        stats.PriorityDistribution[priorityKey] = 1;
                }

                // Change frequency distribution
                foreach (var item in allItems)
                {
                    if (stats.ChangeFrequencyDistribution.ContainsKey(item.ChangeFrequency))
                        stats.ChangeFrequencyDistribution[item.ChangeFrequency]++;
                    else
                        stats.ChangeFrequencyDistribution[item.ChangeFrequency] = 1;
                }

                // Calculate size
                var xml = await GenerateSitemapXmlAsync();
                stats.SizeInBytes = Encoding.UTF8.GetBytes(xml).Length;

                return stats;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error getting sitemap stats");
                throw;
            }
        }

        public async Task<string> SaveSitemapToFileAsync(string sitemapXml, string fileName = "sitemap.xml")
        {
            try
            {
                var webRootPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
                if (!Directory.Exists(webRootPath))
                {
                    Directory.CreateDirectory(webRootPath);
                }

                var filePath = Path.Combine(webRootPath, fileName);
                await File.WriteAllTextAsync(filePath, sitemapXml, Encoding.UTF8);

                _logger.LogInformation("Saved sitemap to {FilePath}", filePath);
                return filePath;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error saving sitemap to file: {FileName}", fileName);
                throw;
            }
        }

        #region Private Helper Methods

        private string ConvertFrequencyToString(SitemapFrequency frequency)
        {
            return frequency switch
            {
                SitemapFrequency.Never => "never",
                SitemapFrequency.Yearly => "yearly",
                SitemapFrequency.Monthly => "monthly",
                SitemapFrequency.Weekly => "weekly",
                SitemapFrequency.Daily => "daily",
                SitemapFrequency.Hourly => "hourly",
                SitemapFrequency.Always => "always",
                _ => "monthly"
            };
        }

        private async Task<string> GetConfigValueAsync(string key, string defaultValue)
        {
            try
            {
                var value = await _appConfigService.GetConfigValueAsync(key, defaultValue);
                return string.IsNullOrEmpty(value) ? defaultValue : value;
            }
            catch
            {
                return defaultValue;
            }
        }

        private string GenerateSitemapXml(List<SitemapItem> items, string baseUrl)
        {
            var ns = XNamespace.Get("http://www.sitemaps.org/schemas/sitemap/0.9");
            var imageNs = XNamespace.Get("http://www.google.com/schemas/sitemap-image/1.1");

            var urlset = new XElement(ns + "urlset",
                new XAttribute("xmlns", ns.NamespaceName),
                new XAttribute(XNamespace.Xmlns + "image", imageNs.NamespaceName),
                new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"),
                new XAttribute(XNamespace.Xmlns + "xhtml", "http://www.w3.org/1999/xhtml"),
                new XAttribute(XNamespace.Get("http://www.w3.org/2001/XMLSchema-instance") + "schemaLocation",
                    "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"));

            foreach (var item in items)
            {
                // Skip invalid URLs
                if (string.IsNullOrWhiteSpace(item.Url))
                    continue;

                // Build full URL from baseUrl and slug
                var fullUrl = item.Url == "/" ? baseUrl : $"{baseUrl}/{item.Url}";

                // Validate URL format
                if (!Uri.TryCreate(fullUrl, UriKind.Absolute, out _))
                    continue;

                var url = new XElement(ns + "url",
                    new XElement(ns + "loc", fullUrl),
                    new XElement(ns + "changefreq", item.ChangeFrequency),
                    new XElement(ns + "priority", item.Priority.ToString("F1"))
                );

                if (item.LastModified.HasValue)
                {
                    url.Add(new XElement(ns + "lastmod", item.LastModified.Value.ToString("yyyy-MM-dd")));
                }

                // Add images
                foreach (var image in item.Images)
                {
                    // Validate image URL
                    if (string.IsNullOrWhiteSpace(image) || !Uri.TryCreate(image, UriKind.Absolute, out _))
                        continue;

                    url.Add(new XElement(imageNs + "image",
                        new XElement(imageNs + "loc", image)
                    ));
                }

                urlset.Add(url);
            }

            var doc = new XDocument(
                new XDeclaration("1.0", "UTF-8", null),
                urlset
            );

            return doc.ToString();
        }

        private string GenerateSitemapIndexXml(List<(string Url, DateTime LastModified)> sitemaps)
        {
            var ns = XNamespace.Get("http://www.sitemaps.org/schemas/sitemap/0.9");

            var sitemapindex = new XElement(ns + "sitemapindex",
                new XAttribute("xmlns", ns.NamespaceName));

            foreach (var (url, lastModified) in sitemaps)
            {
                var sitemap = new XElement(ns + "sitemap",
                    new XElement(ns + "loc", url),
                    new XElement(ns + "lastmod", lastModified.ToString("yyyy-MM-dd"))
                );

                sitemapindex.Add(sitemap);
            }

            var doc = new XDocument(
                new XDeclaration("1.0", "UTF-8", null),
                sitemapindex
            );

            return doc.ToString();
        }

        #endregion
    }
}