using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Feedser.Source.CertFr.Configuration;

namespace StellaOps.Feedser.Source.CertFr.Internal;

public sealed class CertFrFeedClient
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly CertFrOptions _options;
    private readonly ILogger<CertFrFeedClient> _logger;

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

    public async Task<IReadOnlyList<CertFrFeedItem>> LoadAsync(DateTimeOffset windowStart, DateTimeOffset windowEnd, CancellationToken cancellationToken)
    {
        var client = _httpClientFactory.CreateClient(CertFrOptions.HttpClientName);

        using var response = await client.GetAsync(_options.FeedUri, cancellationToken).ConfigureAwait(false);
        response.EnsureSuccessStatusCode();

        await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
        var document = XDocument.Load(stream);

        var items = new List<CertFrFeedItem>();
        var now = DateTimeOffset.UtcNow;

        foreach (var itemElement in document.Descendants("item"))
        {
            var link = itemElement.Element("link")?.Value;
            if (string.IsNullOrWhiteSpace(link) || !Uri.TryCreate(link.Trim(), UriKind.Absolute, out var detailUri))
            {
                continue;
            }

            var title = itemElement.Element("title")?.Value?.Trim();
            var summary = itemElement.Element("description")?.Value?.Trim();

            var published = ParsePublished(itemElement.Element("pubDate")?.Value) ?? now;
            if (published < windowStart)
            {
                continue;
            }

            if (published > windowEnd)
            {
                published = windowEnd;
            }

            var advisoryId = ResolveAdvisoryId(itemElement, detailUri);
            items.Add(new CertFrFeedItem(advisoryId, detailUri, published.ToUniversalTime(), title, summary));
        }

        return items
            .OrderBy(item => item.Published)
            .Take(_options.MaxItemsPerFetch)
            .ToArray();
    }

    private static DateTimeOffset? ParsePublished(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return null;
        }

        if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var parsed))
        {
            return parsed;
        }

        return null;
    }

    private static string ResolveAdvisoryId(XElement itemElement, Uri detailUri)
    {
        var guid = itemElement.Element("guid")?.Value;
        if (!string.IsNullOrWhiteSpace(guid))
        {
            return guid.Trim();
        }

        var segments = detailUri.Segments;
        if (segments.Length > 0)
        {
            var slug = segments[^1].Trim('/');
            if (!string.IsNullOrWhiteSpace(slug))
            {
                return slug;
            }
        }

        return detailUri.AbsoluteUri;
    }
}
