using System.Diagnostics.CodeAnalysis;
using Microsoft.JSInterop;

namespace RpgRoller.Components.Pages;

[ExcludeFromCodeCoverage]
public sealed class WorkspaceCampaignScopeCoordinator(
    WorkspaceState state,
    WorkspaceFeedbackService feedback,
    IJSRuntime js,
    WorkspaceQueryService workspaceQuery,
    Func<bool> isPlayRoute,
    Func<Task> ensureSelectedCharacterActiveAsync,
    Func<Task> refreshSelectedCharacterSheetAsync,
    Func<Guid?, Task> refreshCampaignLogAsync,
    Action resetCampaignLogDetailState,
    Action resetCampaignStateTracking,
    Action clearAuthenticatedState,
    Func<Task> stopStateEventsAsync,
    Func<string?, Task> onLoggedOutAsync)
{
    public async Task ReloadCampaignsAsync(Guid? preferredCampaignId)
    {
        var campaigns = await workspaceQuery.GetCampaignsAsync();
        state.Campaigns = campaigns.OrderBy(campaign => campaign.Name, StringComparer.OrdinalIgnoreCase).ToList();

        if (state.Campaigns.Count == 0)
        {
            state.SelectedCampaignId = null;
            await js.InvokeVoidAsync("rpgRollerApi.setSessionValue", CampaignSessionKey, null);
            return;
        }

        var campaignIds = state.Campaigns.Select(campaign => campaign.Id).ToHashSet();
        if (preferredCampaignId.HasValue && campaignIds.Contains(preferredCampaignId.Value))
            state.SelectedCampaignId = preferredCampaignId.Value;
        else if (!state.SelectedCampaignId.HasValue || !campaignIds.Contains(state.SelectedCampaignId.Value))
            state.SelectedCampaignId = state.Campaigns[0].Id;

        await js.InvokeVoidAsync("rpgRollerApi.setSessionValue", CampaignSessionKey,
            state.SelectedCampaignId?.ToString());
    }

    public async Task ReloadCharacterCampaignOptionsAsync()
    {
        var campaignOptions = await workspaceQuery.GetCharacterCampaignOptionsAsync();
        state.CharacterCampaignOptions =
            campaignOptions.OrderBy(campaign => campaign.Name, StringComparer.OrdinalIgnoreCase).ToList();
    }

    public async Task RefreshCampaignRosterAsync()
    {
        if (!state.SelectedCampaignId.HasValue)
        {
            state.SelectedCampaign = null;
            state.SelectedCharacterId = null;
            return;
        }

        state.SelectedCampaign = await workspaceQuery.GetCampaignAsync(state.SelectedCampaignId.Value);
        SyncSelectedCharacter();

        if (isPlayRoute() && state.PlaySelectedCharacterId.HasValue &&
            state.SelectedCharacterId != state.PlaySelectedCharacterId)
            state.SelectedCharacterId = state.PlaySelectedCharacterId;

        await ensureSelectedCharacterActiveAsync();
    }

    public async Task RefreshCampaignScopeAsync()
    {
        if (!state.SelectedCampaignId.HasValue)
        {
            state.SelectedCampaign = null;
            state.SelectedCharacterSkills = [];
            state.SelectedCharacterSkillGroups = [];
            state.CampaignLog = [];
            state.SelectedCharacterId = null;
            state.ConnectionState = "offline";
            state.CurrentCampaignState = null;
            state.CampaignLogCursor = null;
            resetCampaignLogDetailState();
            return;
        }

        state.IsCampaignDataLoading = true;
        try
        {
            await RefreshCampaignRosterAsync();
            if (isPlayRoute())
            {
                await refreshSelectedCharacterSheetAsync();
                await refreshCampaignLogAsync(null);
                resetCampaignStateTracking();
            }
            else
            {
                state.SelectedCharacterSkills = [];
                state.SelectedCharacterSkillGroups = [];
                state.CampaignLog = [];
                state.ConnectionState = "offline";
                state.CurrentCampaignState = null;
                state.CampaignLogCursor = null;
                resetCampaignLogDetailState();
            }
        }
        catch (ApiRequestException ex) when (ex.StatusCode == 401)
        {
            clearAuthenticatedState();
            await stopStateEventsAsync();
            await onLoggedOutAsync("Session expired. Please log in again.");
        }
        catch (ApiRequestException ex)
        {
            feedback.SetStatus(ex.Message, true);
        }
        finally
        {
            state.IsCampaignDataLoading = false;
        }
    }

    public async Task SetMobilePanelAsync(string panel)
    {
        state.MobilePanel = string.Equals(panel, "log", StringComparison.OrdinalIgnoreCase) ? "log" : "character";
        await js.InvokeVoidAsync("rpgRollerApi.setSessionValue", MobilePanelSessionKey, state.MobilePanel);
    }

    private void SyncSelectedCharacter()
    {
        if (state.SelectedCampaign is null || state.SelectedCampaign.Characters.Length == 0)
        {
            state.SelectedCharacterId = null;
            return;
        }

        var candidateIds = state.SelectedCampaign.Characters.Select(character => character.Id).ToHashSet();
        if (state.SelectedCharacterId.HasValue && candidateIds.Contains(state.SelectedCharacterId.Value))
            return;

        if (state.ActiveCharacterId.HasValue && candidateIds.Contains(state.ActiveCharacterId.Value))
        {
            state.SelectedCharacterId = state.ActiveCharacterId;
            return;
        }

        state.SelectedCharacterId = state.SelectedCampaign.Characters[0].Id;
    }

    private const string CampaignSessionKey = "campaign";
    private const string MobilePanelSessionKey = "play-panel";
}