using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
using StellaOps.Cryptography;

namespace StellaOps.Authority.Signing;

internal sealed class AuthorityJwksService
{
    private readonly ICryptoProviderRegistry registry;
    private readonly ILogger<AuthorityJwksService> logger;

    public AuthorityJwksService(ICryptoProviderRegistry registry, ILogger<AuthorityJwksService> logger)
    {
        this.registry = registry ?? throw new ArgumentNullException(nameof(registry));
        this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public AuthorityJwksResponse Build() => new(BuildKeys());

    private IReadOnlyCollection<JwksKeyEntry> BuildKeys()
    {
        var keys = new List<JwksKeyEntry>();
        var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

        foreach (var provider in registry.Providers)
        {
            foreach (var signingKey in provider.GetSigningKeys())
            {
                var keyId = signingKey.Reference.KeyId;
                if (!seen.Add(keyId))
                {
                    continue;
                }

                try
                {
                    var signer = provider.GetSigner(signingKey.AlgorithmId, signingKey.Reference);
                    var jwk = signer.ExportPublicJsonWebKey();
                    var entry = new JwksKeyEntry
                    {
                        Kid = jwk.Kid,
                        Kty = jwk.Kty,
                        Use = string.IsNullOrWhiteSpace(jwk.Use) ? "sig" : jwk.Use,
                        Alg = jwk.Alg,
                        Crv = jwk.Crv,
                        X = jwk.X,
                        Y = jwk.Y,
                        Status = signingKey.Metadata.TryGetValue("status", out var status) ? status : "active"
                    };
                    keys.Add(entry);
                }
                catch (Exception ex)
                {
                    logger.LogWarning(ex, "Failed to export JWKS entry for key {KeyId}.", keyId);
                }
            }
        }

        return keys;
    }
}

internal sealed record AuthorityJwksResponse([property: JsonPropertyName("keys")] IReadOnlyCollection<JwksKeyEntry> Keys);

internal sealed class JwksKeyEntry
{
    [JsonPropertyName("kty")]
    public string? Kty { get; set; }

    [JsonPropertyName("use")]
    public string? Use { get; set; }

    [JsonPropertyName("kid")]
    public string? Kid { get; set; }

    [JsonPropertyName("alg")]
    public string? Alg { get; set; }

    [JsonPropertyName("crv")]
    public string? Crv { get; set; }

    [JsonPropertyName("x")]
    public string? X { get; set; }

    [JsonPropertyName("y")]
    public string? Y { get; set; }

    [JsonPropertyName("status")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? Status { get; set; }
}
