@page "/sessions"
@inherits DashboardPageBase
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject IDashboardSessionAdminService SessionAdminService

<PageTitle>Dashboard Sessions</PageTitle>

@if (Snapshot is null)
{
    <div class="empty-state">Loading sessions.</div>
}
else
{
    <div class="dashboard-page-header">
        <div>
            <h1>Sessions</h1>
            <div class="text-secondary">@Snapshot.Sessions.Count session rows</div>
        </div>
    </div>

    @if (CanManage && !string.IsNullOrWhiteSpace(ResultMessage))
    {
        <div class="alert @(LastOperationSucceeded ? "alert-success" : "alert-danger")" role="alert">
            @ResultMessage
        </div>
    }

    @if (CanManage)
    {
        <ConfirmDialog IsOpen="@(PendingAction is not null)"
                       Title="@(PendingAction?.Title ?? string.Empty)"
                       Message="@(PendingAction?.Message ?? string.Empty)"
                       ConfirmLabel="@(PendingAction?.ConfirmLabel ?? "Confirm")"
                       ConfirmButtonClass="@(PendingAction?.ConfirmButtonClass ?? "btn-primary")"
                       IsBusy="IsBusy"
                       OnConfirm="ConfirmPendingAsync"
                       OnCancel="CancelPending" />
    }

    <section class="dashboard-section">
        @if (Snapshot.Sessions.Count == 0)
        {
            <div class="empty-state">No sessions are active or retained.</div>
        }
        else
        {
            <div class="table-responsive">
                <table class="table table-sm align-middle dashboard-table">
                    <thead>
                        <tr>
                            <th scope="col">Session</th>
                            <th scope="col">State</th>
                            <th scope="col">Client</th>
                            <th scope="col">Backend</th>
                            <th scope="col">Worker</th>
                            <th scope="col">Events</th>
                            <th scope="col">Opened</th>
                            <th scope="col">Activity</th>
                            <th scope="col">Heartbeat</th>
                            <th scope="col">Fault</th>
                            @if (CanManage)
                            {
                                <th scope="col">Actions</th>
                            }
                        </tr>
                    </thead>
                    <tbody>
                        @foreach (DashboardSessionSummary session in Snapshot.Sessions)
                        {
                            <tr>
                                <td><NavLink href="@($"sessions/{Uri.EscapeDataString(session.SessionId)}")"><code>@session.SessionId</code></NavLink></td>
                                <td><StatusBadge Text="@session.State.ToString()" /></td>
                                <td>@DashboardDisplay.Text(session.ClientIdentity)</td>
                                <td>@session.BackendName</td>
                                <td>
                                    @(session.WorkerProcessId?.ToString(System.Globalization.CultureInfo.InvariantCulture) ?? "-")
                                    @if (session.WorkerState is not null)
                                    {
                                        <span class="ms-1"><StatusBadge Text="@session.WorkerState.ToString()" /></span>
                                    }
                                </td>
                                <td>@DashboardDisplay.Count(session.EventsReceived)</td>
                                <td>@DashboardDisplay.DateTime(session.OpenedAt)</td>
                                <td>@DashboardDisplay.DateTime(session.LastClientActivityAt)</td>
                                <td>@DashboardDisplay.DateTime(session.LastWorkerHeartbeatAt)</td>
                                @* Full text stays reachable on the tooltip and on the session detail page. *@
                                <td title="@session.LastFault">@DashboardDisplay.Abbreviate(session.LastFault)</td>
                                @if (CanManage)
                                {
                                    <td>
                                        <div class="btn-group btn-group-sm" role="group" aria-label="Session actions">
                                            <button type="button" class="btn btn-outline-warning"
                                                    disabled="@IsBusy"
                                                    @onclick="() => RequestClose(session.SessionId)">
                                                Close
                                            </button>
                                            <button type="button" class="btn btn-outline-danger"
                                                    disabled="@IsBusy"
                                                    @onclick="() => RequestKill(session.SessionId)">
                                                Kill
                                            </button>
                                        </div>
                                    </td>
                                }
                            </tr>
                        }
                    </tbody>
                </table>
            </div>
        }
    </section>
}

@code {
    private bool CanManage { get; set; }

    private bool IsBusy { get; set; }

    private string? ResultMessage { get; set; }

    private bool LastOperationSucceeded { get; set; }

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync().ConfigureAwait(false);

        AuthenticationState authenticationState = await AuthenticationStateProvider.GetAuthenticationStateAsync()
            .ConfigureAwait(false);
        CanManage = SessionAdminService.CanManage(authenticationState.User);
    }

    private PendingConfirm? PendingAction { get; set; }

    private void RequestClose(string sessionId)
    {
        if (IsBusy)
        {
            return;
        }

        PendingAction = new PendingConfirm(
            Title: "Close session?",
            Message: $"Gracefully close session {sessionId}? The worker will be shut down.",
            ConfirmLabel: "Close",
            ConfirmButtonClass: "btn-warning",
            Action: user => SessionAdminService.CloseSessionAsync(user, sessionId, CancellationToken.None));
    }

    private void RequestKill(string sessionId)
    {
        if (IsBusy)
        {
            return;
        }

        PendingAction = new PendingConfirm(
            Title: "Kill worker?",
            Message: $"Forcefully kill the worker for session {sessionId}? This skips graceful shutdown.",
            ConfirmLabel: "Kill",
            ConfirmButtonClass: "btn-danger",
            Action: user => SessionAdminService.KillWorkerAsync(user, sessionId, CancellationToken.None));
    }

    private void CancelPending()
    {
        if (!IsBusy)
        {
            PendingAction = null;
        }
    }

    private async Task ConfirmPendingAsync()
    {
        if (IsBusy || PendingAction is null)
        {
            return;
        }

        Func<System.Security.Claims.ClaimsPrincipal, Task<DashboardSessionAdminResult>> action = PendingAction.Action;

        IsBusy = true;
        try
        {
            AuthenticationState authenticationState = await AuthenticationStateProvider.GetAuthenticationStateAsync()
                .ConfigureAwait(false);
            CanManage = SessionAdminService.CanManage(authenticationState.User);
            DashboardSessionAdminResult result = await action(authenticationState.User).ConfigureAwait(false);
            ResultMessage = result.Message;
            LastOperationSucceeded = result.Succeeded;
        }
        finally
        {
            IsBusy = false;
            PendingAction = null;
        }
    }

    private sealed record PendingConfirm(
        string Title,
        string Message,
        string ConfirmLabel,
        string ConfirmButtonClass,
        Func<System.Security.Claims.ClaimsPrincipal, Task<DashboardSessionAdminResult>> Action);
}
