using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;

namespace StellaOps.Auth.Client;

/// <summary>
/// DI helpers for the StellaOps auth client.
/// </summary>
public static class ServiceCollectionExtensions
{
    /// <summary>
    /// Registers the StellaOps auth client with the provided configuration.
    /// </summary>
    public static IServiceCollection AddStellaOpsAuthClient(this IServiceCollection services, Action<StellaOpsAuthClientOptions> configure)
    {
        ArgumentNullException.ThrowIfNull(services);
        ArgumentNullException.ThrowIfNull(configure);

        services.AddOptions<StellaOpsAuthClientOptions>()
            .Configure(configure)
            .PostConfigure(static options => options.Validate());

        services.TryAddSingleton<IStellaOpsTokenCache, InMemoryTokenCache>();

        services.AddHttpClient<StellaOpsDiscoveryCache>((provider, client) =>
        {
            var options = provider.GetRequiredService<IOptionsMonitor<StellaOpsAuthClientOptions>>().CurrentValue;
            client.Timeout = options.HttpTimeout;
        });

        services.AddHttpClient<StellaOpsJwksCache>((provider, client) =>
        {
            var options = provider.GetRequiredService<IOptionsMonitor<StellaOpsAuthClientOptions>>().CurrentValue;
            client.Timeout = options.HttpTimeout;
        });

        services.AddHttpClient<IStellaOpsTokenClient, StellaOpsTokenClient>((provider, client) =>
        {
            var options = provider.GetRequiredService<IOptionsMonitor<StellaOpsAuthClientOptions>>().CurrentValue;
            client.Timeout = options.HttpTimeout;
        });

        return services;
    }

    /// <summary>
    /// Registers a file-backed token cache implementation.
    /// </summary>
    public static IServiceCollection AddStellaOpsFileTokenCache(this IServiceCollection services, string cacheDirectory)
    {
        ArgumentNullException.ThrowIfNull(services);
        ArgumentException.ThrowIfNullOrWhiteSpace(cacheDirectory);

        services.Replace(ServiceDescriptor.Singleton<IStellaOpsTokenCache>(provider =>
        {
            var logger = provider.GetService<Microsoft.Extensions.Logging.ILogger<FileTokenCache>>();
            var options = provider.GetRequiredService<IOptionsMonitor<StellaOpsAuthClientOptions>>().CurrentValue;
            return new FileTokenCache(cacheDirectory, TimeProvider.System, options.ExpirationSkew, logger);
        }));

        return services;
    }
}
