using System.Text;
using System.Xml;
using System.Xml.Linq;
using Microsoft.Extensions.Options;

namespace FrontOffice.Main.Utilities.Seo;

public class SitemapGenerator
{
    private readonly BlogPostService _blogPostService;
    private readonly SeoSettings _settings;

    private static readonly string[] StaticPaths =
    [
        RouteConstants.Main.MainPage,
        RouteConstants.About.Index,
        RouteConstants.FAQ.Index,
        RouteConstants.Contact.Index,
        RouteConstants.Licenses.Index,
        RouteConstants.Blog.Index,
        RouteConstants.Store.Products,
        RouteConstants.Package.List,
        RouteConstants.Club.Membership,
        RouteConstants.Club.Features,
        RouteConstants.DiscountStore.Products,
        RouteConstants.Registration.Wizard,
    ];

    public SitemapGenerator(BlogPostService blogPostService, IOptions<SeoSettings> settings)
    {
        _blogPostService = blogPostService;
        _settings = settings.Value;
    }

    public async Task<string> GenerateAsync(CancellationToken cancellationToken = default)
    {
        var baseUrl = _settings.SiteUrl.TrimEnd('/');
        var ns = XNamespace.Get("http://www.sitemaps.org/schemas/sitemap/0.9");
        var urls = new List<XElement>();

        foreach (var path in StaticPaths)
        {
            urls.Add(CreateUrlElement(ns, $"{baseUrl}{path}", priority: path == "/" ? "1.0" : "0.8"));
        }

        var page = 1;
        const int pageSize = 100;

        while (true)
        {
            var result = await _blogPostService.GetPublishedPostsAsync(page: page, pageSize: pageSize);
            if (result.Posts.Count == 0)
                break;

            foreach (var post in result.Posts)
            {
                if (string.IsNullOrWhiteSpace(post.Slug))
                    continue;

                var lastMod = post.PublishedAt?.ToString("yyyy-MM-dd") ?? DateTime.UtcNow.ToString("yyyy-MM-dd");
                urls.Add(CreateUrlElement(
                    ns,
                    $"{baseUrl}{RouteConstants.Blog.Post}{post.Slug}",
                    lastMod: lastMod,
                    priority: "0.6"));
            }

            if (page >= result.TotalPages)
                break;

            page++;
        }

        var document = new XDocument(
            new XDeclaration("1.0", "utf-8", null),
            new XElement(ns + "urlset", urls));

        var settings = new XmlWriterSettings
        {
            Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false),
            Indent = true,
            OmitXmlDeclaration = false
        };

        using var stream = new MemoryStream();
        using (var writer = XmlWriter.Create(stream, settings))
        {
            document.Save(writer);
        }

        return Encoding.UTF8.GetString(stream.ToArray());
    }

    public async Task<byte[]> GenerateBytesAsync(CancellationToken cancellationToken = default)
    {
        var xml = await GenerateAsync(cancellationToken);
        return Encoding.UTF8.GetBytes(xml);
    }

    private static XElement CreateUrlElement(
        XNamespace ns,
        string loc,
        string? lastMod = null,
        string changefreq = "weekly",
        string priority = "0.5")
    {
        var element = new XElement(ns + "url",
            new XElement(ns + "loc", loc),
            new XElement(ns + "changefreq", changefreq),
            new XElement(ns + "priority", priority));

        if (!string.IsNullOrWhiteSpace(lastMod))
            element.Add(new XElement(ns + "lastmod", lastMod));

        return element;
    }
}
