using System.Diagnostics.CodeAnalysis;
using RpgRoller.Contracts;

namespace RpgRoller.Components.Pages;

[ExcludeFromCodeCoverage]
public sealed class WorkspaceLiveStateController(
    WorkspaceState state,
    WorkspaceFeedbackService feedback,
    Func<bool> isPlayRoute,
    Func<bool> isAdminRoute,
    Func<Guid, Task> startStateEventsAsync,
    Func<Task> stopStateEventsCoreAsync,
    Func<Task> refreshCampaignRosterAsync,
    Func<Task> refreshSelectedCharacterSheetAsync,
    Func<Guid?, Task> refreshCampaignLogAsync,
    Func<Task> requestRefreshAsync)
{
    public async Task OnStateEventReceivedAsync(CampaignStateSnapshot state1)
    {
        if (state.StateRefreshInProgress)
            return;

        if (!state.SelectedCampaignId.HasValue || state1.CampaignId != state.SelectedCampaignId.Value)
            return;

        state.StateRefreshInProgress = true;
        try
        {
            if (state.CurrentCampaignState is null)
            {
                state.CurrentCampaignState = state1;
                return;
            }

            var previousState = state.CurrentCampaignState;
            var previousSelectedCharacterId = state.SelectedCharacterId;
            var previousSelectedCharacterVersion = GetCharacterVersion(previousState, previousSelectedCharacterId);
            var rosterChanged = state1.RosterVersion != previousState.RosterVersion;
            var logChanged = isPlayRoute() && state1.LogVersion != previousState.LogVersion;

            if (rosterChanged)
                await refreshCampaignRosterAsync();

            var selectedCharacterChanged = previousSelectedCharacterId != state.SelectedCharacterId;
            var selectedCharacterVersionChanged = isPlayRoute() && !selectedCharacterChanged &&
                                                  GetCharacterVersion(state1, state.SelectedCharacterId) !=
                                                  previousSelectedCharacterVersion;

            if (isPlayRoute() && (selectedCharacterChanged || selectedCharacterVersionChanged))
                await refreshSelectedCharacterSheetAsync();

            if (logChanged)
                await refreshCampaignLogAsync(state.CampaignLogCursor);

            state.CurrentCampaignState = state1;
        }
        finally
        {
            state.StateRefreshInProgress = false;
            await requestRefreshAsync();
        }
    }

    public async Task OnConnectionStateChangedAsync(string state1)
    {
        state.ConnectionState = state1 switch
        {
            "connected" => "connected",
            "reconnecting" => "reconnecting",
            _ => "offline"
        };

        if (state.ConnectionState == "reconnecting")
            feedback.Announce("Reconnecting to live updates.");

        if (state.ConnectionState == "offline")
            feedback.Announce("Live updates offline. Use manual refresh.");

        await requestRefreshAsync();
    }

    public async Task SyncStateEventsAsync()
    {
        if (state.User is null || !state.SelectedCampaignId.HasValue || isAdminRoute() || !isPlayRoute())
        {
            await StopStateEventsAsync();
            state.ConnectionState = "offline";
            return;
        }

        await startStateEventsAsync(state.SelectedCampaignId.Value);
        state.ConnectionState = "reconnecting";
    }

    public async Task StopStateEventsAsync()
    {
        if (!state.HasInteractiveRenderStarted)
            return;

        await stopStateEventsCoreAsync();
    }

    private static long GetCharacterVersion(CampaignStateSnapshot snapshot, Guid? characterId)
    {
        if (!characterId.HasValue)
            return 0;

        return snapshot.CharacterVersions.FirstOrDefault(version => version.CharacterId == characterId.Value)
            ?.Version ?? 0;
    }
}