using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using RpgRoller.Components.Pages.HomeControls;
using RpgRoller.Contracts;

namespace RpgRoller.Components.Pages;

[ExcludeFromCodeCoverage]
public partial class Workspace : IAsyncDisposable
{
    protected override void OnParametersSet()
    {
        State.IsScreenMenuOpen = false;
    }

    [JSInvokable]
    public Task OnStateEventReceived(CampaignStateSnapshot state)
    {
        return Live.OnStateEventReceivedAsync(state);
    }

    [JSInvokable]
    public Task OnConnectionStateChanged(string state)
    {
        return Live.OnConnectionStateChangedAsync(state);
    }

    public async ValueTask DisposeAsync()
    {
        await StopStateEventsAsync();
        DotNetRef?.Dispose();
    }

    private bool CanEditCharacter(CharacterSummary character)
    {
        return Campaigns.CanEditCharacter(character);
    }

    private void ClearAuthenticatedState()
    {
        Session.ClearAuthenticatedState();
    }

    private Task EnsureAdminUsersLoadedAsync()
    {
        return Admin.EnsureAdminUsersLoadedAsync();
    }

    private Task StopStateEventsAsync()
    {
        return Live.StopStateEventsAsync();
    }

    private async Task StartStateEventsCoreAsync(Guid campaignId)
    {
        DotNetRef ??= DotNetObjectReference.Create(this);
        await JS.InvokeVoidAsync("rpgRollerApi.startStateEvents", campaignId.ToString(), DotNetRef);
    }

    private async Task StopStateEventsCoreAsync()
    {
        try
        {
            await JS.InvokeVoidAsync("rpgRollerApi.stopStateEvents");
        }
        catch (JSDisconnectedException)
        {
        }
        catch (InvalidOperationException ex) when (IsStaticRenderInteropException(ex))
        {
        }
    }

    private void ToggleScreenMenu()
    {
        State.IsScreenMenuOpen = !State.IsScreenMenuOpen;
    }

    private Task NavigateToRouteAsync(string route)
    {
        State.IsScreenMenuOpen = false;
        Navigation.NavigateTo(route, forceLoad: true);
        return Task.CompletedTask;
    }

    private Task RedirectToPlayAsync()
    {
        if (IsPlayRoute)
            return Task.CompletedTask;

        Navigation.NavigateTo("/play", forceLoad: true);
        return Task.CompletedTask;
    }

    private Task RequestRefreshAsync()
    {
        return InvokeAsync(StateHasChanged);
    }

    private Task InitializeRouteAsync()
    {
        return InitializationTask ??= InitializeRouteCoreAsync();
    }

    private async Task InitializeRouteCoreAsync()
    {
        if (HasSessionInitialized)
            return;

        State.HasInteractiveRenderStarted = true;
        await Session.InitializeAsync();
        HasSessionInitialized = true;
        await RequestRefreshAsync();
    }

    private static bool IsStaticRenderInteropException(InvalidOperationException exception)
    {
        return exception.Message.Contains("statically rendered", StringComparison.OrdinalIgnoreCase);
    }

    [Inject] private IJSRuntime JS { get; set; } = null!;

    [Inject] private RpgRollerApiClient ApiClient { get; set; } = null!;

    [Inject] private WorkspaceQueryService WorkspaceQuery { get; set; } = null!;

    [Inject] private NavigationManager Navigation { get; set; } = null!;

    [Parameter] public EventCallback<string?> LoggedOut { get; set; }
    [Parameter] public WorkspaceRoute Route { get; set; } = WorkspaceRoute.Play;
    [Parameter] public RenderFragment<WorkspacePageContext>? ChildContent { get; set; }

    private WorkspaceState State { get; } = new();
    private bool HasSessionInitialized { get; set; }
    private bool IsPlayRoute => Route == WorkspaceRoute.Play;
    private bool IsCampaignsRoute => Route == WorkspaceRoute.Campaigns;
    private bool IsAdminRoute => Route == WorkspaceRoute.Admin;
    private string AppCssClass => IsPlayRoute ? "rr-app app-play" : "rr-app";
    private bool ShowCampaignInHeader => !IsAdminRoute;
    private bool ShowConnectionStateInHeader => IsPlayRoute;

