import { createHmac, randomBytes } from "node:crypto";
import { hasExternalServiceMutationCommand } from "../../permissions/PermissionPolicyEngine.js";
import { redactSecretText } from "../../utils/redactSecrets.js";
const commandMatchSecret = randomBytes(32);
export function extractOpenCodePermissionRequest(event) {
    if (!isPermissionRequestEvent(event))
        return undefined;
    const runtimeSessionId = readStringAt(event, [
        ["sessionID"],
        ["sessionId"],
        ["session", "id"],
    ]);
    const providerPermissionId = readStringAt(event, [
        ["permissionID"],
        ["permissionId"],
        ["permission", "id"],
        ["permission", "permissionID"],
        ["permission", "permissionId"],
    ]);
    const providerRequestId = readStringAt(event, [
        ["requestID"],
        ["requestId"],
        ["permission", "requestID"],
        ["permission", "requestId"],
        ["request", "id"],
    ]);
    const toolRecord = readRecordAt(event, [
        ["toolCall"],
        ["tool"],
        ["call"],
        ["permission", "toolCall"],
        ["permission", "tool"],
    ]);
    const toolName = readStringAt(toolRecord ?? {}, [["name"], ["tool"], ["id"]]);
    if (!runtimeSessionId ||
        (!providerPermissionId && !providerRequestId) ||
        !toolName) {
        return undefined;
    }
    const rawCommand = readRawStringAt(toolRecord ?? {}, [
        ["command"],
        ["input", "command"],
    ]);
    const command = redactPreview(rawCommand);
    const preview = redactPreview(readStringAt(toolRecord ?? {}, [
        ["inputPreview"],
        ["preview"],
        ["description"],
        ["input", "preview"],
        ["input", "command"],
        ["command"],
    ]) ?? toolName) ?? toolName;
    const affectedPaths = readStringArrayAt(toolRecord ?? {}, [
        ["affectedPaths"],
        ["paths"],
        ["files"],
    ]);
    const toolCallId = readStringAt(toolRecord ?? {}, [["id"], ["callID"], ["callId"]]) ??
        providerPermissionId ??
        providerRequestId;
    if (!toolCallId)
        return undefined;
    const toolCall = {
        id: toolCallId,
        name: toolName,
        title: titleForTool(toolName),
        risk: riskForTool(toolName, rawCommand ?? command),
        inputPreview: preview,
        ...(affectedPaths ? { affectedPaths } : {}),
        ...(command ? { command } : {}),
        ...(rawCommand ? { commandMatchKey: commandMatchKeyFor(rawCommand) } : {}),
    };
    return {
        runtimeSessionId,
        ...(providerRequestId ? { providerRequestId } : {}),
        ...(providerPermissionId ? { providerPermissionId } : {}),
        toolCall,
    };
}
function isPermissionRequestEvent(event) {
    const type = typeof event.type === "string" ? event.type.toLowerCase() : "";
    return (type.includes("permission") &&
        (type.includes("ask") ||
            type.includes("request") ||
            type.includes("pending")) &&
        !type.includes("reply") &&
        !type.includes("replied"));
}
function riskForTool(toolName, command) {
    const lower = toolName.toLowerCase();
    const commandLower = command?.toLowerCase() ?? "";
    if (lower.includes("github") ||
        lower.includes("deploy") ||
        lower.includes("publish") ||
        hasExternalServiceMutationCommand(commandLower))
        return "external";
    if (lower.includes("bash") ||
        lower.includes("shell") ||
        lower.includes("exec"))
        return "shell";
    if (lower.includes("edit") ||
        lower.includes("write") ||
        lower.includes("patch"))
        return "write";
    if (lower.includes("web") ||
        lower.includes("fetch") ||
        lower.includes("search") ||
        lower.includes("network"))
        return "network";
    return "read";
}
function titleForTool(toolName) {
    return (toolName
        .split(/[._:-]+/g)
        .filter(Boolean)
        .map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`)
        .join(" ") || "Tool call");
}
function commandMatchKeyFor(command) {
    return createHmac("sha256", commandMatchSecret)
        .update(normalizeCommandForMatch(command))
        .digest("base64url");
}
function normalizeCommandForMatch(command) {
    return command.trim().replace(/\s+/g, " ");
}
function redactPreview(value) {
    if (!value)
        return undefined;
    return redactSecretText(value).slice(0, 500);
}
function readStringArrayAt(value, paths) {
    for (const path of paths) {
        const entry = readAt(value, path);
        if (!Array.isArray(entry))
            continue;
        const strings = entry
            .filter((item) => typeof item === "string")
            .map((item) => item.slice(0, 500))
            .slice(0, 20);
        if (strings.length > 0)
            return strings;
    }
    return undefined;
}
function readRawStringAt(value, paths) {
    for (const path of paths) {
        const entry = readAt(value, path);
        if (typeof entry === "string" && entry.length > 0)
            return entry;
    }
    return undefined;
}
function readStringAt(value, paths) {
    for (const path of paths) {
        const entry = readAt(value, path);
        if (typeof entry === "string" && entry.length > 0)
            return entry.slice(0, 500);
    }
    return undefined;
}
function readRecordAt(value, paths) {
    for (const path of paths) {
        const entry = readAt(value, path);
        if (isRecord(entry))
            return entry;
    }
    return undefined;
}
function readAt(value, path) {
    let current = value;
    for (const segment of path) {
        if (!isRecord(current))
            return undefined;
        current = current[segment];
    }
    return current;
}
function isRecord(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}
