using System.Net;
using System.Text.Json;
using GmRelay.Web;
using GmRelay.Web.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Abstractions;

namespace GmRelay.Bot.Tests.Web;

public class DiscordAuthServiceTests
{
    [Fact]
    public void BuildAuthorizeUrl_GeneratesCorrectUrl()
    {
        var config = new ConfigurationBuilder()
            .AddInMemoryCollection(new Dictionary<string, string?>
            {
                ["Discord:ClientId"] = "12345",
                ["Discord:ClientSecret"] = "secret",
                ["Discord:RedirectUri"] = "https://example.com/callback"
            })
            .Build();

        var service = new DiscordAuthService(new TestHttpClientFactory(), config, NullLogger<DiscordAuthService>.Instance);
        var url = service.BuildAuthorizeUrl("state123");

        Assert.Contains("client_id=12345", url);
        Assert.Contains("response_type=code", url);
        Assert.Contains("state=state123", url);
        Assert.Contains("https%3A%2F%2Fexample.com%2Fcallback", url);
    }

    [Fact]
    public void BuildAuthorizeUrl_WithMissingConfig_ThrowsInvalidOperationException()
    {
        var config = new ConfigurationBuilder().Build();
        var service = new DiscordAuthService(new TestHttpClientFactory(), config, NullLogger<DiscordAuthService>.Instance);

        Assert.Throws<InvalidOperationException>(() => service.BuildAuthorizeUrl("state"));
    }

    [Fact]
    public async Task ExchangeCodeAsync_WithValidCode_ReturnsUser()
    {
        var handler = new TestHttpMessageHandler((request) =>
        {
            if (request.RequestUri?.AbsolutePath == "/api/oauth2/token")
            {
                var response = new DiscordTokenResponse("access_123", "Bearer", 604800, "identify");
                return new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = new StringContent(JsonSerializer.Serialize(response))
                };
            }

            if (request.RequestUri?.AbsolutePath == "/api/users/@me")
            {
                var user = new DiscordUser("98765", "TestUser", "0", "avatar123", null);
                return new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = new StringContent(JsonSerializer.Serialize(user))
                };
            }

            return new HttpResponseMessage(HttpStatusCode.NotFound);
        });

        var config = new ConfigurationBuilder()
            .AddInMemoryCollection(new Dictionary<string, string?>
            {
                ["Discord:ClientId"] = "client",
                ["Discord:ClientSecret"] = "secret",
                ["Discord:RedirectUri"] = "https://example.com/callback"
            })
            .Build();

        var factory = new TestHttpClientFactory(handler);
        var service = new DiscordAuthService(factory, config, NullLogger<DiscordAuthService>.Instance);

        var result = await service.ExchangeCodeAsync("valid_code");

        Assert.NotNull(result);
        Assert.Equal("98765", result.Id);
        Assert.Equal("TestUser", result.Username);
        Assert.Equal("https://cdn.discordapp.com/avatars/98765/avatar123.png", result.AvatarUrl);
    }

    [Fact]
    public async Task ExchangeCodeAsync_WithInvalidCode_ReturnsNull()
    {
        var handler = new TestHttpMessageHandler((request) =>
        {
            return new HttpResponseMessage(HttpStatusCode.BadRequest);
        });

        var config = new ConfigurationBuilder()
            .AddInMemoryCollection(new Dictionary<string, string?>
            {
                ["Discord:ClientId"] = "client",
                ["Discord:ClientSecret"] = "secret",
                ["Discord:RedirectUri"] = "https://example.com/callback"
            })
            .Build();

        var factory = new TestHttpClientFactory(handler);
        var service = new DiscordAuthService(factory, config, NullLogger<DiscordAuthService>.Instance);

        var result = await service.ExchangeCodeAsync("invalid_code");

        Assert.Null(result);
    }

    private class TestHttpClientFactory : IHttpClientFactory
    {
        private readonly HttpMessageHandler? _handler;

        public TestHttpClientFactory(HttpMessageHandler? handler = null)
        {
            _handler = handler;
        }

        public HttpClient CreateClient(string name) =>
            _handler is not null ? new HttpClient(_handler) : new HttpClient();
    }

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

        public TestHttpMessageHandler(Func<HttpRequestMessage, HttpResponseMessage> handler)
        {
            _handler = handler;
        }

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