using System.Net;
using System.Text;
using Xunit;
using ytLive.Models;
using ytLive.Services;

namespace ytLive.Tests;

public class YouTubeAuthServiceTests
{
    private sealed class FakeHandler : HttpMessageHandler
    {
        private readonly string _tokenResponse;
        private readonly string _channelResponse;

        public FakeHandler(string tokenResponse, string channelResponse)
        {
            _tokenResponse = tokenResponse;
            _channelResponse = channelResponse;
        }

        protected override Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var isToken = request.RequestUri!.PathAndQuery.Contains("/token");
            var body = isToken ? _tokenResponse : _channelResponse;
            return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(body, Encoding.UTF8, "application/json"),
            });
        }
    }

    private static YouTubeAuthService CreateService(string tokenResponse, string channelResponse) =>
        new("test-id", "test-secret", new HttpClient(new FakeHandler(tokenResponse, channelResponse)));

    [Fact]
    public async Task ExchangeCodeForToken_MockServer_ParsesChannel()
    {
        var service = CreateService(
            """{"access_token":"acc-123","refresh_token":"ref-123","expires_in":3600}""",
            """{"items":[{"id":"UC123","snippet":{"title":"Test Channel","thumbnails":{"default":{"url":"http://example/thumb.jpg"}}}}]}""");

        var channel = await service.ExchangeCodeForToken("auth-code", "http://localhost:8765/oauth2/callback");

        Assert.NotNull(channel);
        Assert.Equal("UC123", channel!.ChannelId);
        Assert.Equal("Test Channel", channel.DisplayName);
        Assert.Equal("http://example/thumb.jpg", channel.ProfileImageUrl);
        Assert.Equal("acc-123", channel.AccessToken);
        Assert.Equal("ref-123", channel.RefreshToken);
        Assert.InRange(channel.TokenExpiry, DateTime.UtcNow.AddSeconds(3590), DateTime.UtcNow.AddSeconds(3610));
    }

    [Fact]
    public async Task RefreshToken_MockServer_UpdatesAccessTokenAndExpiry()
    {
        var service = CreateService(
            """{"access_token":"new-acc","expires_in":3600}""",
            """{"items":[]}""");
        service.SetSession(new YouTubeChannel
        {
            RefreshToken = "ref-123",
            TokenExpiry = DateTime.UtcNow.AddMinutes(-5),
        });

        var ok = await service.RefreshToken();

        Assert.True(ok);
        Assert.Equal("new-acc", service.CurrentChannel!.AccessToken);
        Assert.InRange(service.CurrentChannel.TokenExpiry,
            DateTime.UtcNow.AddSeconds(3590), DateTime.UtcNow.AddSeconds(3610));
    }

    [Fact]
    public void ClearSession_DropsCurrentChannel()
    {
        var service = CreateService("""{"access_token":"a","refresh_token":"r","expires_in":3600}""",
            """{"items":[]}""");
        service.SetSession(new YouTubeChannel { DisplayName = "Test Channel" });

        service.ClearSession();

        Assert.Null(service.CurrentChannel);
    }
}
