using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Protocols;
using Microsoft.Extensions.Options;

namespace Digdir.Domain.Dialogporten.GraphQL.Common.Authentication;

public interface ITokenIssuerCache
{
    public Task<string?> GetIssuerForScheme(string schemeName);
}

public sealed class TokenIssuerCache : ITokenIssuerCache, IDisposable
{
    private readonly Dictionary<string, string> _issuerMappings = new();
    private readonly SemaphoreSlim _initializationSemaphore = new(1, 1);
    private bool _initialized;
    private readonly IReadOnlyCollection<JwtBearerTokenSchemasOptions> _jwtTokenSchemas;

    public TokenIssuerCache(IOptions<GraphQlSettings> apiSettings)
    {
        _jwtTokenSchemas = apiSettings
            .Value
            .Authentication
            .JwtBearerTokenSchemas
            ?? throw new ArgumentException("JwtBearerTokenSchemas is required.");
    }

    public async Task<string?> GetIssuerForScheme(string schemeName)
    {
        await EnsureInitializedAsync();

        return _issuerMappings.TryGetValue(schemeName, out var issuer)
            ? issuer : null;
    }

    private async Task EnsureInitializedAsync()
    {
        if (_initialized) return;
        await _initializationSemaphore.WaitAsync();
        if (_initialized) return;

        try
        {
            foreach (var schema in _jwtTokenSchemas)
            {
                var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
                    schema.WellKnown, new OpenIdConnectConfigurationRetriever());
                var config = await configManager.GetConfigurationAsync();
                _issuerMappings[schema.Name] = config.Issuer;
            }

            _initialized = true;
        }
        finally
        {
            _initializationSemaphore.Release();
        }
    }

    public void Dispose()
    {
        _initializationSemaphore.Dispose();
    }
}