using System.Text.Json;
using Microsoft.JSInterop;
using RpgRoller.Components;

namespace RpgRoller.Tests;

public sealed class WorkspaceQueryServiceTests
{
    private sealed class StubJsRuntime(Func<string, object?[]?, Type, object?> handler) : IJSRuntime
    {
        public ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args)
        {
            return ValueTask.FromResult((TValue)handler(identifier, args, typeof(TValue))!);
        }

        public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken,
            object?[]? args)
        {
            return InvokeAsync<TValue>(identifier, args);
        }
    }

    [Fact]
    public async Task GetCampaignsAsync_UsesCampaignsApiEndpoint()
    {
        var queryService = new WorkspaceQueryService(new RpgRollerApiClient(
            new StubJsRuntime((identifier, args, returnType) =>
            {
                Assert.Equal("rpgRollerApi.request", identifier);
                Assert.Equal("GET", args![0]);
                Assert.Equal("/api/campaigns", args[1]);
                Assert.Null(args[2]);

                return CreateJsApiResponse(args: new
                {
                    ok = true,
                    status = 200,
                    data = new[]
                    {
                        new CampaignSummary(Guid.NewGuid(), "Alpha", "d6", new CampaignGmSummary(Guid.NewGuid(), "GM"),
                            1)
                    }
                }, returnType);
            })));

        var campaigns = await queryService.GetCampaignsAsync();

        var campaign = Assert.Single(campaigns);
        Assert.Equal("Alpha", campaign.Name);
    }

    [Fact]
    public async Task GetMeAsync_MapsUnauthorizedApiResponseToApiRequestException()
    {
        var queryService = new WorkspaceQueryService(new RpgRollerApiClient(
            new StubJsRuntime((identifier, args, returnType) =>
            {
                Assert.Equal("rpgRollerApi.request", identifier);
                Assert.Equal("GET", args![0]);
                Assert.Equal("/api/me", args[1]);

                return CreateJsApiResponse(args: new
                {
                    ok = false,
                    status = 401,
                    error = "You must be logged in.",
                    code = "unauthorized",
                    data = (object?)null
                }, returnType);
            })));

        var exception = await Assert.ThrowsAsync<ApiRequestException>(queryService.GetMeAsync);

        Assert.Equal(401, exception.StatusCode);
        Assert.Equal("You must be logged in.", exception.Message);
        Assert.Equal("unauthorized", exception.ErrorCode);
    }

    private static object CreateJsApiResponse(object args, Type returnType)
    {
        var json = JsonSerializer.Serialize(args);
        return JsonSerializer.Deserialize(json, returnType, JsonOptions)!;
    }

    private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
}