using System.Net;

namespace StellaOps.Feedser.Source.Ru.Nkcki.Configuration;

/// <summary>
/// Connector options for the Russian NKTsKI bulletin ingestion pipeline.
/// </summary>
public sealed class RuNkckiOptions
{
    public const string HttpClientName = "ru-nkcki";

    private static readonly TimeSpan DefaultRequestTimeout = TimeSpan.FromSeconds(90);
    private static readonly TimeSpan DefaultFailureBackoff = TimeSpan.FromMinutes(20);
    private static readonly TimeSpan DefaultListingCache = TimeSpan.FromMinutes(10);

    /// <summary>
    /// Base endpoint used for resolving relative resource links.
    /// </summary>
    public Uri BaseAddress { get; set; } = new("https://cert.gov.ru/", UriKind.Absolute);

    /// <summary>
    /// Relative path to the bulletin listing page.
    /// </summary>
    public string ListingPath { get; set; } = "materialy/uyazvimosti/";

    /// <summary>
    /// Timeout applied to listing and bulletin fetch requests.
    /// </summary>
    public TimeSpan RequestTimeout { get; set; } = DefaultRequestTimeout;

    /// <summary>
    /// Backoff applied when the listing or attachments cannot be retrieved.
    /// </summary>
    public TimeSpan FailureBackoff { get; set; } = DefaultFailureBackoff;

    /// <summary>
    /// Maximum number of bulletin attachments downloaded per fetch run.
    /// </summary>
    public int MaxBulletinsPerFetch { get; set; } = 5;

    /// <summary>
    /// Maximum number of listing pages visited per fetch cycle.
    /// </summary>
    public int MaxListingPagesPerFetch { get; set; } = 3;

    /// <summary>
    /// Maximum number of vulnerabilities ingested per fetch cycle across all attachments.
    /// </summary>
    public int MaxVulnerabilitiesPerFetch { get; set; } = 250;

    /// <summary>
    /// Maximum bulletin identifiers remembered to avoid refetching historical files.
    /// </summary>
    public int KnownBulletinCapacity { get; set; } = 512;

    /// <summary>
    /// Delay between sequential bulletin downloads.
    /// </summary>
    public TimeSpan RequestDelay { get; set; } = TimeSpan.FromMilliseconds(250);

    /// <summary>
    /// Duration the HTML listing can be cached before forcing a refetch.
    /// </summary>
    public TimeSpan ListingCacheDuration { get; set; } = DefaultListingCache;

    public string UserAgent { get; set; } = "StellaOps/Feedser (+https://stella-ops.org)";

    public string AcceptLanguage { get; set; } = "ru-RU,ru;q=0.9,en-US;q=0.6,en;q=0.4";

    /// <summary>
    /// Absolute URI for the listing page.
    /// </summary>
    public Uri ListingUri => new(BaseAddress, ListingPath);

    /// <summary>
    /// Optional directory for caching downloaded bulletins (relative paths resolve under the content root).
    /// </summary>
    public string? CacheDirectory { get; set; } = null;

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

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

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

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

        if (MaxBulletinsPerFetch <= 0)
        {
            throw new InvalidOperationException("RuNkcki MaxBulletinsPerFetch must be greater than zero.");
        }

        if (MaxListingPagesPerFetch <= 0)
        {
            throw new InvalidOperationException("RuNkcki MaxListingPagesPerFetch must be greater than zero.");
        }

        if (MaxVulnerabilitiesPerFetch <= 0)
        {
            throw new InvalidOperationException("RuNkcki MaxVulnerabilitiesPerFetch must be greater than zero.");
        }

        if (KnownBulletinCapacity <= 0)
        {
            throw new InvalidOperationException("RuNkcki KnownBulletinCapacity must be greater than zero.");
        }

        if (CacheDirectory is not null && CacheDirectory.Trim().Length == 0)
        {
            throw new InvalidOperationException("RuNkcki CacheDirectory cannot be whitespace.");
        }

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

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