import { isRecord } from "../utils/json.js";
import { redactPublicDiagnosticText } from "./redactionBoundary.js";
export const DEBUG_REVEAL_TTL_SECONDS_MIN = 60;
export const DEBUG_REVEAL_TTL_SECONDS_MAX = 15 * 60;
export const DEBUG_REVEAL_TTL_SECONDS_DEFAULT = 5 * 60;
export const DEBUG_REVEAL_REASON_MAX_LENGTH = 240;
export const DEBUG_REVEAL_SURFACES = [
    "runtime-diagnostics",
    "provider-auth-status",
    "permission-audit",
    "run-timeline",
];
export class DebugRevealContractError extends Error {
    code;
    statusCode;
    details;
    constructor(code, message, statusCode, details = {}) {
        super(message);
        this.code = code;
        this.statusCode = statusCode;
        this.details = details;
        this.name = "DebugRevealContractError";
    }
}
export function parseDebugRevealActivationInput(value) {
    if (!isRecord(value)) {
        throw new DebugRevealContractError("invalid_request", "Expected JSON body for debug reveal activation.", 400);
    }
    if (value.breakGlassConfirmed !== true) {
        throw new DebugRevealContractError("confirmation_required", "Debug reveal activation requires breakGlassConfirmed to be true.", 422);
    }
    const ttlSeconds = parseDebugRevealTtlSeconds(value.ttlSeconds);
    const surfaces = parseDebugRevealSurfaces(value.surfaces);
    const reasonLabel = sanitizeDebugRevealReasonLabel(value.reason);
    return {
        ttlSeconds,
        surfaces,
        ...(reasonLabel ? { reasonLabel } : {}),
    };
}
export function parseDebugRevealRevokeInput(value) {
    if (!isRecord(value))
        return {};
    const reasonLabel = sanitizeDebugRevealReasonLabel(value.reason);
    return reasonLabel ? { reasonLabel } : {};
}
export function isDebugRevealSurface(value) {
    return (typeof value === "string" &&
        DEBUG_REVEAL_SURFACES.includes(value));
}
function parseDebugRevealTtlSeconds(value) {
    if (value === undefined)
        return DEBUG_REVEAL_TTL_SECONDS_DEFAULT;
    if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
        throw new DebugRevealContractError("invalid_ttl", "Debug reveal ttlSeconds must be a positive number.", 422, {
            minSeconds: DEBUG_REVEAL_TTL_SECONDS_MIN,
            maxSeconds: DEBUG_REVEAL_TTL_SECONDS_MAX,
        });
    }
    return Math.max(DEBUG_REVEAL_TTL_SECONDS_MIN, Math.min(DEBUG_REVEAL_TTL_SECONDS_MAX, Math.trunc(value)));
}
function parseDebugRevealSurfaces(value) {
    if (value === undefined)
        return ["runtime-diagnostics"];
    if (!Array.isArray(value)) {
        throw new DebugRevealContractError("invalid_surface", "Debug reveal surfaces must be an array of supported safe surface labels.", 422, { allowedSurfaces: [...DEBUG_REVEAL_SURFACES] });
    }
    const surfaces = [];
    for (const item of value) {
        if (!isDebugRevealSurface(item)) {
            throw new DebugRevealContractError("invalid_surface", "Debug reveal surfaces must use supported safe surface labels.", 422, { allowedSurfaces: [...DEBUG_REVEAL_SURFACES] });
        }
        if (!surfaces.includes(item))
            surfaces.push(item);
    }
    return surfaces.length > 0 ? surfaces : ["runtime-diagnostics"];
}
function sanitizeDebugRevealReasonLabel(value) {
    if (value === undefined || value === null)
        return undefined;
    if (typeof value !== "string") {
        throw new DebugRevealContractError("invalid_reason", "Debug reveal reason must be a string when provided.", 422);
    }
    const trimmed = value.trim();
    if (trimmed.length === 0)
        return undefined;
    if (trimmed.length > DEBUG_REVEAL_REASON_MAX_LENGTH) {
        throw new DebugRevealContractError("reason_too_long", "Debug reveal reason exceeds the 240 character limit.", 422);
    }
    const redacted = redactPublicDiagnosticText(trimmed)
        .replace(/[\u0000-\u001F\u007F]/gu, " ")
        .replace(/\s+/gu, " ")
        .trim();
    if (redacted.length === 0)
        return undefined;
    if (containsSensitiveReasonSignal(trimmed, redacted)) {
        return "redacted-sensitive-reason";
    }
    return `operator-note:${redacted.slice(0, 96)}`;
}
function containsSensitiveReasonSignal(raw, redacted) {
    if (raw !== redacted)
        return true;
    return /\b(?:pnpm|npm|node|bash|sh|zsh|git|gh|curl|ssh|scp|rm|sudo|env)\b/u.test(raw);
}
