using System.Collections.ObjectModel; using System.Linq; namespace StellaOps.Zastava.Core.Security; public readonly record struct ZastavaOperationalToken( string AccessToken, string TokenType, DateTimeOffset? ExpiresAtUtc, IReadOnlyList Scopes) { public bool IsExpired(TimeProvider timeProvider, TimeSpan refreshSkew) { ArgumentNullException.ThrowIfNull(timeProvider); if (ExpiresAtUtc is null) { return false; } return timeProvider.GetUtcNow() >= ExpiresAtUtc.Value - refreshSkew; } public static ZastavaOperationalToken FromResult( string accessToken, string tokenType, DateTimeOffset? expiresAtUtc, IEnumerable scopes) { ArgumentException.ThrowIfNullOrWhiteSpace(accessToken); ArgumentException.ThrowIfNullOrWhiteSpace(tokenType); IReadOnlyList normalized = scopes switch { null => Array.Empty(), IReadOnlyList readOnly => readOnly.Count == 0 ? Array.Empty() : readOnly, ICollection collection => NormalizeCollection(collection), _ => NormalizeEnumerable(scopes) }; return new ZastavaOperationalToken( accessToken, tokenType, expiresAtUtc, normalized); } private static IReadOnlyList NormalizeCollection(ICollection collection) { if (collection.Count == 0) { return Array.Empty(); } if (collection is IReadOnlyList readOnly) { return readOnly; } var buffer = new string[collection.Count]; collection.CopyTo(buffer, 0); return new ReadOnlyCollection(buffer); } private static IReadOnlyList NormalizeEnumerable(IEnumerable scopes) { var buffer = scopes.ToArray(); return buffer.Length == 0 ? Array.Empty() : new ReadOnlyCollection(buffer); } }