using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http;
using StellaOps.Auth.Client;
using Xunit;

namespace StellaOps.Auth.Client.Tests;

public class ServiceCollectionExtensionsTests
{
    [Fact]
    public async Task AddStellaOpsAuthClient_ConfiguresRetryPolicy()
    {
        var services = new ServiceCollection();
        services.AddLogging();

        services.AddStellaOpsAuthClient(options =>
        {
            options.Authority = "https://authority.test";
            options.RetryDelays.Clear();
            options.RetryDelays.Add(TimeSpan.FromMilliseconds(1));
            options.DiscoveryCacheLifetime = TimeSpan.FromMinutes(1);
            options.JwksCacheLifetime = TimeSpan.FromMinutes(1);
            options.AllowOfflineCacheFallback = false;
        });

        var recordedHandlers = new List<DelegatingHandler>();
        var attemptCount = 0;

        services.AddHttpClient<StellaOpsDiscoveryCache>()
            .ConfigureHttpMessageHandlerBuilder(builder =>
            {
                recordedHandlers = new List<DelegatingHandler>(builder.AdditionalHandlers);

                var responses = new Queue<Func<HttpResponseMessage>>(new[]
                {
                    () => CreateResponse(HttpStatusCode.InternalServerError, "{}"),
                    () => CreateResponse(HttpStatusCode.OK, "{\"token_endpoint\":\"https://authority.test/connect/token\",\"jwks_uri\":\"https://authority.test/jwks\"}")
                });

                builder.PrimaryHandler = new LambdaHttpMessageHandler((_, _) =>
                {
                    attemptCount++;

                    if (responses.Count == 0)
                    {
                        return Task.FromResult(CreateResponse(HttpStatusCode.OK, "{}"));
                    }

                    var factory = responses.Dequeue();
                    return Task.FromResult(factory());
                });
            });

        using var provider = services.BuildServiceProvider();

        var cache = provider.GetRequiredService<StellaOpsDiscoveryCache>();
        var configuration = await cache.GetAsync(CancellationToken.None);

        Assert.Equal(new Uri("https://authority.test/connect/token"), configuration.TokenEndpoint);
        Assert.Equal(2, attemptCount);
        Assert.NotEmpty(recordedHandlers);
        Assert.Contains(recordedHandlers, handler => handler.GetType().Name.Contains("PolicyHttpMessageHandler", StringComparison.Ordinal));
    }

    private static HttpResponseMessage CreateResponse(HttpStatusCode statusCode, string jsonContent)
    {
        return new HttpResponseMessage(statusCode)
        {
            Content = new StringContent(jsonContent)
            {
                Headers = { ContentType = new MediaTypeHeaderValue("application/json") }
            }
        };
    }
    private sealed class LambdaHttpMessageHandler : HttpMessageHandler
    {
        private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> responder;

        public LambdaHttpMessageHandler(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> responder)
        {
            this.responder = responder;
        }

        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            => responder(request, cancellationToken);
    }
}
