using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using FluentAssertions;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Excititor.Connectors.SUSE.RancherVEXHub.Authentication;
using StellaOps.Excititor.Connectors.SUSE.RancherVEXHub.Configuration;
using Xunit;

namespace StellaOps.Excititor.Connectors.SUSE.RancherVEXHub.Tests.Authentication;

public sealed class RancherHubTokenProviderTests
{
    private const string TokenResponse = "{\"access_token\":\"abc123\",\"token_type\":\"Bearer\",\"expires_in\":3600}";

    [Fact]
    public async Task GetAccessTokenAsync_RequestsAndCachesToken()
    {
        var handler = TestHttpMessageHandler.RespondWith(request =>
        {
            request.Headers.Authorization.Should().NotBeNull();
            request.Content.Should().NotBeNull();

            return new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(TokenResponse, Encoding.UTF8, "application/json"),
            };
        });

        var client = new HttpClient(handler)
        {
            BaseAddress = new Uri("https://identity.suse.com"),
        };

        var factory = new SingleClientHttpClientFactory(client);
        var cache = new MemoryCache(new MemoryCacheOptions());
        var provider = new RancherHubTokenProvider(factory, cache, NullLogger<RancherHubTokenProvider>.Instance);

        var options = new RancherHubConnectorOptions
        {
            ClientId = "client",
            ClientSecret = "secret",
            TokenEndpoint = new Uri("https://identity.suse.com/oauth/token"),
            Audience = "https://vexhub.suse.com",
        };
        options.Scopes.Clear();
        options.Scopes.Add("hub.read");
        options.Scopes.Add("hub.events");

        var token = await provider.GetAccessTokenAsync(options, CancellationToken.None);
        token.Should().NotBeNull();
        token!.Value.Should().Be("abc123");

        var cached = await provider.GetAccessTokenAsync(options, CancellationToken.None);
        cached.Should().NotBeNull();
        handler.InvocationCount.Should().Be(1);
    }

    [Fact]
    public async Task GetAccessTokenAsync_ReturnsNullWhenOfflinePreferred()
    {
        var handler = TestHttpMessageHandler.RespondWith(_ => new HttpResponseMessage(HttpStatusCode.OK));
        var client = new HttpClient(handler)
        {
            BaseAddress = new Uri("https://identity.suse.com"),
        };

        var factory = new SingleClientHttpClientFactory(client);
        var cache = new MemoryCache(new MemoryCacheOptions());
        var provider = new RancherHubTokenProvider(factory, cache, NullLogger<RancherHubTokenProvider>.Instance);
        var options = new RancherHubConnectorOptions
        {
            PreferOfflineSnapshot = true,
            ClientId = "client",
            ClientSecret = "secret",
            TokenEndpoint = new Uri("https://identity.suse.com/oauth/token"),
        };

        var token = await provider.GetAccessTokenAsync(options, CancellationToken.None);
        token.Should().BeNull();
        handler.InvocationCount.Should().Be(0);
    }

    [Fact]
    public async Task GetAccessTokenAsync_ReturnsNullWithoutCredentials()
    {
        var handler = TestHttpMessageHandler.RespondWith(_ => new HttpResponseMessage(HttpStatusCode.OK));
        var client = new HttpClient(handler)
        {
            BaseAddress = new Uri("https://identity.suse.com"),
        };

        var factory = new SingleClientHttpClientFactory(client);
        var cache = new MemoryCache(new MemoryCacheOptions());
        var provider = new RancherHubTokenProvider(factory, cache, NullLogger<RancherHubTokenProvider>.Instance);
        var options = new RancherHubConnectorOptions();

        var token = await provider.GetAccessTokenAsync(options, CancellationToken.None);
        token.Should().BeNull();
        handler.InvocationCount.Should().Be(0);
    }

    private sealed class SingleClientHttpClientFactory : IHttpClientFactory
    {
        private readonly HttpClient _client;

        public SingleClientHttpClientFactory(HttpClient client)
        {
            _client = client;
        }

        public HttpClient CreateClient(string name) => _client;
    }

    private sealed class TestHttpMessageHandler : HttpMessageHandler
    {
        private readonly Func<HttpRequestMessage, HttpResponseMessage> _responseFactory;

        private TestHttpMessageHandler(Func<HttpRequestMessage, HttpResponseMessage> responseFactory)
        {
            _responseFactory = responseFactory;
        }

        public int InvocationCount { get; private set; }

        public static TestHttpMessageHandler RespondWith(Func<HttpRequestMessage, HttpResponseMessage> responseFactory)
            => new(responseFactory);

        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            InvocationCount++;
            return Task.FromResult(_responseFactory(request));
        }
    }
}
