using System.Security.Cryptography;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Tiku.Application.Security;

namespace Tiku.Infrastructure.Auth;

internal sealed class JwtKeyRing : IJwtKeyRing, IDisposable
{
    private readonly List<RSA> keys = [];

    public JwtKeyRing(IOptions<JwtOptions> options)
    {
        var value = options.Value;
        var signingRsa = RSA.Create(3072);
        keys.Add(signingRsa);
        if (!string.IsNullOrWhiteSpace(value.PrivateKeyPem))
        {
            signingRsa.ImportFromPem(value.PrivateKeyPem);
        }

        var signingKey = CreateKey(signingRsa, value.KeyId);
        SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.RsaSha256);

        var validationKeys = new List<SecurityKey> { signingKey };
        foreach (var pair in value.PublicKeys.Where(pair => pair.Key != value.KeyId))
        {
            var rsa = RSA.Create();
            rsa.ImportFromPem(pair.Value);
            keys.Add(rsa);
            validationKeys.Add(CreateKey(rsa, pair.Key));
        }

        ValidationKeys = validationKeys;
    }

    public SigningCredentials SigningCredentials { get; }
    public IReadOnlyCollection<SecurityKey> ValidationKeys { get; }

    private static RsaSecurityKey CreateKey(RSA rsa, string keyId) => new(rsa)
    {
        KeyId = keyId,
        // IdentityModel caches signature providers globally by key identity. A key ring owns
        // and disposes its RSA instances, so a provider retained by another in-process host
        // could otherwise reference an RSA instance that has already been disposed.
        CryptoProviderFactory = new CryptoProviderFactory
        {
            CacheSignatureProviders = false
        }
    };

    public void Dispose()
    {
        foreach (var key in keys)
        {
            key.Dispose();
        }
    }
}
