using System;
using System.Net;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Polly;
using Polly.Extensions.Http;
namespace StellaOps.Auth.Client;
/// 
/// DI helpers for the StellaOps auth client.
/// 
public static class ServiceCollectionExtensions
{
    /// 
    /// Registers the StellaOps auth client with the provided configuration.
    /// 
    public static IServiceCollection AddStellaOpsAuthClient(this IServiceCollection services, Action configure)
    {
        ArgumentNullException.ThrowIfNull(services);
        ArgumentNullException.ThrowIfNull(configure);
        services.AddOptions()
            .Configure(configure)
            .PostConfigure(static options => options.Validate());
        services.TryAddSingleton();
        services.AddHttpClient((provider, client) =>
        {
            var options = provider.GetRequiredService>().CurrentValue;
            client.Timeout = options.HttpTimeout;
        }).AddPolicyHandler(static (provider, _) => CreateRetryPolicy(provider));
        services.AddHttpClient((provider, client) =>
        {
            var options = provider.GetRequiredService>().CurrentValue;
            client.Timeout = options.HttpTimeout;
        }).AddPolicyHandler(static (provider, _) => CreateRetryPolicy(provider));
        services.AddHttpClient((provider, client) =>
        {
            var options = provider.GetRequiredService>().CurrentValue;
            client.Timeout = options.HttpTimeout;
        }).AddPolicyHandler(static (provider, _) => CreateRetryPolicy(provider));
        return services;
    }
    /// 
    /// Registers a file-backed token cache implementation.
    /// 
    public static IServiceCollection AddStellaOpsFileTokenCache(this IServiceCollection services, string cacheDirectory)
    {
        ArgumentNullException.ThrowIfNull(services);
        ArgumentException.ThrowIfNullOrWhiteSpace(cacheDirectory);
        services.Replace(ServiceDescriptor.Singleton(provider =>
        {
            var logger = provider.GetService>();
            var options = provider.GetRequiredService>().CurrentValue;
            return new FileTokenCache(cacheDirectory, TimeProvider.System, options.ExpirationSkew, logger);
        }));
        return services;
    }
    private static IAsyncPolicy CreateRetryPolicy(IServiceProvider provider)
    {
        var options = provider.GetRequiredService>().CurrentValue;
        var delays = options.NormalizedRetryDelays;
        if (delays.Count == 0)
        {
            return Policy.NoOpAsync();
        }
        var logger = provider.GetService()?.CreateLogger("StellaOps.Auth.Client.HttpRetry");
        return HttpPolicyExtensions
            .HandleTransientHttpError()
            .OrResult(static response => response.StatusCode == HttpStatusCode.TooManyRequests)
            .WaitAndRetryAsync(
                delays.Count,
                attempt => delays[attempt - 1],
                (outcome, delay, attempt, _) =>
                {
                    if (logger is null)
                    {
                        return;
                    }
                    if (outcome.Exception is not null)
                    {
                        logger.LogWarning(
                            outcome.Exception,
                            "Retrying Authority HTTP call ({Attempt}/{TotalAttempts}) after exception; waiting {Delay}.",
                            attempt,
                            delays.Count,
                            delay);
                    }
                    else
                    {
                        logger.LogWarning(
                            "Retrying Authority HTTP call ({Attempt}/{TotalAttempts}) due to status {StatusCode}; waiting {Delay}.",
                            attempt,
                            delays.Count,
                            outcome.Result!.StatusCode,
                            delay);
                    }
                });
    }
}