import { formatRuntimeTimelineRecord } from "../runs/runtimeTimelineContract.js";
import { isRecord } from "../utils/json.js";
import { authorizeWorkspace, requireLocalUiSessionForBrowserRoute, writeAuthorityDiagnostic, } from "./authGuards.js";
import { PUBLIC_IDENTIFIER_SCAN_OPTIONS, writeJson } from "./publicResponse.js";
import { readJsonBody, readLimit, readOptionalString, } from "./requestParsing.js";
import { findRunIntentBridgeReceiptForRun, formatRunIntentReceiptRejectionBody, } from "../authority/runIntentReceipts.js";
import { formatCreateRunResult, formatPromptResult, readRunCapPolicy, readPromptOptions, readSessionCreateInput, } from "./runPayloads.js";
import { firstCapture, matchPath } from "./routeMatching.js";
export async function handleWorkspaceRunRoutes(context) {
    const { app, auth, config, request, response, url } = context;
    const workspaceRunsMatch = matchPath(url.pathname, /^\/workspaces\/([^/]+)\/runs$/);
    if (request.method === "POST" && workspaceRunsMatch) {
        if (auth.kind === "local-ui-session") {
            writeAuthorityDiagnostic(response, {
                guard: "bridge-token-required",
                reason: "bridge_token_required",
                status: 403,
                metadata: {
                    requiredScope: "runs:write",
                    grantedScopes: auth.session.scopes,
                },
            }, { request, routeId: "workspace.runs" });
            return true;
        }
        if (writeProductionLegacyBridgeTokenMutationDisabled(response, config, "legacy-bridge-token-run-route-disabled")) {
            return true;
        }
        const body = await readJsonBody(request);
        if (!isRecord(body) || typeof body.rootPath !== "string") {
            writeJson(response, 400, {
                error: "invalid_request",
                message: "Expected JSON body with rootPath string.",
            });
            return true;
        }
        const workspaceId = firstCapture(workspaceRunsMatch);
        if (!authorizeWorkspace(response, auth, workspaceId))
            return true;
        const createRunInput = {
            workspaceId,
            rootPath: body.rootPath,
        };
        const title = readOptionalString(body.title);
        const session = readSessionCreateInput(body.session, body.title);
        const capPolicy = readRunCapPolicy(body.capPolicy);
        const result = await app.runService.createRun({
            ...createRunInput,
            ...(title ? { title } : {}),
            ...(session ? { session } : {}),
            ...(capPolicy ? { capPolicy } : {}),
        });
        writeJson(response, 201, formatCreateRunResult(result));
        return true;
    }
    const runPromptMatch = matchPath(url.pathname, /^\/runs\/([^/]+)\/prompt$/);
    if (request.method === "POST" && runPromptMatch) {
        if (auth.kind === "local-ui-session") {
            writeAuthorityDiagnostic(response, {
                guard: "bridge-token-required",
                reason: "bridge_token_required",
                status: 403,
                metadata: {
                    requiredScope: "runs:write",
                    grantedScopes: auth.session.scopes,
                },
            }, { request, routeId: "runs.prompt" });
            return true;
        }
        if (writeProductionLegacyBridgeTokenMutationDisabled(response, config, "legacy-bridge-token-prompt-route-disabled")) {
            return true;
        }
        const body = await readJsonBody(request);
        if (!isRecord(body) || typeof body.text !== "string") {
            writeJson(response, 400, {
                error: "invalid_request",
                message: "Expected JSON body with text string.",
            });
            return true;
        }
        const runId = firstCapture(runPromptMatch);
        const run = app.localStore.findRun(runId);
        if (!run) {
            writeJson(response, 404, {
                error: "not_found",
                message: `Run ${runId} was not found.`,
            });
            return true;
        }
        if (!authorizeWorkspace(response, auth, run.workspaceId))
            return true;
        const prompt = readPromptOptions(body);
        const result = await app.runService.sendPrompt({
            runId,
            text: body.text,
            ...(prompt ? { prompt } : {}),
        });
        writeJson(response, 202, formatPromptResult(result, {
            textLength: body.text.length,
            hasModel: Boolean(prompt?.model),
        }));
        return true;
    }
    const runEventsMatch = matchPath(url.pathname, /^\/runs\/([^/]+)\/events$/);
    if (request.method === "GET" && runEventsMatch) {
        const runId = firstCapture(runEventsMatch);
        const run = app.localStore.findRun(runId);
        if (!run) {
            writeJson(response, 404, {
                error: "not_found",
                message: `Run ${runId} was not found.`,
            });
            return true;
        }
        if (!authorizeWorkspace(response, auth, run.workspaceId))
            return true;
        const eventOptions = {};
        const limit = readLimit(url.searchParams.get("limit"));
        const cursor = readOptionalString(url.searchParams.get("cursor"));
        if (limit !== undefined)
            eventOptions.limit = limit;
        if (cursor)
            eventOptions.cursor = cursor;
        const page = app.localStore.listRuntimeEventsForRun(run.id, eventOptions);
        writeJson(response, 200, {
            runId: run.id,
            events: page.events.map(formatRuntimeTimelineRecord),
            nextCursor: page.nextCursor ?? null,
        }, {
            surface: "runs.events",
            scanOptions: PUBLIC_IDENTIFIER_SCAN_OPTIONS,
        });
        return true;
    }
    const runProofEvidenceMatch = matchPath(url.pathname, /^\/runs\/([^/]+)\/proof-evidence$/);
    if (request.method === "POST" && runProofEvidenceMatch) {
        if (!requireLocalUiSessionForBrowserRoute(response, auth, {
            request,
            routeId: "runs.proof-evidence",
        })) {
            return true;
        }
        const runId = firstCapture(runProofEvidenceMatch);
        const run = app.localStore.findRun(runId);
        if (!run) {
            writeJson(response, 404, {
                error: "not_found",
                message: `Run ${runId} was not found.`,
            });
            return true;
        }
        if (!authorizeWorkspace(response, auth, run.workspaceId))
            return true;
        const body = await readJsonBody(request);
        if (!isRecord(body)) {
            writeJson(response, 400, {
                error: "invalid_request",
                message: "Expected JSON body.",
            });
            return true;
        }
        const receipt = findRunIntentBridgeReceiptForRun(runId, context.runIntentReceiptState);
        if (!receipt) {
            writeRunIntentReceiptRejection(response, 409, "intent_receipt_mismatch");
            return true;
        }
        try {
            const capHit = await app.runCapMonitor.evaluateAndSubmit({
                runId,
                runIntentBridgeReceipt: receipt,
            });
            if (capHit.status === "submitted") {
                writeJson(response, 200, {
                    status: "submitted",
                    reasonCode: capHit.reasonCode,
                    evidence: capHit.response,
                }, {
                    surface: "runs.proof-evidence",
                    scanOptions: RUN_PROOF_EVIDENCE_SCAN_OPTIONS,
                });
                return true;
            }
            if (capHit.status === "submission-failed") {
                writeJson(response, hostedEvidenceFailureStatus(capHit.submission), {
                    status: "failed",
                    reasonCode: capHit.reasonCode,
                    submission: formatHostedEvidenceFailure(capHit.submission),
                }, {
                    surface: "runs.proof-evidence",
                    scanOptions: RUN_PROOF_EVIDENCE_SCAN_OPTIONS,
                });
                return true;
            }
            const submission = app.runEvidenceService.captureBackendSubmission({
                runId,
                warnings: readRunEvidenceWarnings(body.warnings),
            });
            const hostedSubmission = await app.hostedEvidenceSubmitter.submit({
                submission,
                runIntentBridgeReceipt: receipt,
            });
            if (hostedSubmission.status === "succeeded") {
                writeJson(response, 200, formatHostedEvidenceSubmission(hostedSubmission), {
                    surface: "runs.proof-evidence",
                    scanOptions: RUN_PROOF_EVIDENCE_SCAN_OPTIONS,
                });
                return true;
            }
            const statusCode = hostedEvidenceFailureStatus(hostedSubmission);
            writeJson(response, statusCode, formatHostedEvidenceFailure(hostedSubmission), {
                surface: "runs.proof-evidence",
                scanOptions: RUN_PROOF_EVIDENCE_SCAN_OPTIONS,
            });
        }
        catch {
            writeJson(response, 400, {
                error: "invalid_request",
                message: "Run evidence could not be captured.",
            });
        }
        return true;
    }
    return false;
}
const RUN_PROOF_EVIDENCE_SCAN_OPTIONS = {
    allowedFieldNames: [
        ...PUBLIC_IDENTIFIER_SCAN_OPTIONS.allowedFieldNames,
        "attempts",
        "bridgeRunId",
        "changedFiles",
        "contentBytes",
        "contractVersion",
        "createdAt",
        "evidence",
        "evidenceId",
        "fileCount",
        "missingSettings",
        "patchArtifact",
        "preparationId",
        "reasonCode",
        "runIntentId",
        "status",
        "submission",
        "validations",
        "warnings",
    ],
};
function formatHostedEvidenceSubmission(result) {
    if (result.status !== "succeeded") {
        throw new Error("Hosted evidence submission did not succeed.");
    }
    return {
        status: "submitted",
        reasonCode: "run-evidence-submitted",
        attempts: result.attempts,
        evidence: result.response,
    };
}
function formatHostedEvidenceFailure(result) {
    if (result.status === "environment-gated") {
        return {
            status: "environment-gated",
            reasonCode: result.reasonCode,
            missingSettings: result.missingSettings,
        };
    }
    if (result.status === "failed") {
        return {
            status: "failed",
            reasonCode: result.reasonCode,
            attempts: result.attempts,
        };
    }
    throw new Error("Hosted evidence submission failure was invalid.");
}
function hostedEvidenceFailureStatus(result) {
    if (result.status === "environment-gated")
        return 424;
    if (result.status === "failed") {
        switch (result.reasonCode) {
            case "receipt-rejected":
                return 401;
            case "workspace-unauthorized":
                return 403;
            case "idempotency-conflict":
                return 409;
            case "payload-rejected":
                return 400;
            case "hosted-unavailable":
                return 503;
            case "hosted-response-invalid":
                return 502;
        }
    }
    return 500;
}
function writeRunIntentReceiptRejection(response, statusCode, reasonCode) {
    writeJson(response, statusCode, formatRunIntentReceiptRejectionBody(reasonCode));
}
function readRunEvidenceWarnings(value) {
    if (!Array.isArray(value))
        return [];
    return value.filter(isRecord).map((item) => ({
        label: typeof item.label === "string" ? item.label : "",
        severity: typeof item.severity === "string"
            ? item.severity
            : "warning",
    }));
}
function writeProductionLegacyBridgeTokenMutationDisabled(response, config, reasonCode) {
    if (config.mode !== "production")
        return false;
    writeJson(response, 403, {
        error: "bridge_authority_rejected",
        reasonCode,
        message: legacyBridgeTokenMutationDisableMessage(reasonCode),
    });
    return true;
}
function legacyBridgeTokenMutationDisableMessage(reasonCode) {
    switch (reasonCode) {
        case "legacy-bridge-token-run-route-disabled":
            return "Legacy run creation route is disabled in production.";
        case "legacy-bridge-token-prompt-route-disabled":
            return "Legacy prompt submission route is disabled in production.";
    }
}
