using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using System.Net.Http;

namespace StellaOps.Feedser.Source.Ics.Cisa.Configuration;

public sealed class IcsCisaOptions
{
    public static string HttpClientName => "source.ics.cisa";

    /// <summary>
    /// GovDelivery topics RSS endpoint. Feed URIs are constructed from this base.
    /// </summary>
    public Uri TopicsEndpoint { get; set; } = new("https://content.govdelivery.com/accounts/USDHSCISA/topics.rss", UriKind.Absolute);

    /// <summary>
    /// GovDelivery personalised subscription code (code=...).
    /// </summary>
    public string GovDeliveryCode { get; set; } = string.Empty;

    /// <summary>
    /// Topic identifiers to pull (e.g. USDHSCISA_16 for general ICS advisories).
    /// </summary>
    public IList<string> TopicIds { get; } = new List<string>
    {
        "USDHSCISA_16", // ICS advisories (ICSA)
        "USDHSCISA_19", // ICS medical advisories (ICSMA)
        "USDHSCISA_17", // ICS alerts
    };

    /// <summary>
    /// Optional delay between sequential topic fetches to appease GovDelivery throttling.
    /// </summary>
    public TimeSpan RequestDelay { get; set; } = TimeSpan.FromMilliseconds(250);

    public TimeSpan FailureBackoff { get; set; } = TimeSpan.FromMinutes(5);

    public TimeSpan DocumentExpiry { get; set; } = TimeSpan.FromDays(30);

    /// <summary>
    /// Optional proxy endpoint used when Akamai blocks direct pulls.
    /// </summary>
    public Uri? ProxyUri { get; set; }

    /// <summary>
    /// HTTP version requested when contacting GovDelivery.
    /// </summary>
    public Version RequestVersion { get; set; } = HttpVersion.Version11;

    /// <summary>
    /// Negotiation policy applied to HTTP requests.
    /// </summary>
    public HttpVersionPolicy RequestVersionPolicy { get; set; } = HttpVersionPolicy.RequestVersionOrLower;

    /// <summary>
    /// Maximum number of retry attempts for RSS fetches.
    /// </summary>
    public int MaxAttempts { get; set; } = 4;

    /// <summary>
    /// Base delay used for exponential backoff between attempts.
    /// </summary>
    public TimeSpan BaseDelay { get; set; } = TimeSpan.FromSeconds(3);

    /// <summary>
    /// Base URI used when fetching HTML detail pages.
    /// </summary>
    public Uri DetailBaseUri { get; set; } = new("https://www.cisa.gov/", UriKind.Absolute);

    /// <summary>
    /// Optional timeout override applied to detail page fetches.
    /// </summary>
    public TimeSpan DetailRequestTimeout { get; set; } = TimeSpan.FromSeconds(25);

    /// <summary>
    /// Additional hosts allowed by the connector (detail pages, attachments).
    /// </summary>
    public IList<string> AdditionalHosts { get; } = new List<string>
    {
        "www.cisa.gov",
        "cisa.gov"
    };

    public bool EnableDetailScrape { get; set; } = true;

    public bool CaptureAttachments { get; set; } = true;

    [MemberNotNull(nameof(TopicsEndpoint), nameof(GovDeliveryCode))]
    public void Validate()
    {
        if (TopicsEndpoint is null || !TopicsEndpoint.IsAbsoluteUri)
        {
            throw new InvalidOperationException("TopicsEndpoint must be an absolute URI.");
        }

        if (string.IsNullOrWhiteSpace(GovDeliveryCode))
        {
            throw new InvalidOperationException("GovDeliveryCode must be provided.");
        }

        if (TopicIds.Count == 0)
        {
            throw new InvalidOperationException("At least one GovDelivery topic identifier is required.");
        }

        foreach (var topic in TopicIds)
        {
            if (string.IsNullOrWhiteSpace(topic))
            {
                throw new InvalidOperationException("Topic identifiers cannot be blank.");
            }
        }

        if (RequestDelay < TimeSpan.Zero)
        {
            throw new InvalidOperationException("RequestDelay cannot be negative.");
        }

        if (FailureBackoff <= TimeSpan.Zero)
        {
            throw new InvalidOperationException("FailureBackoff must be greater than zero.");
        }

        if (DocumentExpiry <= TimeSpan.Zero)
        {
            throw new InvalidOperationException("DocumentExpiry must be greater than zero.");
        }

        if (MaxAttempts <= 0)
        {
            throw new InvalidOperationException("MaxAttempts must be positive.");
        }

        if (BaseDelay <= TimeSpan.Zero)
        {
            throw new InvalidOperationException("BaseDelay must be greater than zero.");
        }

        if (DetailBaseUri is null || !DetailBaseUri.IsAbsoluteUri)
        {
            throw new InvalidOperationException("DetailBaseUri must be an absolute URI.");
        }

        if (DetailRequestTimeout <= TimeSpan.Zero)
        {
            throw new InvalidOperationException("DetailRequestTimeout must be greater than zero.");
        }

        if (ProxyUri is not null && !ProxyUri.IsAbsoluteUri)
        {
            throw new InvalidOperationException("ProxyUri must be an absolute URI when specified.");
        }

        foreach (var host in AdditionalHosts)
        {
            if (string.IsNullOrWhiteSpace(host))
            {
                throw new InvalidOperationException("Additional host entries cannot be blank.");
            }
        }
    }

    public Uri BuildTopicUri(string topicId)
    {
        ArgumentException.ThrowIfNullOrEmpty(topicId);
        Validate();

        var builder = new UriBuilder(TopicsEndpoint);
        var query = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
        {
            ["code"] = GovDeliveryCode,
            ["format"] = "xml",
            ["topic_id"] = topicId.Trim(),
        };

        builder.Query = string.Join("&", query.Select(pair => $"{Uri.EscapeDataString(pair.Key)}={Uri.EscapeDataString(pair.Value)}"));
        return builder.Uri;
    }
}
