import { maskSensitiveText } from "../util/secret-mask.js";
export const WEB_BRIDGE_SCHEMA_VERSION = 1;
export const WEB_BRIDGE_NATIVE_HOST_NAME = "io.ohmykimi.web_bridge";
export const WEB_BRIDGE_MCP_SERVER_NAME = "omk-web-bridge";
export const WEB_BRIDGE_MAX_TEXT_CHARS = 120_000;
export const WEB_BRIDGE_MAX_PAYLOAD_BYTES = 1_000_000;
const READ_METHODS = [
    "bridge.ping",
    "bridge.status",
    "bridge.capabilities",
    "browser.tabs.list",
    "browser.page.read",
    "browser.selection.read",
    "browser.dom.query",
    "browser.screenshot.capture",
    "orchestration.context.write",
];
const MUTATION_METHODS = ["browser.action.request"];
const METHODS = new Set([...READ_METHODS, ...MUTATION_METHODS]);
const SENSITIVE_KEY_RE = /(?:cookie|set-cookie|authorization|proxy-authorization|password|passwd|token|secret|credential|localstorage|sessionstorage|indexeddb|auth|privatekey|api[-_]?key)/iu;
export function getDefaultWebBridgeCapabilities() {
    return {
        schemaVersion: WEB_BRIDGE_SCHEMA_VERSION,
        readOnlyDefault: true,
        supportsTabs: true,
        supportsPageText: true,
        supportsSelection: true,
        supportsDom: true,
        supportsScreenshot: true,
        supportsMutations: false,
        mutationsRequireApproval: true,
        forbiddenData: [
            "cookies",
            "passwords",
            "localStorage",
            "sessionStorage",
            "indexedDB",
            "authorization headers",
            "raw secrets",
        ],
        methods: [...READ_METHODS, ...MUTATION_METHODS],
    };
}
export function isWebBridgeMethod(value) {
    return METHODS.has(value);
}
export function isWebBridgeMutation(method) {
    return MUTATION_METHODS.includes(method);
}
export function createWebBridgeErrorResponse(requestId, code, message, options = {}) {
    return {
        schemaVersion: WEB_BRIDGE_SCHEMA_VERSION,
        requestId,
        ok: false,
        error: {
            code,
            message: maskSensitiveText(message),
            ...(options.retryable === undefined ? {} : { retryable: options.retryable }),
        },
    };
}
export function createWebBridgeOkResponse(requestId, result) {
    return {
        schemaVersion: WEB_BRIDGE_SCHEMA_VERSION,
        requestId,
        ok: true,
        result: redactWebBridgeValue(result),
    };
}
export function validateWebBridgeRequest(input) {
    if (!input || typeof input !== "object" || Array.isArray(input)) {
        throw new Error("Web bridge request must be an object");
    }
    const value = input;
    if (value.schemaVersion !== WEB_BRIDGE_SCHEMA_VERSION) {
        throw new Error(`Unsupported web bridge schemaVersion: ${String(value.schemaVersion)}`);
    }
    if (typeof value.requestId !== "string" || value.requestId.trim() === "") {
        throw new Error("Web bridge requestId must be a non-empty string");
    }
    if (typeof value.method !== "string" || !isWebBridgeMethod(value.method)) {
        throw new Error(`Unknown web bridge method: ${String(value.method)}`);
    }
    const origin = typeof value.origin === "string" ? maskSensitiveText(value.origin) : undefined;
    const nonce = typeof value.nonce === "string" ? value.nonce : undefined;
    const approval = parseApproval(value.approval);
    return {
        schemaVersion: WEB_BRIDGE_SCHEMA_VERSION,
        requestId: value.requestId,
        method: value.method,
        ...(value.params === undefined ? {} : { params: redactWebBridgeValue(value.params) }),
        ...(origin === undefined ? {} : { origin }),
        ...(nonce === undefined ? {} : { nonce }),
        ...(approval === undefined ? {} : { approval }),
    };
}
export function assertReadOnlyWebBridgeRequest(request) {
    if (!isWebBridgeMutation(request.method))
        return;
    throw new Error("Mutating browser actions require explicit approval and are disabled by default");
}
export function sanitizeWebBridgePageSnapshot(snapshot) {
    const sanitized = redactWebBridgeValue(snapshot);
    return {
        ...sanitized,
        text: truncateBridgeText(sanitized.text),
        selectedText: truncateBridgeText(sanitized.selectedText),
        dom: truncateBridgeText(sanitized.dom),
        warnings: normalizeWarnings(sanitized.warnings),
    };
}
export function redactWebBridgeValue(value) {
    return redactUnknown(value);
}
export function truncateBridgeText(value, maxChars = WEB_BRIDGE_MAX_TEXT_CHARS) {
    if (typeof value !== "string")
        return undefined;
    const masked = maskSensitiveText(value)
        .replace(/\b(localStorage|sessionStorage|indexedDB)\s*[:=]\s*[^\n\r;]+/giu, "$1: [redacted]")
        .replace(/(<input\b[^>]*type=["']?password["']?[^>]*)(value=["'][^"']*["'])/giu, "$1value=" + JSON.stringify("[redacted]"));
    if (masked.length <= maxChars)
        return masked;
    return `${masked.slice(0, maxChars)}\n[truncated:${masked.length - maxChars}]`;
}
function redactUnknown(value) {
    if (value === null || value === undefined)
        return value;
    if (typeof value === "string")
        return truncateBridgeText(value);
    if (typeof value === "number" || typeof value === "boolean")
        return value;
    if (Array.isArray(value))
        return value.map((item) => redactUnknown(item));
    if (typeof value !== "object")
        return String(value);
    const output = {};
    for (const [key, nested] of Object.entries(value)) {
        if (SENSITIVE_KEY_RE.test(key)) {
            output[key] = "[redacted]";
            continue;
        }
        output[key] = redactUnknown(nested);
    }
    return output;
}
function parseApproval(value) {
    if (!value || typeof value !== "object" || Array.isArray(value))
        return undefined;
    const raw = value;
    if (typeof raw.token !== "string" ||
        typeof raw.expiresAt !== "string" ||
        raw.method !== "browser.action.request" ||
        typeof raw.target !== "string" ||
        typeof raw.argsDigest !== "string") {
        return undefined;
    }
    return {
        token: "[redacted]",
        expiresAt: raw.expiresAt,
        method: "browser.action.request",
        target: maskSensitiveText(raw.target),
        argsDigest: raw.argsDigest,
    };
}
function normalizeWarnings(value) {
    const warnings = Array.isArray(value) ? value : [];
    return [...new Set(warnings.map((item) => maskSensitiveText(String(item))).filter(Boolean))].slice(0, 20);
}
