/**
 * Loopback run-cancel mutation route.
 *
 * POST /workspaces/{wsId}/runs/{runId}/cancel
 *
 * Cancels a single in-flight PatchTrail-owned run without affecting the global
 * runtime or other runs. The run status is set to `cancelled` in the local store
 * so the frontend reflects it and the event ingestor stops processing events for
 * this run. The global OpenCode process is left running if other sessions exist.
 *
 * Authorized behind the least-privilege `runs:cancel` scope (via the route
 * table) and workspace authorization.  Fail-closed: only cancellable runs are
 * accepted; terminal states (completed, failed) return a finite 409 reason;
 * already-cancelled is idempotent 200.
 *
 * Bridge-token callers are rejected — this is a browser-only surface.
 */
import { authorizeWorkspace } from "./authGuards.js";
import { PUBLIC_IDENTIFIER_SCAN_OPTIONS, writeJson } from "./publicResponse.js";
import { firstCapture, matchPath, secondCapture } from "./routeMatching.js";
import { requireLocalUiSessionForBrowserRoute } from "./authGuards.js";
const RUN_CANCEL_ROUTE_PATTERN = /^\/workspaces\/([^/]+)\/runs\/([^/]+)\/cancel$/;
/** Terminal run states that cannot be cancelled. */
const TERMINAL_STATES = new Set(["completed", "failed"]);
export async function handleRunCancelRoute(context) {
    const { app, auth, request, response, url } = context;
    if (request.method !== "POST")
        return false;
    const match = matchPath(url.pathname, RUN_CANCEL_ROUTE_PATTERN);
    if (!match)
        return false;
    const workspaceId = firstCapture(match);
    const runId = secondCapture(match);
    if (!requireLocalUiSessionForBrowserRoute(response, auth, {
        request,
        routeId: "workspace.runs.cancel",
    })) {
        return true;
    }
    const run = app.localStore.findRun(runId);
    if (!run) {
        writeJson(response, 404, {
            error: "not_found",
            message: `Run ${runId} was not found.`,
        });
        return true;
    }
    // The URL workspace must match the run's workspace; otherwise the caller
    // is addressing a run that does not belong to the claimed workspace.
    if (run.workspaceId !== workspaceId) {
        writeJson(response, 403, {
            error: "forbidden",
            message: "Run does not belong to the requested workspace.",
        });
        return true;
    }
    if (!authorizeWorkspace(response, auth, run.workspaceId))
        return true;
    // Reject cancel on terminal states with a finite reason.
    if (run.status === "cancelled") {
        writeJson(response, 200, {
            runId: run.id,
            status: "cancelled",
            cancelledAt: run.updatedAt,
        }, {
            surface: "workspace.runs.cancel",
            scanOptions: RUN_CANCEL_SCAN_OPTIONS,
        });
        return true;
    }
    if (TERMINAL_STATES.has(run.status)) {
        writeJson(response, 409, {
            error: "run_not_cancellable",
            message: `Run ${runId} is in terminal state "${run.status}" and cannot be cancelled.`,
        });
        return true;
    }
    const cancelledAt = new Date().toISOString();
    app.localStore.updateRunStatus(runId, "cancelled");
    app.audit.append({
        type: "run.cancelled",
        severity: "info",
        message: "Run cancelled by local UI session.",
        metadata: {
            runId: run.id,
            workspaceId: run.workspaceId,
            previousStatus: run.status,
            cancelledAt,
            source: "local_ui",
        },
    });
    writeJson(response, 200, {
        runId: run.id,
        status: "cancelled",
        cancelledAt,
    }, {
        surface: "workspace.runs.cancel",
        scanOptions: RUN_CANCEL_SCAN_OPTIONS,
    });
    return true;
}
const RUN_CANCEL_SCAN_OPTIONS = {
    allowedFieldNames: [
        ...PUBLIC_IDENTIFIER_SCAN_OPTIONS.allowedFieldNames,
        "status",
        "cancelledAt",
    ],
};
