using Microsoft.JSInterop;
using RpgRoller.Contracts;

namespace RpgRoller.Components.Pages;

public sealed class WorkspaceSessionCoordinator(
    WorkspaceState state,
    WorkspaceFeedbackService feedback,
    IJSRuntime js,
    RpgRollerApiClient apiClient,
    WorkspaceQueryService workspaceQuery,
    Func<bool> isAdminRoute,
    Func<Task> redirectToPlayAsync,
    Func<Guid?, Task> reloadCampaignsAsync,
    Func<Task> reloadCharacterCampaignOptionsAsync,
    Func<Task> refreshCampaignScopeAsync,
    Func<Task> requestRefreshAsync,
    Func<Task> syncStateEventsAsync,
    Func<Task> stopStateEventsAsync,
    Func<Task> ensureAdminUsersLoadedAsync,
    Action resetCampaignLogDetailState,
    Func<string?, Task> onLoggedOutAsync)
{
    public async Task InitializeAsync()
    {
        var storedPanel = await js.InvokeAsync<string?>("rpgRollerApi.getSessionValue", MobilePanelSessionKey);
        if (string.Equals(storedPanel, "log", StringComparison.OrdinalIgnoreCase))
            state.MobilePanel = "log";

        var storedRollVisibility =
            await js.InvokeAsync<string?>("rpgRollerApi.getSessionValue", RollVisibilitySessionKey);
        state.RollVisibility = NormalizeRollVisibility(storedRollVisibility);

        Guid? preferredCampaignId = null;
        if (!isAdminRoute())
        {
            var storedCampaignId = await js.InvokeAsync<string?>("rpgRollerApi.getSessionValue", CampaignSessionKey);
            if (Guid.TryParse(storedCampaignId, out var parsedCampaignId))
                preferredCampaignId = parsedCampaignId;
        }

        await CheckHealthAsync();

        var reloaded = await ReloadAuthenticatedSessionAsync(preferredCampaignId);
        if (!reloaded)
            await onLoggedOutAsync("Session expired. Please log in again.");
    }

    public async Task RetryAfterHealthIssueAsync()
    {
        await CheckHealthAsync();
        if (!state.HasHealthIssue && state.User is not null)
        {
            var reloaded = await ReloadAuthenticatedSessionAsync(state.SelectedCampaignId);
            if (!reloaded)
                await onLoggedOutAsync("Session expired. Please log in again.");
        }
    }

    public async Task LoadKnownUsernamesAsync()
    {
        try
        {
            var usernames = await workspaceQuery.GetUsernamesAsync();
            state.KnownUsernames = usernames.OrderBy(username => username, StringComparer.OrdinalIgnoreCase).ToList();
        }
        catch (ApiRequestException ex)
        {
            state.KnownUsernames = [];
            feedback.SetStatus(ex.Message, true);
        }
    }

    public async Task LogoutAsync()
    {
        if (state.IsMutating)
            return;

        state.IsMutating = true;
        try
        {
            await apiClient.RequestWithoutPayloadAsync("POST", "/api/auth/logout");
        }
        catch (ApiRequestException)
        {
        }
        finally
        {
            state.IsMutating = false;
        }

        ClearAuthenticatedState();
        await stopStateEventsAsync();
        await onLoggedOutAsync("Logged out.");
    }

    public async Task OnRollVisibilityChangedAsync(string visibility)
    {
        state.RollVisibility = NormalizeRollVisibility(visibility);
        await js.InvokeVoidAsync("rpgRollerApi.setSessionValue", RollVisibilitySessionKey, state.RollVisibility);
        await requestRefreshAsync();
    }

    public void ClearAuthenticatedState()
    {
        state.User = null;
        state.ActiveCharacterId = null;
        state.SelectedCampaignId = null;
        state.SelectedCampaign = null;
        state.Campaigns = [];
        state.CharacterCampaignOptions = [];
        state.SelectedCharacterSkills = [];
        state.SelectedCharacterSkillGroups = [];
        state.CampaignLog = [];
        state.CampaignLogCursor = null;
        resetCampaignLogDetailState();
        state.SelectedCharacterId = null;
        state.LastRoll = null;
        state.KnownUsernames = [];
        state.ShowCreateCharacterModal = false;
        state.ShowEditCharacterModal = false;
        state.CanEditCharacterOwner = false;
        state.CreateCharacterInitialModel = new();
        state.EditCharacterInitialModel = new();
        state.CreateCharacterFormVersion = 0;
        state.EditCharacterFormVersion = 0;
        state.AdminUsers = [];
        state.HasLoadedAdminUsers = false;
        state.IsAdminDataLoading = false;
        feedback.ClearToasts();
    }

    private async Task CheckHealthAsync()
    {
        state.HasHealthIssue = false;
        state.HealthIssueMessage = string.Empty;
        await Task.CompletedTask;
    }

    private async Task LoadRulesetsAsync()
    {
        try
        {
            state.Rulesets = (await workspaceQuery.GetRulesetsAsync()).ToList();
        }
        catch (ApiRequestException ex)
        {
            feedback.SetStatus(ex.Message, true);
        }
    }

    private async Task<bool> ReloadAuthenticatedSessionAsync(Guid? preferredCampaignId)
    {
        var me = await TryGetMeAsync();
        if (me is null)
        {
            ClearAuthenticatedState();
            await stopStateEventsAsync();
            return false;
        }

        state.User = me.User;
        state.ActiveCharacterId = me.ActiveCharacterId;
        if (!await EnsureRouteAccessAsync())
            return true;

        if (isAdminRoute())
        {
            await stopStateEventsAsync();
            state.ConnectionState = "offline";
            await ensureAdminUsersLoadedAsync();
            return true;
        }

        await LoadRulesetsAsync();
        await reloadCampaignsAsync(preferredCampaignId ?? me.CurrentCampaignId);
        await reloadCharacterCampaignOptionsAsync();
        await refreshCampaignScopeAsync();
        await syncStateEventsAsync();

        return true;
    }

    private async Task<MeResponse?> TryGetMeAsync()
    {
        try
        {
            return await workspaceQuery.GetMeAsync();
        }
        catch (ApiRequestException ex) when (ex.StatusCode == 401)
        {
            return null;
        }
    }

    private async Task<bool> EnsureRouteAccessAsync()
    {
        if (state.IsCurrentUserAdmin || !isAdminRoute())
        {
            return true;
        }

        state.AdminUsers = [];
        state.HasLoadedAdminUsers = false;
        await redirectToPlayAsync();
        return false;
    }

    private static string NormalizeRollVisibility(string? visibility)
    {
        return string.Equals(visibility, "private", StringComparison.OrdinalIgnoreCase) ? "private" : "public";
    }

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