using System.Globalization;

namespace StellaOps.Feedser.Source.Vndr.Cisco.Configuration;

public sealed class CiscoOptions
{
    public const string HttpClientName = "feedser.source.vndr.cisco";
    public const string AuthHttpClientName = "feedser.source.vndr.cisco.auth";

    public Uri BaseUri { get; set; } = new("https://api.cisco.com/security/advisories/v2/", UriKind.Absolute);

    public Uri TokenEndpoint { get; set; } = new("https://id.cisco.com/oauth2/default/v1/token", UriKind.Absolute);

    public string ClientId { get; set; } = string.Empty;

    public string ClientSecret { get; set; } = string.Empty;

    public int PageSize { get; set; } = 100;

    public int MaxPagesPerFetch { get; set; } = 5;

    public int MaxAdvisoriesPerFetch { get; set; } = 200;

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

    public TimeSpan RequestDelay { get; set; } = TimeSpan.FromMilliseconds(250);

    public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(30);

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

    public TimeSpan TokenRefreshSkew { get; set; } = TimeSpan.FromMinutes(1);

    public string LastModifiedPathTemplate { get; set; } = "advisories/lastmodified/{0}";

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

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

        if (string.IsNullOrWhiteSpace(ClientId))
        {
            throw new InvalidOperationException("Cisco clientId must be configured.");
        }

        if (string.IsNullOrWhiteSpace(ClientSecret))
        {
            throw new InvalidOperationException("Cisco clientSecret must be configured.");
        }

        if (PageSize is < 1 or > 100)
        {
            throw new InvalidOperationException("Cisco PageSize must be between 1 and 100.");
        }

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

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

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

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

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

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

        if (TokenRefreshSkew < TimeSpan.FromSeconds(5))
        {
            throw new InvalidOperationException("Cisco TokenRefreshSkew must be at least 5 seconds.");
        }

        if (string.IsNullOrWhiteSpace(LastModifiedPathTemplate))
        {
            throw new InvalidOperationException("Cisco LastModifiedPathTemplate must be configured.");
        }
    }

    public Uri BuildLastModifiedUri(DateOnly date, int pageIndex, int pageSize)
    {
        if (pageIndex < 1)
        {
            throw new ArgumentOutOfRangeException(nameof(pageIndex), pageIndex, "Page index must be >= 1.");
        }

        if (pageSize is < 1 or > 100)
        {
            throw new ArgumentOutOfRangeException(nameof(pageSize), pageSize, "Page size must be between 1 and 100.");
        }

        var path = string.Format(CultureInfo.InvariantCulture, LastModifiedPathTemplate, date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture));
        var builder = new UriBuilder(BaseUri);
        var basePath = builder.Path.TrimEnd('/');
        builder.Path = $"{basePath}/{path}".Replace("//", "/", StringComparison.Ordinal);
        var query = $"pageIndex={pageIndex.ToString(CultureInfo.InvariantCulture)}&pageSize={pageSize.ToString(CultureInfo.InvariantCulture)}";
        builder.Query = string.IsNullOrEmpty(builder.Query) ? query : builder.Query.TrimStart('?') + "&" + query;
        return builder.Uri;
    }
}
