using System.ServiceModel.Syndication;
using System.Xml;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Feedser.Source.Vndr.Chromium.Configuration;

namespace StellaOps.Feedser.Source.Vndr.Chromium.Internal;

public sealed class ChromiumFeedLoader
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly ChromiumOptions _options;
    private readonly ILogger<ChromiumFeedLoader> _logger;

    public ChromiumFeedLoader(IHttpClientFactory httpClientFactory, IOptions<ChromiumOptions> options, ILogger<ChromiumFeedLoader> logger)
    {
        _httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
        _options = options?.Value ?? throw new ArgumentNullException(nameof(options));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async Task<IReadOnlyList<ChromiumFeedEntry>> LoadAsync(DateTimeOffset windowStart, DateTimeOffset windowEnd, CancellationToken cancellationToken)
    {
        var client = _httpClientFactory.CreateClient(ChromiumOptions.HttpClientName);
        var results = new List<ChromiumFeedEntry>();
        var startIndex = 1;

        for (var page = 0; page < _options.MaxFeedPages; page++)
        {
            var requestUri = BuildRequestUri(startIndex);
            using var response = await client.GetAsync(requestUri, cancellationToken).ConfigureAwait(false);
            response.EnsureSuccessStatusCode();

            await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
            using var reader = XmlReader.Create(stream);
            var feed = SyndicationFeed.Load(reader);
            if (feed is null || feed.Items is null)
            {
                break;
            }

            var pageEntries = new List<ChromiumFeedEntry>();
            foreach (var entry in feed.Items)
            {
                var published = entry.PublishDate != DateTimeOffset.MinValue
                    ? entry.PublishDate.ToUniversalTime()
                    : entry.LastUpdatedTime.ToUniversalTime();

                if (published > windowEnd || published < windowStart - _options.WindowOverlap)
                {
                    continue;
                }

                var detailUri = entry.Links.FirstOrDefault(link => string.Equals(link.RelationshipType, "alternate", StringComparison.OrdinalIgnoreCase))?.Uri;
                if (detailUri is null)
                {
                    continue;
                }

                var postId = ExtractPostId(detailUri);
                if (string.IsNullOrEmpty(postId))
                {
                    continue;
                }

                var categories = entry.Categories.Select(static cat => cat.Name).Where(static name => !string.IsNullOrWhiteSpace(name)).ToArray();
                var chromiumEntry = new ChromiumFeedEntry(
                    entry.Id ?? detailUri.ToString(),
                    postId,
                    entry.Title?.Text?.Trim() ?? postId,
                    detailUri,
                    published,
                    entry.LastUpdatedTime == DateTimeOffset.MinValue ? null : entry.LastUpdatedTime.ToUniversalTime(),
                    entry.Summary?.Text?.Trim(),
                    categories);

                if (chromiumEntry.Published >= windowStart && chromiumEntry.Published <= windowEnd)
                {
                    pageEntries.Add(chromiumEntry);
                }
            }

            if (pageEntries.Count == 0)
            {
                var oldest = feed.Items?.Select(static item => item.PublishDate).Where(static dt => dt != DateTimeOffset.MinValue).OrderBy(static dt => dt).FirstOrDefault();
                if (oldest.HasValue && oldest.Value.ToUniversalTime() < windowStart)
                {
                    break;
                }
            }

            results.AddRange(pageEntries);

            if (feed.Items?.Any() != true)
            {
                break;
            }

            var nextLink = feed.Links?.FirstOrDefault(link => string.Equals(link.RelationshipType, "next", StringComparison.OrdinalIgnoreCase))?.Uri;
            if (nextLink is null)
            {
                break;
            }

            startIndex += _options.MaxEntriesPerPage;
        }

        return results
            .DistinctBy(static entry => entry.DetailUri)
            .OrderBy(static entry => entry.Published)
            .ToArray();
    }

    private Uri BuildRequestUri(int startIndex)
    {
        var builder = new UriBuilder(_options.FeedUri);
        var query = new List<string>();

        if (!string.IsNullOrEmpty(builder.Query))
        {
            query.Add(builder.Query.TrimStart('?'));
        }

        query.Add($"max-results={_options.MaxEntriesPerPage}");
        query.Add($"start-index={startIndex}");
        query.Add("redirect=false");
        builder.Query = string.Join('&', query);
        return builder.Uri;
    }

    private static string ExtractPostId(Uri detailUri)
    {
        var segments = detailUri.Segments;
        if (segments.Length == 0)
        {
            return detailUri.AbsoluteUri;
        }

        var last = segments[^1].Trim('/');
        if (last.EndsWith(".html", StringComparison.OrdinalIgnoreCase))
        {
            last = last[..^5];
        }

        return last.Replace('/', '-');
    }
}
