using System.Collections.ObjectModel;
using System.Linq;

namespace StellaOps.Scanner.Core.Security;

public readonly record struct ScannerOperationalToken(
    string AccessToken,
    string TokenType,
    DateTimeOffset ExpiresAt,
    IReadOnlyList<string> Scopes)
{
    public bool IsExpired(TimeProvider timeProvider, TimeSpan refreshSkew)
    {
        ArgumentNullException.ThrowIfNull(timeProvider);

        var now = timeProvider.GetUtcNow();
        return now >= ExpiresAt - refreshSkew;
    }

    public static ScannerOperationalToken FromResult(
        string accessToken,
        string tokenType,
        DateTimeOffset expiresAt,
        IEnumerable<string> scopes)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(accessToken);
        ArgumentException.ThrowIfNullOrWhiteSpace(tokenType);

        IReadOnlyList<string> normalized = scopes switch
        {
            null => Array.Empty<string>(),
            IReadOnlyList<string> readOnly => readOnly.Count == 0 ? Array.Empty<string>() : readOnly,
            ICollection<string> collection => NormalizeCollection(collection),
            _ => NormalizeEnumerable(scopes)
        };

        return new ScannerOperationalToken(
            accessToken,
            tokenType,
            expiresAt,
            normalized);
    }

    private static IReadOnlyList<string> NormalizeCollection(ICollection<string> collection)
    {
        if (collection.Count == 0)
        {
            return Array.Empty<string>();
        }

        if (collection is IReadOnlyList<string> readOnly)
        {
            return readOnly;
        }

        var buffer = new string[collection.Count];
        collection.CopyTo(buffer, 0);
        return new ReadOnlyCollection<string>(buffer);
    }

    private static IReadOnlyList<string> NormalizeEnumerable(IEnumerable<string> scopes)
    {
        var buffer = scopes.ToArray();
        return buffer.Length == 0 ? Array.Empty<string>() : new ReadOnlyCollection<string>(buffer);
    }
}
