/**
 * Bridge HTTP routes for the D085 hybrid local GitHub-write path.
 *
 * Routes:
 *   GET    /workspaces/{ws}/forge/github/status   → sanitized credential status
 *   POST   /workspaces/{ws}/forge/github/connect  → OAuth code exchange or PAT store
 *   DELETE /workspaces/{ws}/forge/github/connect  → revoke forge token
 *   POST   /workspaces/{ws}/forge/evidence        → commit evidence to branch
 *   POST   /workspaces/{ws}/forge/proof-draft     → create proof commit
 *   POST   /workspaces/{ws}/forge/draft-pr        → open draft PR
 *
 * All mutation routes are leak-guarded (PUBLIC_IDENTIFIER_SCAN_OPTIONS) and
 * audit-logged. The forge token is never included in any response.
 */
import { authorizeWorkspace } from "./authGuards.js";
import { PUBLIC_IDENTIFIER_SCAN_OPTIONS, writeJson, } from "./publicResponse.js";
import { readJsonBody } from "./requestParsing.js";
import { firstCapture, matchPath } from "./routeMatching.js";
import { isRecord } from "../utils/json.js";
const FORGE_RESPONSE_SCAN_OPTIONS = {
    allowedFieldNames: [
        ...PUBLIC_IDENTIFIER_SCAN_OPTIONS.allowedFieldNames,
        "accountId",
        "authorizeUrl",
        "backend",
        "branch",
        "commitSha",
        "degraded",
        "maskedLabel",
        "message",
        "proofCommitSha",
        "proofDraftId",
        "pullRequestNumber",
        "pullRequestUrl",
        "reasonCode",
        "repositories",
        "state",
        "status",
        "statusCode",
        "updatedAt",
    ],
};
export async function handleForgeRoutes(context) {
    const { app, auth, request, response, url } = context;
    // GET /workspaces/{ws}/forge/github/status
    const statusMatch = matchPath(url.pathname, /^\/workspaces\/([^/]+)\/forge\/github\/status$/);
    if (request.method === "GET" && statusMatch) {
        const workspaceId = firstCapture(statusMatch);
        if (!authorizeWorkspace(response, auth, workspaceId))
            return true;
        try {
            const forgeStatus = await app.githubForgeAuth.status();
            writeJson(response, 200, forgeStatus, {
                surface: "forge.github.status",
                scanOptions: FORGE_RESPONSE_SCAN_OPTIONS,
            });
        }
        catch (error) {
            writeJson(response, 500, {
                error: "forge_status_failed",
                message: error instanceof Error
                    ? error.message
                    : "Could not read forge status.",
            });
        }
        return true;
    }
    // GET /workspaces/{ws}/forge/github/oauth-url
    const oauthUrlMatch = matchPath(url.pathname, /^\/workspaces\/([^/]+)\/forge\/github\/oauth-url$/);
    if (request.method === "GET" && oauthUrlMatch) {
        const workspaceId = firstCapture(oauthUrlMatch);
        if (!authorizeWorkspace(response, auth, workspaceId))
            return true;
        handleForgeOAuthUrl(context);
        return true;
    }
    // POST /workspaces/{ws}/forge/github/connect
    const connectMatch = matchPath(url.pathname, /^\/workspaces\/([^/]+)\/forge\/github\/connect$/);
    if (request.method === "POST" && connectMatch) {
        const workspaceId = firstCapture(connectMatch);
        if (!authorizeWorkspace(response, auth, workspaceId))
            return true;
        await handleForgeConnect(context);
        return true;
    }
    // DELETE /workspaces/{ws}/forge/github/connect
    if (request.method === "DELETE" && connectMatch) {
        const workspaceId = firstCapture(connectMatch);
        if (!authorizeWorkspace(response, auth, workspaceId))
            return true;
        await handleForgeDisconnect(context);
        return true;
    }
    // POST /workspaces/{ws}/forge/evidence
    const evidenceMatch = matchPath(url.pathname, /^\/workspaces\/([^/]+)\/forge\/evidence$/);
    if (request.method === "POST" && evidenceMatch) {
        const workspaceId = firstCapture(evidenceMatch);
        if (!authorizeWorkspace(response, auth, workspaceId))
            return true;
        await handleForgeEvidence(context);
        return true;
    }
    // POST /workspaces/{ws}/forge/proof-draft
    const proofDraftMatch = matchPath(url.pathname, /^\/workspaces\/([^/]+)\/forge\/proof-draft$/);
    if (request.method === "POST" && proofDraftMatch) {
        const workspaceId = firstCapture(proofDraftMatch);
        if (!authorizeWorkspace(response, auth, workspaceId))
            return true;
        await handleForgeProofDraft(context);
        return true;
    }
    // POST /workspaces/{ws}/forge/draft-pr
    const draftPrMatch = matchPath(url.pathname, /^\/workspaces\/([^/]+)\/forge\/draft-pr$/);
    if (request.method === "POST" && draftPrMatch) {
        const workspaceId = firstCapture(draftPrMatch);
        if (!authorizeWorkspace(response, auth, workspaceId))
            return true;
        await handleForgeDraftPr(context);
        return true;
    }
    return false;
}
// ---------------------------------------------------------------------------
// Connect / disconnect handlers
// ---------------------------------------------------------------------------
function handleForgeOAuthUrl(context) {
    const { app, response, url } = context;
    try {
        const redirectUri = url.searchParams.get("redirectUri");
        if (typeof redirectUri !== "string" || !redirectUri) {
            writeJson(response, 400, {
                error: "invalid_request",
                message: 'A loopback "redirectUri" query parameter is required.',
            });
            return;
        }
        const authorization = app.githubForgeAuth.issueOAuthAuthorization({
            redirectUri,
        });
        writeJson(response, 200, authorization, {
            surface: "forge.github.oauth-url",
            scanOptions: FORGE_RESPONSE_SCAN_OPTIONS,
        });
    }
    catch (error) {
        writeForgeError(response, error);
    }
}
async function handleForgeConnect(context) {
    const { app, response } = context;
    try {
        const body = await readJsonBody(context.request);
        if (!isRecord(body)) {
            writeJson(response, 400, {
                error: "invalid_request",
                message: 'Expected JSON body with mode "oauth" or "pat".',
            });
            return;
        }
        const mode = body.mode;
        if (mode === "oauth") {
            if (typeof body.code !== "string" || !body.code) {
                writeJson(response, 400, {
                    error: "invalid_request",
                    message: 'OAuth mode requires a "code" string.',
                });
                return;
            }
            if (body.state !== undefined && typeof body.state !== "string") {
                writeJson(response, 400, {
                    error: "invalid_request",
                    message: 'OAuth "state" must be a string when provided.',
                });
                return;
            }
            const result = await app.githubForgeAuth.connect({
                mode: "oauth",
                code: body.code,
                ...(typeof body.state === "string" ? { state: body.state } : {}),
            });
            writeJson(response, 200, result, {
                surface: "forge.github.connect",
                scanOptions: FORGE_RESPONSE_SCAN_OPTIONS,
            });
            return;
        }
        if (mode === "pat") {
            if (typeof body.token !== "string" || !body.token) {
                writeJson(response, 400, {
                    error: "invalid_request",
                    message: 'PAT mode requires a "token" string.',
                });
                return;
            }
            const result = await app.githubForgeAuth.connect({
                mode: "pat",
                token: body.token,
            });
            writeJson(response, 200, result, {
                surface: "forge.github.connect",
                scanOptions: FORGE_RESPONSE_SCAN_OPTIONS,
            });
            return;
        }
        writeJson(response, 400, {
            error: "invalid_request",
            message: 'Mode must be "oauth" or "pat".',
        });
    }
    catch (error) {
        writeForgeError(response, error);
    }
}
async function handleForgeDisconnect(context) {
    const { app, response } = context;
    try {
        const result = await app.githubForgeAuth.disconnect();
        writeJson(response, 200, result, {
            surface: "forge.github.disconnect",
            scanOptions: FORGE_RESPONSE_SCAN_OPTIONS,
        });
    }
    catch (error) {
        writeForgeError(response, error);
    }
}
// ---------------------------------------------------------------------------
// Forge mutation handlers
// ---------------------------------------------------------------------------
async function handleForgeEvidence(context) {
    const { app, response } = context;
    try {
        const body = await readJsonBody(context.request);
        if (!isRecord(body)) {
            writeJson(response, 400, {
                error: "invalid_request",
                message: "Expected JSON body with evidence submission fields.",
            });
            return;
        }
        if (typeof body.repository !== "string" ||
            typeof body.branch !== "string" ||
            typeof body.message !== "string" ||
            !Array.isArray(body.files)) {
            writeJson(response, 400, {
                error: "invalid_request",
                message: "Required fields: repository (string), branch (string), message (string), files (array).",
            });
            return;
        }
        const result = await app.githubForgeClient.submitEvidence({
            repository: body.repository,
            branch: body.branch,
            message: body.message,
            files: body.files,
        });
        if (result.status === "failed") {
            writeJson(response, 422, result, {
                surface: "forge.evidence",
                scanOptions: FORGE_RESPONSE_SCAN_OPTIONS,
            });
            return;
        }
        app.audit.append({
            type: "forge.evidence_submitted",
            severity: "info",
            message: `Evidence committed to ${body.repository}/${body.branch}.`,
            metadata: {
                repository: body.repository,
                branch: body.branch,
                commitSha: result.commitSha,
                fileCount: Array.isArray(body.files) ? body.files.length : 0,
            },
        });
        writeJson(response, 200, result, {
            surface: "forge.evidence",
            scanOptions: FORGE_RESPONSE_SCAN_OPTIONS,
        });
    }
    catch (error) {
        writeJson(response, 500, {
            error: "forge_evidence_failed",
            message: error instanceof Error ? error.message : "Evidence submission failed.",
        });
    }
}
async function handleForgeProofDraft(context) {
    const { app, response } = context;
    try {
        const body = await readJsonBody(context.request);
        if (!isRecord(body)) {
            writeJson(response, 400, {
                error: "invalid_request",
                message: "Expected JSON body with proof draft fields.",
            });
            return;
        }
        if (typeof body.repository !== "string" ||
            typeof body.branch !== "string" ||
            typeof body.evidenceCommitSha !== "string") {
            writeJson(response, 400, {
                error: "invalid_request",
                message: "Required fields: repository (string), branch (string), evidenceCommitSha (string).",
            });
            return;
        }
        const result = await app.githubForgeClient.draftProof({
            repository: body.repository,
            branch: body.branch,
            evidenceCommitSha: body.evidenceCommitSha,
            proofSummary: typeof body.proofSummary === "string"
                ? body.proofSummary
                : "Proof draft from PatchTrail evidence.",
            changedFileCount: typeof body.changedFileCount === "number" ? body.changedFileCount : 0,
            validationCount: typeof body.validationCount === "number" ? body.validationCount : 0,
        });
        if (result.status === "failed") {
            writeJson(response, 422, result, {
                surface: "forge.proof-draft",
                scanOptions: FORGE_RESPONSE_SCAN_OPTIONS,
            });
            return;
        }
        app.audit.append({
            type: "forge.proof_drafted",
            severity: "info",
            message: `Proof draft created for ${body.repository}/${body.branch}.`,
            metadata: {
                repository: body.repository,
                branch: body.branch,
                proofDraftId: result.proofDraftId,
                proofCommitSha: result.proofCommitSha,
            },
        });
        writeJson(response, 200, result, {
            surface: "forge.proof-draft",
            scanOptions: FORGE_RESPONSE_SCAN_OPTIONS,
        });
    }
    catch (error) {
        writeJson(response, 500, {
            error: "forge_proof_draft_failed",
            message: error instanceof Error ? error.message : "Proof draft failed.",
        });
    }
}
async function handleForgeDraftPr(context) {
    const { app, response } = context;
    try {
        const body = await readJsonBody(context.request);
        if (!isRecord(body)) {
            writeJson(response, 400, {
                error: "invalid_request",
                message: "Expected JSON body with draft PR fields.",
            });
            return;
        }
        if (typeof body.repository !== "string" ||
            typeof body.headBranch !== "string" ||
            typeof body.baseBranch !== "string" ||
            typeof body.title !== "string") {
            writeJson(response, 400, {
                error: "invalid_request",
                message: "Required fields: repository (string), headBranch (string), baseBranch (string), title (string).",
            });
            return;
        }
        const result = await app.githubForgeClient.openDraftPullRequest({
            repository: body.repository,
            headBranch: body.headBranch,
            baseBranch: body.baseBranch,
            title: body.title,
            body: typeof body.body === "string" ? body.body : "",
        });
        if (result.status === "failed") {
            writeJson(response, 422, result, {
                surface: "forge.draft-pr",
                scanOptions: FORGE_RESPONSE_SCAN_OPTIONS,
            });
            return;
        }
        app.audit.append({
            type: "forge.draft_pr_opened",
            severity: "info",
            message: `Draft PR opened for ${body.repository}: ${body.headBranch} → ${body.baseBranch}.`,
            metadata: {
                repository: body.repository,
                headBranch: body.headBranch,
                baseBranch: body.baseBranch,
                pullRequestNumber: result.pullRequestNumber,
                pullRequestUrl: result.pullRequestUrl,
            },
        });
        writeJson(response, 200, result, {
            surface: "forge.draft-pr",
            scanOptions: FORGE_RESPONSE_SCAN_OPTIONS,
        });
    }
    catch (error) {
        writeJson(response, 500, {
            error: "forge_draft_pr_failed",
            message: error instanceof Error ? error.message : "Draft PR creation failed.",
        });
    }
}
// ---------------------------------------------------------------------------
// Error formatting
// ---------------------------------------------------------------------------
function writeForgeError(response, error) {
    if (error !== null &&
        typeof error === "object" &&
        "code" in error &&
        "message" in error) {
        const err = error;
        const statusCode = err.code === "classic_pat_rejected" ||
            err.code === "pat_scope_insufficient" ||
            err.code === "oauth_state_invalid" ||
            err.code === "oauth_redirect_uri_invalid"
            ? 422
            : err.code === "no_token_configured"
                ? 404
                : err.code === "token_already_configured"
                    ? 409
                    : err.code === "oauth_not_configured"
                        ? 409
                        : err.code === "oauth_code_exchange_failed"
                            ? 502
                            : 500;
        writeJson(response, statusCode, {
            error: err.code,
            message: err.message,
        });
        return;
    }
    writeJson(response, 500, {
        error: "forge_unknown_error",
        message: error instanceof Error
            ? error.message
            : "An unknown forge error occurred.",
    });
}
