using System.Collections.Immutable;
using System.Linq;

namespace StellaOps.Scanner.Core.Security;

public sealed class DpopValidationOptions
{
    private readonly HashSet<string> allowedAlgorithms = new(StringComparer.Ordinal);

    public DpopValidationOptions()
    {
        allowedAlgorithms.Add("ES256");
        allowedAlgorithms.Add("ES384");
    }

    public TimeSpan ProofLifetime { get; set; } = TimeSpan.FromMinutes(2);

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

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

    public ISet<string> AllowedAlgorithms => allowedAlgorithms;

    public IReadOnlySet<string> NormalizedAlgorithms { get; private set; } = ImmutableHashSet<string>.Empty;

    public void Validate()
    {
        if (ProofLifetime <= TimeSpan.Zero)
        {
            throw new InvalidOperationException("DPoP proof lifetime must be greater than zero.");
        }

        if (AllowedClockSkew < TimeSpan.Zero || AllowedClockSkew > TimeSpan.FromMinutes(5))
        {
            throw new InvalidOperationException("DPoP allowed clock skew must be between 0 seconds and 5 minutes.");
        }

        if (ReplayWindow < TimeSpan.Zero)
        {
            throw new InvalidOperationException("DPoP replay window must be greater than or equal to zero.");
        }

        if (allowedAlgorithms.Count == 0)
        {
            throw new InvalidOperationException("At least one allowed DPoP algorithm must be configured.");
        }

        NormalizedAlgorithms = allowedAlgorithms
            .Select(static algorithm => algorithm.Trim().ToUpperInvariant())
            .Where(static algorithm => algorithm.Length > 0)
            .ToImmutableHashSet(StringComparer.Ordinal);

        if (NormalizedAlgorithms.Count == 0)
        {
            throw new InvalidOperationException("Allowed DPoP algorithms cannot be empty after normalization.");
        }
    }
}