    private WorkspacePageContext PageContext => new(State, Play, Campaigns, Admin, Scope, Session,
        InitializeRouteAsync, HasSessionInitialized, RequestRefreshAsync, AdminDatabaseDownloadUrl, HeaderMenuItems,
        IsPlayRoute, IsCampaignsRoute, IsAdminRoute);

    private WorkspaceCampaignScopeCoordinator Scope => m_Scope ??= new(State, Feedback, JS, WorkspaceQuery,
        () => IsPlayRoute, Play.EnsureSelectedCharacterActiveAsync, Play.RefreshSelectedCharacterSheetAsync,
        Play.RefreshCampaignLogAsync, Play.ResetCampaignLogDetailState, Play.ResetCampaignStateTracking,
        ClearAuthenticatedState,
        StopStateEventsAsync, message => LoggedOut.InvokeAsync(message));

    private WorkspaceLiveStateController Live => m_Live ??=
        new(State, Feedback, () => IsPlayRoute, () => IsAdminRoute, StartStateEventsCoreAsync,
            StopStateEventsCoreAsync, Scope.RefreshCampaignRosterAsync, Play.RefreshSelectedCharacterSheetAsync,
            Play.RefreshCampaignLogAsync, RequestRefreshAsync);

    private WorkspacePlayCoordinator Play => m_Play ??= new(State, Feedback, () => IsPlayRoute, ApiClient,
        WorkspaceQuery,
        CanEditCharacter, RequestRefreshAsync);

    private WorkspaceCampaignCoordinator Campaigns => m_Campaigns ??= new(State, Feedback, JS, ApiClient,
        Session.LoadKnownUsernamesAsync, Scope.ReloadCampaignsAsync, Scope.ReloadCharacterCampaignOptionsAsync,
        Scope.RefreshCampaignScopeAsync, Live.SyncStateEventsAsync, RequestRefreshAsync);

    private WorkspaceAdminCoordinator Admin => m_Admin ??= new(State, Feedback, JS, ApiClient, WorkspaceQuery,
        ClearAuthenticatedState, StopStateEventsAsync, message => LoggedOut.InvokeAsync(message));

    private WorkspaceFeedbackService Feedback => m_Feedback ??= new(State, RequestRefreshAsync);

    private WorkspaceSessionCoordinator Session => m_Session ??= new(State, Feedback, JS, ApiClient, WorkspaceQuery,
        () => IsAdminRoute, RedirectToPlayAsync,
        Scope.ReloadCampaignsAsync, Scope.ReloadCharacterCampaignOptionsAsync, Scope.RefreshCampaignScopeAsync,
        RequestRefreshAsync, Live.SyncStateEventsAsync, Live.StopStateEventsAsync, EnsureAdminUsersLoadedAsync,
        Play.ResetCampaignLogDetailState, message => LoggedOut.InvokeAsync(message));

    private IReadOnlyList<AppHeaderMenuItem> HeaderMenuItems
    {
        get
        {
            var items = new List<AppHeaderMenuItem>
            {
                new()
                {
                    Label = "Play",
                    IsActive = IsPlayRoute,
                    OnSelected = () => NavigateToRouteAsync("/play")
                },
                new()
                {
                    Label = "Campaign Management",
                    IsActive = IsCampaignsRoute,
                    OnSelected = () => NavigateToRouteAsync("/campaigns")
                }
            };

            if (State.IsCurrentUserAdmin)
            {
                items.Add(new()
                {
                    Label = "Admin",
                    IsActive = IsAdminRoute,
                    OnSelected = () => NavigateToRouteAsync("/admin")
                });
            }

            return items;
        }
    }

    private string AdminDatabaseDownloadUrl => Navigation.ToAbsoluteUri("api/admin/database").ToString();
    private DotNetObjectReference<Workspace>? DotNetRef { get; set; }

    private WorkspaceAdminCoordinator? m_Admin;
    private WorkspaceCampaignCoordinator? m_Campaigns;
    private WorkspaceFeedbackService? m_Feedback;
    private WorkspaceLiveStateController? m_Live;
    private WorkspacePlayCoordinator? m_Play;

    private WorkspaceCampaignScopeCoordinator? m_Scope;
    private WorkspaceSessionCoordinator? m_Session;
    private Task? InitializationTask { get; set; }
}