using System.Net;
using System.Net.Http;

namespace StellaOps.Feedser.Source.Acsc.Configuration;

/// <summary>
/// Connector options governing ACSC feed access and retry behaviour.
/// </summary>
public sealed class AcscOptions
{
    public const string HttpClientName = "acsc";

    private static readonly TimeSpan DefaultRequestTimeout = TimeSpan.FromSeconds(45);
    private static readonly TimeSpan DefaultFailureBackoff = TimeSpan.FromMinutes(5);
    private static readonly TimeSpan DefaultInitialBackfill = TimeSpan.FromDays(120);

    public AcscOptions()
    {
        Feeds = new List<AcscFeedOptions>
        {
            new() { Slug = "alerts", RelativePath = "/acsc/view-all-content/alerts/rss" },
            new() { Slug = "advisories", RelativePath = "/acsc/view-all-content/advisories/rss" },
            new() { Slug = "news", RelativePath = "/acsc/view-all-content/news/rss", Enabled = false },
            new() { Slug = "publications", RelativePath = "/acsc/view-all-content/publications/rss", Enabled = false },
            new() { Slug = "threats", RelativePath = "/acsc/view-all-content/threats/rss", Enabled = false },
        };
    }

    /// <summary>
    /// Base endpoint for direct ACSC fetches.
    /// </summary>
    public Uri BaseEndpoint { get; set; } = new("https://www.cyber.gov.au/", UriKind.Absolute);

    /// <summary>
    /// Optional relay endpoint used when Akamai terminates direct HTTP/2 connections.
    /// </summary>
    public Uri? RelayEndpoint { get; set; }

    /// <summary>
    /// Default mode when no preference has been captured in connector state. When <c>true</c>, the relay will be preferred for initial fetches.
    /// </summary>
    public bool PreferRelayByDefault { get; set; }

    /// <summary>
    /// If enabled, the connector may switch to the relay endpoint when direct fetches fail.
    /// </summary>
    public bool EnableRelayFallback { get; set; } = true;

    /// <summary>
    /// If set, the connector will always use the relay endpoint and skip direct attempts.
    /// </summary>
    public bool ForceRelay { get; set; }

    /// <summary>
    /// Timeout applied to fetch requests (overrides HttpClient default).
    /// </summary>
    public TimeSpan RequestTimeout { get; set; } = DefaultRequestTimeout;

    /// <summary>
    /// Backoff applied when marking fetch failures.
    /// </summary>
    public TimeSpan FailureBackoff { get; set; } = DefaultFailureBackoff;

    /// <summary>
    /// Look-back period used when deriving initial published cursors.
    /// </summary>
    public TimeSpan InitialBackfill { get; set; } = DefaultInitialBackfill;

    /// <summary>
    /// User-agent header sent with outbound requests.
    /// </summary>
    public string UserAgent { get; set; } = "StellaOps/Feedser (+https://stella-ops.org)";

    /// <summary>
    /// RSS feeds requested during fetch.
    /// </summary>
    public IList<AcscFeedOptions> Feeds { get; }

    /// <summary>
    /// HTTP version policy requested for outbound requests.
    /// </summary>
    public HttpVersionPolicy VersionPolicy { get; set; } = HttpVersionPolicy.RequestVersionOrLower;

    /// <summary>
    /// Default HTTP version requested when connecting to ACSC (defaults to HTTP/2 but allows downgrade).
    /// </summary>
    public Version RequestVersion { get; set; } = HttpVersion.Version20;

    public void Validate()
    {
        if (BaseEndpoint is null || !BaseEndpoint.IsAbsoluteUri)
        {
            throw new InvalidOperationException("ACSC BaseEndpoint must be an absolute URI.");
        }

        if (!BaseEndpoint.AbsoluteUri.EndsWith("/", StringComparison.Ordinal))
        {
            throw new InvalidOperationException("ACSC BaseEndpoint must include a trailing slash.");
        }

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

        if (RelayEndpoint is not null && !RelayEndpoint.AbsoluteUri.EndsWith("/", StringComparison.Ordinal))
        {
            throw new InvalidOperationException("ACSC RelayEndpoint must include a trailing slash when specified.");
        }

        if (RequestTimeout <= TimeSpan.Zero)
        {
            throw new InvalidOperationException("ACSC RequestTimeout must be positive.");
        }

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

        if (InitialBackfill <= TimeSpan.Zero)
        {
            throw new InvalidOperationException("ACSC InitialBackfill must be positive.");
        }

        if (string.IsNullOrWhiteSpace(UserAgent))
        {
            throw new InvalidOperationException("ACSC UserAgent cannot be empty.");
        }

        if (Feeds.Count == 0)
        {
            throw new InvalidOperationException("At least one ACSC feed must be configured.");
        }

        var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        for (var i = 0; i < Feeds.Count; i++)
        {
            var feed = Feeds[i];
            feed.Validate(i);

            if (!feed.Enabled)
            {
                continue;
            }

            if (!seen.Add(feed.Slug))
            {
                throw new InvalidOperationException($"Duplicate ACSC feed slug '{feed.Slug}' detected. Slugs must be unique (case-insensitive).");
            }
        }
    }
}
