using Microsoft.JSInterop;
using RpgRoller.Components;
using RpgRoller.Components.Pages;

namespace RpgRoller.Tests;

public sealed class WorkspaceCampaignCoordinatorTests
{
    [Fact]
    public async Task OnCampaignCreatedAsync_RequestsRefreshAfterReloadingWorkspaceState()
    {
        var calls = new List<string>();
        var coordinator = CreateCoordinator(
            reloadCampaignsAsync: campaignId =>
            {
                calls.Add($"reloadCampaigns:{campaignId:D}");
                return Task.CompletedTask;
            },
            reloadCharacterCampaignOptionsAsync: () =>
            {
                calls.Add("reloadCharacterCampaignOptions");
                return Task.CompletedTask;
            },
            refreshCampaignScopeAsync: () =>
            {
                calls.Add("refreshCampaignScope");
                return Task.CompletedTask;
            },
            syncStateEventsAsync: () =>
            {
                calls.Add("syncStateEvents");
                return Task.CompletedTask;
            },
            requestRefreshAsync: () =>
            {
                calls.Add("requestRefresh");
                return Task.CompletedTask;
            });

        var campaignId = Guid.NewGuid();
        await coordinator.OnCampaignCreatedAsync(campaignId);

        Assert.Equal([
            $"reloadCampaigns:{campaignId:D}",
            "reloadCharacterCampaignOptions",
            "refreshCampaignScope",
            "syncStateEvents",
            "requestRefresh"
        ], calls);
    }

    [Fact]
    public async Task OnCharacterCreatedAsync_RequestsRefreshAfterReloadingWorkspaceState()
    {
        var calls = new List<string>();
        var coordinator = CreateCoordinator(
            reloadCampaignsAsync: campaignId =>
            {
                calls.Add($"reloadCampaigns:{campaignId:D}");
                return Task.CompletedTask;
            },
            reloadCharacterCampaignOptionsAsync: () =>
            {
                calls.Add("reloadCharacterCampaignOptions");
                return Task.CompletedTask;
            },
            refreshCampaignScopeAsync: () =>
            {
                calls.Add("refreshCampaignScope");
                return Task.CompletedTask;
            },
            syncStateEventsAsync: () =>
            {
                calls.Add("syncStateEvents");
                return Task.CompletedTask;
            },
            requestRefreshAsync: () =>
            {
                calls.Add("requestRefresh");
                return Task.CompletedTask;
            });

        var campaignId = Guid.NewGuid();
        await coordinator.OnCharacterCreatedAsync(campaignId);

        Assert.Equal([
            $"reloadCampaigns:{campaignId:D}",
            "reloadCharacterCampaignOptions",
            "refreshCampaignScope",
            "syncStateEvents",
            "requestRefresh"
        ], calls);
    }

    [Fact]
    public async Task OnCharacterUpdatedAsync_RequestsRefreshAfterReloadingWorkspaceState()
    {
        var calls = new List<string>();
        var coordinator = CreateCoordinator(
            reloadCampaignsAsync: campaignId =>
            {
                calls.Add($"reloadCampaigns:{campaignId:D}");
                return Task.CompletedTask;
            },
            reloadCharacterCampaignOptionsAsync: () =>
            {
                calls.Add("reloadCharacterCampaignOptions");
                return Task.CompletedTask;
            },
            refreshCampaignScopeAsync: () =>
            {
                calls.Add("refreshCampaignScope");
                return Task.CompletedTask;
            },
            syncStateEventsAsync: () =>
            {
                calls.Add("syncStateEvents");
                return Task.CompletedTask;
            },
            requestRefreshAsync: () =>
            {
                calls.Add("requestRefresh");
                return Task.CompletedTask;
            });

        var campaignId = Guid.NewGuid();
        await coordinator.OnCharacterUpdatedAsync(campaignId);

        Assert.Equal([
            $"reloadCampaigns:{campaignId:D}",
            "reloadCharacterCampaignOptions",
            "refreshCampaignScope",
            "syncStateEvents",
            "requestRefresh"
        ], calls);
    }

    private static WorkspaceCampaignCoordinator CreateCoordinator(
        Func<Guid?, Task>? reloadCampaignsAsync = null,
        Func<Task>? reloadCharacterCampaignOptionsAsync = null,
        Func<Task>? refreshCampaignScopeAsync = null,
        Func<Task>? syncStateEventsAsync = null,
        Func<Task>? requestRefreshAsync = null)
    {
        var state = new WorkspaceState();
        var feedback = new WorkspaceFeedbackService(state, () => Task.CompletedTask);
        return new WorkspaceCampaignCoordinator(
            state,
            feedback,
            new StubJsRuntime(),
            new RpgRollerApiClient(new StubJsRuntime()),
            () => Task.CompletedTask,
            reloadCampaignsAsync ?? (_ => Task.CompletedTask),
            reloadCharacterCampaignOptionsAsync ?? (() => Task.CompletedTask),
            refreshCampaignScopeAsync ?? (() => Task.CompletedTask),
            syncStateEventsAsync ?? (() => Task.CompletedTask),
            requestRefreshAsync ?? (() => Task.CompletedTask));
    }

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

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