import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";

import { hwlabRuntimeLaneSpecForNode, isHwlabRuntimeLane } from "./hwlab-node-lanes";
import type { NodeWebProbeScriptOptions } from "./hwlab-node/entry";
import { runManagedNodeWebProbeScript } from "./hwlab-node/web-observe-scripts";

const configPath = "/root/superapi/config/superapi.yaml";

interface SmokePage {
  id: string;
  path: string;
  readySelector: string;
  screenshotName: string;
  interaction: "none" | "github-scan" | "api-key-bindings" | "history-detail";
}

interface SmokeProfile {
  actor: "admin" | "user";
  pages: SmokePage[];
  viewport: { width: number; height: number };
  navigationTimeoutMs: number;
  settleMs: number;
  commandTimeoutSeconds: number;
  outputLimits: { failures: number; network: number; console: number };
}

interface SuperApiWebProbeConfig {
  productId: string;
  authentication: {
    mode: "ui-form-memory";
    path: string;
    usernameSelector: string;
    passwordSelector: string;
    submitSelector: string;
    authenticatedPath: string;
    authenticatedSelector: string;
  };
  userAuthentication: {
    mode: "ui-form-memory";
    path: string;
    usernameSelector: string;
    passwordSelector: string;
    submitSelector: string;
    authenticatedPath: string;
    authenticatedSelector: string;
    credentials: {
      sourceRef: string;
      usernameKey: string;
      passwordKey: string;
    };
  };
  runner: { node: string; lane: string };
  origin: { nativeBaseUrl: string; browserProxyMode: "auto" | "direct" };
  defaultSmokeProfile: string;
  smokeProfiles: Record<string, SmokeProfile>;
  auth: { username: string; passwordPath: string };
}

export interface SuperApiProductSmokeOptions {
  target: string;
  profile: string | null;
}

export function resolveSuperApiProductSmoke(options: SuperApiProductSmokeOptions): {
  config: SuperApiWebProbeConfig;
  profileName: string;
  profile: SmokeProfile;
  configRef: string;
  commandLabel: string;
  scriptOptions: NodeWebProbeScriptOptions;
} {
  const config = readSuperApiWebProbeConfig();
  if (config.productId !== "superapi") throw new Error(`${configPath} webProbe.productId 必须是 superapi`);
  if (options.target !== config.runner.node) throw new Error(`SuperAPI target 必须是 owning YAML 声明的 ${config.runner.node}`);
  if (!isHwlabRuntimeLane(config.runner.lane)) throw new Error(`SuperAPI Web smoke runner lane 未在 HWLAB YAML 中声明：${config.runner.lane}`);
  hwlabRuntimeLaneSpecForNode(config.runner.lane, config.runner.node);
  const profileName = options.profile ?? config.defaultSmokeProfile;
  const profile = config.smokeProfiles[profileName];
  if (profile === undefined) throw new Error(`未知 --profile ${profileName}；可用项：${Object.keys(config.smokeProfiles).sort().join(", ")}`);
  const configRef = `${configPath}#webProbe.smokeProfiles.${profileName}`;
  const commandLabel = `web-probe product-smoke --product superapi --target ${options.target} --profile ${profileName}`;
  const scriptText = superApiManagementSmokeScript(profileName, profile, configRef);
  const scriptOptions: NodeWebProbeScriptOptions = {
    action: "script",
    node: config.runner.node,
    lane: config.runner.lane,
    url: config.origin.nativeBaseUrl,
    originName: "native",
    originMode: "public",
    originConfigPath: `${configPath}#webProbe.origin.nativeBaseUrl`,
    browserProxyMode: config.origin.browserProxyMode,
    browserProxyModeSource: "yaml-origin",
    timeoutMs: profile.navigationTimeoutMs,
    viewport: `${profile.viewport.width}x${profile.viewport.height}`,
    commandTimeoutSeconds: profile.commandTimeoutSeconds,
    scriptText,
    commandLabel,
    suppressAdHocWarning: true,
    generatedHints: [
      `该命令由 SuperAPI owning YAML 驱动，并使用 ${profile.actor === "admin" ? "统一管理员" : "普通测试用户"}凭据、远端浏览器、内存门禁和证据恢复链。`,
      profile.actor === "admin"
        ? "smoke 只读取 Skill、API Key 和请求历史页面，不执行管理写操作。"
        : "smoke 只读取 Skill 和当前用户 API Key，并验证请求历史和 API Key 注册入口不可访问。",
    ],
    generatedPreferredCommands: { rerun: commandLabel },
    scriptSource: {
      kind: "generated",
      path: `builtin:product-smoke/superapi/${profileName}`,
      byteCount: Buffer.byteLength(scriptText),
      sha256: `sha256:${createHash("sha256").update(scriptText).digest("hex")}`,
    },
  };
  return { config, profileName, profile, configRef, commandLabel, scriptOptions };
}

export function runWebProbeSuperApiSmoke(options: SuperApiProductSmokeOptions): Record<string, unknown> {
  const resolved = resolveSuperApiProductSmoke(options);
  const credential = resolved.profile.actor === "admin"
    ? {
        username: resolved.config.auth.username,
        password: readFileSync(resolved.config.auth.passwordPath, "utf8").trim(),
        sourceRef: resolved.config.auth.passwordPath,
        keys: ["contents"],
        authentication: resolved.config.authentication,
      }
    : readUserCredential(resolved.config);
  if (credential.password === "") throw new Error("SuperAPI Web smoke 密码声明为空");
  const fingerprint = `sha256:${createHash("sha256")
    .update(`${credential.username}\0${credential.password}`)
    .digest("hex")}`;
  const laneSpec = hwlabRuntimeLaneSpecForNode(resolved.config.runner.lane, resolved.config.runner.node);
  const result = runManagedNodeWebProbeScript(
    resolved.scriptOptions,
    laneSpec,
    { username: credential.username, password: credential.password },
    {
      product: "superapi",
      target: options.target,
      source: {
        sourceRef: credential.sourceRef,
        keys: credential.keys.map((key) => ({ key, present: true })),
        fingerprint,
        valuesPrinted: false,
      },
      valuesPrinted: false,
    },
    {
      mode: "ui-form-memory",
      loginPath: credential.authentication.path,
      usernameSelector: credential.authentication.usernameSelector,
      passwordSelector: credential.authentication.passwordSelector,
      submitSelector: credential.authentication.submitSelector,
      authenticatedPath: credential.authentication.authenticatedPath,
      authenticatedSelector: credential.authentication.authenticatedSelector,
    },
  );
  return {
    ...result,
    productSmoke: {
      product: "superapi",
      target: options.target,
      profile: resolved.profileName,
      actor: resolved.profile.actor,
      configRef: resolved.configRef,
      mutation: false,
      valuesPrinted: false,
    },
    valuesPrinted: false,
  };
}

export function superApiManagementSmokeScript(
  profileName: string,
  profile: SmokeProfile,
  configRef: string,
): string {
  const config = { profileName, configRef, ...profile };
  return String.raw`const config = ${JSON.stringify(config)};

export default async function superApiManagementSmoke({ page, goto, wait, screenshot, jsonArtifact, recordStep }) {
  const failures = [];
  const network = [];
  const consoleErrors = [];
  const pages = [];
  const interactions = [];
  const screenshots = [];
  let networkCount = 0;
  let consoleCount = 0;
  const addFailure = (value) => {
    if (failures.length < config.outputLimits.failures) failures.push(String(value).replace(/\s+/gu, " ").slice(0, 300));
  };
  page.on("response", (response) => {
    const parsed = safeUrl(response.url());
    if (parsed === null || parsed.origin !== new URL(page.url()).origin) return;
    networkCount += 1;
    const item = { method: response.request().method(), path: parsed.pathname, status: response.status() };
    if (network.length < config.outputLimits.network) network.push(item);
    const expectedDenied = config.actor === "user"
      && response.status() === 403
      && parsed.pathname === "/api/admin/history";
    if (response.status() >= 400 && !expectedDenied) addFailure("HTTP " + response.status() + " " + parsed.pathname);
  });
  page.on("requestfailed", (request) => {
    const parsed = safeUrl(request.url());
    if (parsed === null) return;
    networkCount += 1;
    const failure = String(request.failure()?.errorText || "request failed").slice(0, 160);
    const browserAborted = failure.includes("net::ERR_ABORTED");
    if (network.length < config.outputLimits.network) network.push({ method: request.method(), path: parsed.pathname, status: null, failure, browserAborted });
    if (!browserAborted) addFailure(parsed.pathname + "：" + failure);
  });
  page.on("console", (message) => {
    if (message.type() !== "error") return;
    consoleCount += 1;
    const text = message.text().replace(/\s+/gu, " ").slice(0, 240);
    if (consoleErrors.length < config.outputLimits.console) consoleErrors.push({ type: "error", text });
    addFailure("console.error：" + text);
  });
  page.on("pageerror", (error) => {
    consoleCount += 1;
    const text = String(error?.message || error).replace(/\s+/gu, " ").slice(0, 240);
    if (consoleErrors.length < config.outputLimits.console) consoleErrors.push({ type: "pageerror", text });
    addFailure("pageerror：" + text);
  });

  for (const item of config.pages) {
    await goto(item.path, { selectors: ["html"], readinessTimeoutMs: config.navigationTimeoutMs, attempts: 3 });
    if (item.id === "history") {
      await page.locator('[data-page="history"][data-history-loaded="true"]')
        .waitFor({ state: "visible", timeout: config.navigationTimeoutMs });
    }
    if (config.settleMs > 0) await wait(config.settleMs);
    const dom = await page.evaluate(({ id, readySelector }) => {
      const root = document.querySelector(readySelector);
      const rect = root?.getBoundingClientRect();
      return {
        pageId: root?.getAttribute("data-page") || null,
        title: document.title,
        heading: root?.querySelector("h1")?.textContent?.trim() || null,
        activeNav: document.querySelector('nav a[aria-current="page"]')?.textContent?.trim() || null,
        rowCount: root?.querySelectorAll("tbody tr").length || 0,
        historyMessageSummaryCount: Array.from(root?.querySelectorAll("[data-history-message-summary]") ?? [])
          .filter((element) => {
            const value = element.textContent?.trim() || "";
            return value !== "" && value !== "-";
          }).length,
        historyAssistantSummaryCount: Array.from(root?.querySelectorAll("[data-history-assistant-summary]") ?? [])
          .filter((element) => {
            const value = element.textContent?.trim() || "";
            return value !== "" && value !== "-";
          }).length,
        historyToolTagCount: root?.querySelectorAll("[data-history-tool-tags] .tool-tag:not(.tool-tag-more)").length || 0,
        historyWindow: root?.querySelector("select[aria-label='时间范围']")?.value || null,
        historyCustomWindowAvailable: Array.from(root?.querySelectorAll("[data-history-filter-window] option") ?? [])
          .some((option) => option.getAttribute("value") === "custom"),
        historyUserFilter: root?.querySelector("[data-history-filter-user]") !== null,
        historyPathFilter: root?.querySelector("[data-history-filter-path]")?.tagName === "SELECT",
        historyStatusFilter: root?.querySelector("[data-history-filter-status]")?.tagName === "SELECT",
        historyAdvancedFilter: root?.querySelector("[data-history-advanced]") !== null,
        historyRequestIdAdvanced: root?.querySelector("[data-history-advanced] [data-history-filter-request-id]") !== null
          && root?.querySelector(".filter-bar > [data-history-filter-request-id]") === null,
        historyCustomRangeInputs: root?.querySelector("[data-history-custom-since]") !== null
          && root?.querySelector("[data-history-custom-until]") !== null,
        historyFilterControlsVisible: Array.from(root?.querySelectorAll(".filter-bar > .filter-field select") ?? [])
          .every((element) => {
            const controlRect = element.getBoundingClientRect();
            return controlRect.width >= 110 && controlRect.height >= 30;
          }),
        historyPagination: root?.querySelector("[data-history-pagination]") !== null,
        historyTotalPages: Number(root?.querySelector("[data-total-pages]")?.getAttribute("data-total-pages") ?? NaN),
        historyQueryDurationMs: Number(root?.querySelector("[data-query-duration-ms]")?.getAttribute("data-query-duration-ms") ?? NaN),
        historyQuerySlow: root?.querySelector("[data-query-slow]")?.getAttribute("data-query-slow") === "true",
        historyOptionsQueryDurationMs: Number(root?.querySelector("[data-options-query-duration-ms]")?.getAttribute("data-options-query-duration-ms") ?? NaN),
        historyOptionsQuerySlow: root?.querySelector("[data-options-query-slow]")?.getAttribute("data-options-query-slow") === "true",
        readySelectorPresent: root !== null,
        readySelectorVisible: Boolean(rect && rect.width > 0 && rect.height > 0),
        error: root?.querySelector(".error-bar")?.textContent?.trim() || null,
        expectedPageId: id,
      };
    }, { id: item.id, readySelector: item.readySelector });
    if (dom.pageId !== item.id || !dom.readySelectorPresent || !dom.readySelectorVisible || dom.error) {
      addFailure("页面 " + item.id + " DOM 未就绪");
    }
    if (item.id === "history" && dom.rowCount > 0 && dom.historyMessageSummaryCount === 0) {
      addFailure("请求历史没有可辨识的最后用户消息摘要");
    }
    if (item.id === "history" && dom.rowCount > 0 && dom.historyAssistantSummaryCount === 0) {
      addFailure("请求历史没有可辨识的 Assistant 回复摘要");
    }
    if (item.id === "history" && dom.rowCount > 0 && dom.historyToolTagCount === 0) {
      addFailure("请求历史没有实际工具调用 Tag");
    }
    if (item.id === "history" && (
      dom.historyWindow !== "2h"
      || !dom.historyCustomWindowAvailable
      || !dom.historyUserFilter
      || !dom.historyPathFilter
      || !dom.historyStatusFilter
      || !dom.historyAdvancedFilter
      || !dom.historyRequestIdAdvanced
      || !dom.historyCustomRangeInputs
      || !dom.historyFilterControlsVisible
      || !dom.historyPagination
      || !Number.isInteger(dom.historyTotalPages)
      || dom.historyTotalPages < 1
      || !Number.isFinite(dom.historyQueryDurationMs)
      || !Number.isFinite(dom.historyOptionsQueryDurationMs)
      || dom.historyQuerySlow
      || dom.historyOptionsQuerySlow
    )) {
      addFailure("请求历史筛选控件、默认时间窗口、分页或查询性能证据不完整");
    }
    const shot = await screenshot(item.screenshotName);
    screenshots.push(shot);
    if (item.id === "history") {
      await page.locator("[data-history-filter-window]").selectOption("custom");
      const range = await page.evaluate(() => {
        const local = (value) => {
          const shifted = new Date(value.getTime() - value.getTimezoneOffset() * 60000);
          return shifted.toISOString().slice(0, 16);
        };
        const until = new Date();
        return { since: local(new Date(until.getTime() - 30 * 60000)), until: local(until) };
      });
      await page.locator("[data-history-custom-since]").fill(range.since);
      await page.locator("[data-history-custom-until]").fill(range.until);
      await page.getByRole("button", { name: "筛选", exact: true }).click();
      await page.locator('[data-page="history"][data-history-loaded="true"]')
        .waitFor({ state: "visible", timeout: config.navigationTimeoutMs });
      const customEvidence = await page.locator("[data-page='history']").evaluate((root) => ({
        window: root.querySelector("[data-history-filter-window]")?.value || null,
        queryDurationMs: Number(root.querySelector("[data-query-duration-ms]")?.getAttribute("data-query-duration-ms") ?? NaN),
        slowQuery: root.querySelector("[data-query-slow]")?.getAttribute("data-query-slow") === "true",
        advancedOpen: root.querySelector("[data-history-advanced]")?.hasAttribute("open") === true,
      }));
      const customOk = customEvidence.window === "custom"
        && Number.isFinite(customEvidence.queryDurationMs)
        && !customEvidence.slowQuery
        && customEvidence.advancedOpen;
      if (!customOk) addFailure("请求历史任意时间段 L1 查询或性能验证失败");
      const customShot = await screenshot("superapi-history-custom-range.png");
      screenshots.push(customShot);
      interactions.push({ id: "history-custom-range", ok: customOk, ...customEvidence, screenshot: customShot });
    }
    if (item.interaction === "api-key-bindings" && dom.rowCount > 0) {
      await page.locator(item.readySelector + " tbody tr").first().click();
      const dialog = page.getByRole("dialog", { name: /Skill 绑定/u });
      await dialog.waitFor({ state: "visible", timeout: config.navigationTimeoutMs });
      const select = dialog.getByRole("combobox", { name: "选择要绑定的 Skill" });
      const bindingEvidence = await select.evaluate((element) => {
        const option = element.querySelector("option");
        const rect = element.getBoundingClientRect();
        return {
          placeholder: option?.textContent?.trim() || null,
          visible: rect.width > 0 && rect.height > 0,
          width: Math.round(rect.width),
        };
      });
      const bindingControls = await dialog.locator(".binding-row").evaluateAll((rows) =>
        rows.map((row) => {
          const inputs = Array.from(row.querySelectorAll("input[type='checkbox']"));
          const enabled = inputs.find((input) => input.getAttribute("aria-label")?.startsWith("启用 "));
          const force = inputs.find((input) => input.getAttribute("aria-label")?.startsWith("强制注入 "));
          return {
            enabledControl: enabled !== undefined,
            forceControl: force !== undefined,
            forceDisabled: force instanceof HTMLInputElement ? force.disabled : null,
          };
        }));
      const bindingControlsOk = bindingControls.every((row) =>
        row.enabledControl && row.forceControl);
      const ok = bindingEvidence.placeholder === "请选择要绑定的 Skill"
        && bindingEvidence.visible
        && bindingEvidence.width >= 240
        && bindingControlsOk;
      if (!ok) addFailure("API Key Skill 下拉框占位符或布局不完整");
      const interactionShot = await screenshot("superapi-api-key-bindings.png");
      screenshots.push(interactionShot);
      interactions.push({
        id: item.interaction,
        ok,
        ...bindingEvidence,
        bindingControls,
        bindingControlsOk,
        screenshot: interactionShot,
      });
      await dialog.getByLabel("关闭", { exact: true }).click();
      if (config.actor === "admin") {
        await page.getByRole("button", { name: "注册 API Key", exact: true }).click();
        const registerDialog = page.getByRole("dialog", { name: "注册 API Key" });
        await registerDialog.waitFor({ state: "visible", timeout: config.navigationTimeoutMs });
        const registrationEvidence = await registerDialog.evaluate((root) => {
          const alias = root.querySelector("input:not([type='password'])");
          const apiKey = root.querySelector("input[type='password']");
          return {
            aliasVisible: Boolean(alias && alias.getBoundingClientRect().width > 0),
            apiKeyVisible: Boolean(apiKey && apiKey.getBoundingClientRect().width > 0),
            providerLoginAbsent: root.querySelector("input[type='email']") === null,
          };
        });
        const registrationOk = registrationEvidence.aliasVisible
          && registrationEvidence.apiKeyVisible
          && registrationEvidence.providerLoginAbsent;
        if (!registrationOk) addFailure("管理员 API Key 手工注册入口不完整");
        const registrationShot = await screenshot("superapi-api-key-admin-register.png");
        screenshots.push(registrationShot);
        interactions.push({
          id: "api-key-admin-register",
          ok: registrationOk,
          ...registrationEvidence,
          screenshot: registrationShot,
        });
        await registerDialog.getByLabel("关闭", { exact: true }).click();
      }
    }
    if (item.interaction === "github-scan") {
      await page.getByRole("button", { name: "从 GitHub 导入" }).click();
      const dialog = page.getByRole("dialog", { name: "从 GitHub 批量导入 Skill" });
      await dialog.waitFor({ state: "visible", timeout: config.navigationTimeoutMs });
      await dialog.getByRole("button", { name: "扫描仓库" }).click();
      await dialog.locator(".github-skill-list > div").first().waitFor({ state: "visible", timeout: config.navigationTimeoutMs });
      const scanEvidence = await dialog.evaluate((root) => {
        const rows = Array.from(root.querySelectorAll(".github-skill-list > div"));
        const names = rows.map((row) => row.querySelector("strong")?.textContent?.trim() || "");
        const commit = root.querySelector(".github-scan > header code")?.textContent?.trim() || "";
        return { count: rows.length, names, commit };
      });
      const expectedNames = [
        "rtthread-create-bsp", "rtthread-create-pkg", "rtthread-env",
        "rtthread-git-commit", "rtthread-git-rebase", "rtthread-review", "rtthread-simplify",
      ];
      const ok = scanEvidence.count === 7
        && scanEvidence.commit.length === 40
        && expectedNames.every((name) => scanEvidence.names.includes(name));
      if (!ok) addFailure("GitHub Skill 扫描结果不完整");
      const interactionShot = await screenshot("superapi-github-skill-scan.png");
      screenshots.push(interactionShot);
      interactions.push({ id: item.interaction, ok, ...scanEvidence, screenshot: interactionShot });
      await dialog.locator("footer.modal-actions").getByRole("button", { name: "关闭", exact: true }).click();
    }
    if (item.interaction === "history-detail" && dom.rowCount > 0) {
      await page.locator(item.readySelector + " tbody tr").first().click();
      const dialog = page.getByRole("dialog", { name: "请求详情" });
      await dialog.waitFor({ state: "visible", timeout: config.navigationTimeoutMs });
      const readablePanels = dialog.locator(".readable-columns > section");
      const requestPanel = readablePanels.nth(0);
      const requestSections = await readablePanels.nth(0).locator(".readable-section").evaluateAll((elements) =>
        elements.map((element) => element.getAttribute("data-section-id")));
      const responseSections = await readablePanels.nth(1).locator(".readable-section").evaluateAll((elements) =>
        elements.map((element) => element.getAttribute("data-section-id")));
      const readableVisible = await dialog.locator(".readable-columns").isVisible();
      const skillNode = requestPanel.locator('[data-section-id="skills"] .readable-node').first();
      const toolNode = requestPanel.locator('[data-section-id="tools"] .readable-node').first();
      const namespaceNode = requestPanel.locator('[data-section-id="mcpNamespaces"] .readable-node').first();
      const developerNodes = await requestPanel
        .locator('[data-section-id="developerPrompts"] .readable-node > summary')
        .evaluateAll((elements) => elements.map((element) => ({
          label: element.querySelector("strong")?.textContent?.trim() || "",
          type: element.parentElement?.getAttribute("data-node-type") || "",
          summary: element.querySelector("span")?.textContent?.trim() || "",
        })));
      const skillNodes = await requestPanel
        .locator('[data-section-id="skills"] > .readable-section-body > .readable-node > summary')
        .evaluateAll((elements) => elements.map((element) => ({
          label: element.querySelector("strong")?.textContent?.trim() || "",
          source: element.parentElement?.getAttribute("data-node-type") || "",
        })));
      const skillNodeCount = await requestPanel.locator('[data-section-id="skills"] > .readable-section-body > .readable-node').count();
      const toolNodeCount = await requestPanel.locator('[data-section-id="tools"] > .readable-section-body > .readable-node').count();
      const namespaceNodeCount = await requestPanel.locator('[data-section-id="mcpNamespaces"] > .readable-section-body > .readable-node').count();
      const inputItemNodeCount = await requestPanel.locator('[data-section-id="inputItems"] > .readable-section-body > .readable-node').count();
      if (skillNodeCount > 0) await skillNode.locator("summary").first().click();
      if (toolNodeCount > 0) await toolNode.locator("summary").first().click();
      if (namespaceNodeCount > 0) await namespaceNode.locator("summary").first().click();
      const skillDetailFields = skillNodeCount > 0 ? await skillNode.locator(".readable-value").count() : 0;
      const toolDetailFields = toolNodeCount > 0 ? await toolNode.locator(".readable-value").count() : 0;
      const namespaceDetailFields = namespaceNodeCount > 0 ? await namespaceNode.locator(".readable-value").count() : 0;
      const semanticDetailsVisible = (skillNodeCount === 0 || skillDetailFields >= 2)
        && (toolNodeCount === 0 || toolDetailFields >= 2);
      await dialog.getByRole("button", { name: "Raw", exact: true }).evaluate((button) => button.click());
      await dialog.locator(".raw-columns").waitFor({ state: "visible", timeout: config.navigationTimeoutMs });
      const rawVisible = await dialog.locator(".raw-columns").isVisible();
      const rawPanelCount = await dialog.locator(".raw-columns > section > .raw-json").count();
      const readableHiddenInRaw = await dialog.locator(".readable-columns").count() === 0;
      await dialog.getByRole("button", { name: "分块", exact: true }).evaluate((button) => button.click());
      await dialog.locator(".readable-columns").waitFor({ state: "visible", timeout: config.navigationTimeoutMs });
      const readableRestored = await dialog.locator(".readable-columns").isVisible();
      const requestSemantic = requestSections.includes("userPrompts")
        && (requestSections.includes("systemPrompt") || requestSections.includes("skills"))
        && requestSections.includes("tools")
        && requestSections.includes("mcpNamespaces")
        && requestSections.includes("inputItems");
      const responseSemantic = responseSections.includes("overview")
        && responseSections.includes("outputs");
      const developerPromptsValid = developerNodes.every((node, index) =>
        node.label === "Developer #" + (index + 1)
        && node.type !== ""
        && node.summary !== "空内容");
      const skillNames = skillNodes.map((node) => node.label.toLocaleLowerCase());
      const skillsUnique = new Set(skillNames).size === skillNames.length;
      const codexNativeSkillsVisible = skillNodes.some((node) => node.source.includes("codex-native"));
      const ok = readableVisible
        && requestSemantic
        && responseSemantic
        && developerPromptsValid
        && skillsUnique
        && codexNativeSkillsVisible
        && semanticDetailsVisible
        && (namespaceNodeCount === 0 || namespaceDetailFields >= 2)
        && rawVisible
        && rawPanelCount === 2
        && readableHiddenInRaw
        && readableRestored;
      if (!ok) addFailure("请求详情分块或 Raw 模式不可用");
      const interactionShot = await screenshot("superapi-history-detail-readable.png");
      screenshots.push(interactionShot);
      interactions.push({
        id: item.interaction,
        ok,
        requestSections,
        responseSections,
        requestSemantic,
        responseSemantic,
        developerNodes,
        developerPromptsValid,
        skillNodes,
        skillsUnique,
        codexNativeSkillsVisible,
        skillNodeCount,
        toolNodeCount,
        namespaceNodeCount,
        inputItemNodeCount,
        skillDetailFields,
        toolDetailFields,
        namespaceDetailFields,
        semanticDetailsVisible,
        readableVisible,
        rawVisible,
        rawPanelCount,
        readableHiddenInRaw,
        readableRestored,
        screenshot: interactionShot,
      });
      await dialog.getByLabel("关闭", { exact: true }).click();
    }
    pages.push({ id: item.id, path: new URL(page.url()).pathname, dom, screenshot: shot });
    recordStep("读取 SuperAPI " + item.id + " 页面", { ok: dom.pageId === item.id && !dom.error, path: new URL(page.url()).pathname, rowCount: dom.rowCount });
  }
  if (config.actor === "user") {
    await goto("/api-keys", { selectors: ["[data-page='api-keys']"], readinessTimeoutMs: config.navigationTimeoutMs, attempts: 3 });
    const accessEvidence = await page.evaluate(() => {
      const navigationLabels = Array.from(document.querySelectorAll("nav a"))
        .map((item) => item.textContent?.trim() || "");
      const registerVisible = Array.from(document.querySelectorAll("button"))
        .some((item) => item.textContent?.includes("注册 API Key"));
      return {
        historyNavigationHidden: !navigationLabels.includes("请求历史"),
        registerVisible,
      };
    });
    const historyResponse = await page.request.get(new URL("/api/admin/history", page.url()).toString(), {
      headers: { accept: "application/json" },
    });
    const historyStatus = historyResponse.status();
    await goto("/history", { selectors: ["[data-page='skills']"], readinessTimeoutMs: config.navigationTimeoutMs, attempts: 3 });
    const deepLinkPath = new URL(page.url()).pathname;
    const accessOk = accessEvidence.historyNavigationHidden
      && accessEvidence.registerVisible === false
      && historyStatus === 403
      && deepLinkPath === "/skills";
    if (!accessOk) addFailure("普通用户请求历史或 API Key 注册入口仍可访问");
    interactions.push({
      id: "ordinary-user-access",
      ok: accessOk,
      ...accessEvidence,
      historyStatus,
      deepLinkPath,
    });
  }
  const mutatingRequests = network.filter((item) =>
    !["GET", "HEAD"].includes(item.method)
    && !(item.method === "POST" && item.path === "/api/admin/skills/github/scan"));
  if (mutatingRequests.length > 0) addFailure("smoke 观察到管理写请求");
  const evidence = {
    profileName: config.profileName,
    configRef: config.configRef,
    pages,
    interactions,
    network: { count: networkCount, items: network, mutatingRequests },
    console: { count: consoleCount, items: consoleErrors },
    failures,
    screenshots,
    mutation: false,
    valuesRedacted: true,
  };
  const report = await jsonArtifact("superapi-management-smoke.json", evidence);
  const ok = failures.length === 0 && pages.length === config.pages.length && mutatingRequests.length === 0;
  return {
    ok,
    status: ok ? "pass" : "blocked",
    ...evidence,
    report,
    issueEvidence: {
      ok,
      status: ok ? "pass" : "blocked",
      failedCondition: ok ? null : failures[0] || "SuperAPI 多页面 smoke 未完成",
      result: { pageCount: pages.length, pages, network: evidence.network, console: evidence.console, mutation: false },
      reportPath: report?.path || null,
      reportSha256: report?.sha256 || null,
      screenshots,
      valuesRedacted: true,
    },
  };

  function safeUrl(value) {
    try { return new URL(value); } catch { return null; }
  }
}`;
}

function readSuperApiWebProbeConfig(): SuperApiWebProbeConfig {
  const root = Bun.YAML.parse(readFileSync(configPath, "utf8")) as Record<string, unknown>;
  const webProbe = record(root.webProbe, "webProbe");
  if (webProbe.enabled !== true) throw new Error(`${configPath} webProbe.enabled=false`);
  const authentication = record(webProbe.authentication, "webProbe.authentication");
  const userAuthentication = record(webProbe.userAuthentication, "webProbe.userAuthentication");
  const userCredentials = record(userAuthentication.credentials, "webProbe.userAuthentication.credentials");
  const runner = record(webProbe.runner, "webProbe.runner");
  const origin = record(webProbe.origin, "webProbe.origin");
  const exposure = record(root.publicExposure, "publicExposure");
  const auth = record(exposure.managementAuth, "publicExposure.managementAuth");
  const password = record(auth.password, "publicExposure.managementAuth.password");
  const profiles = record(webProbe.smokeProfiles, "webProbe.smokeProfiles");
  return {
    productId: text(webProbe.productId, "webProbe.productId"),
    authentication: {
      mode: authentication.mode === "ui-form-memory"
        ? "ui-form-memory"
        : (() => { throw new Error("webProbe.authentication.mode 必须是 ui-form-memory"); })(),
      path: pathValue(authentication.path, "webProbe.authentication.path"),
      usernameSelector: text(authentication.usernameSelector, "webProbe.authentication.usernameSelector"),
      passwordSelector: text(authentication.passwordSelector, "webProbe.authentication.passwordSelector"),
      submitSelector: text(authentication.submitSelector, "webProbe.authentication.submitSelector"),
      authenticatedPath: pathValue(authentication.authenticatedPath, "webProbe.authentication.authenticatedPath"),
      authenticatedSelector: text(authentication.authenticatedSelector, "webProbe.authentication.authenticatedSelector"),
    },
    userAuthentication: {
      mode: userAuthentication.mode === "ui-form-memory"
        ? "ui-form-memory"
        : (() => { throw new Error("webProbe.userAuthentication.mode 必须是 ui-form-memory"); })(),
      path: pathValue(userAuthentication.path, "webProbe.userAuthentication.path"),
      usernameSelector: text(userAuthentication.usernameSelector, "webProbe.userAuthentication.usernameSelector"),
      passwordSelector: text(userAuthentication.passwordSelector, "webProbe.userAuthentication.passwordSelector"),
      submitSelector: text(userAuthentication.submitSelector, "webProbe.userAuthentication.submitSelector"),
      authenticatedPath: pathValue(userAuthentication.authenticatedPath, "webProbe.userAuthentication.authenticatedPath"),
      authenticatedSelector: text(userAuthentication.authenticatedSelector, "webProbe.userAuthentication.authenticatedSelector"),
      credentials: {
        sourceRef: absolutePath(userCredentials.sourceRef, "webProbe.userAuthentication.credentials.sourceRef"),
        usernameKey: text(userCredentials.usernameKey, "webProbe.userAuthentication.credentials.usernameKey"),
        passwordKey: text(userCredentials.passwordKey, "webProbe.userAuthentication.credentials.passwordKey"),
      },
    },
    runner: { node: text(runner.node, "webProbe.runner.node"), lane: text(runner.lane, "webProbe.runner.lane") },
    origin: {
      nativeBaseUrl: httpsUrl(origin.nativeBaseUrl, "webProbe.origin.nativeBaseUrl"),
      browserProxyMode: proxyMode(origin.browserProxyMode),
    },
    defaultSmokeProfile: text(webProbe.defaultSmokeProfile, "webProbe.defaultSmokeProfile"),
    smokeProfiles: Object.fromEntries(Object.entries(profiles).map(([name, value]) => [name, smokeProfile(value, name)])),
    auth: {
      username: text(auth.username, "publicExposure.managementAuth.username"),
      passwordPath: absolutePath(password.sourceRef, "publicExposure.managementAuth.password.sourceRef"),
    },
  };
}

function smokeProfile(value: unknown, name: string): SmokeProfile {
  const input = record(value, `webProbe.smokeProfiles.${name}`);
  const viewport = record(input.viewport, `webProbe.smokeProfiles.${name}.viewport`);
  const limits = record(input.outputLimits, `webProbe.smokeProfiles.${name}.outputLimits`);
  if (!Array.isArray(input.pages) || input.pages.length === 0) throw new Error(`webProbe.smokeProfiles.${name}.pages 必须是非空列表`);
  return {
    actor: input.actor === "admin" || input.actor === "user"
      ? input.actor
      : (() => { throw new Error(`webProbe.smokeProfiles.${name}.actor 必须是 admin 或 user`); })(),
    pages: input.pages.map((page, index) => {
      const item = record(page, `webProbe.smokeProfiles.${name}.pages[${index}]`);
      return {
        id: text(item.id, "page.id"),
        path: pathValue(item.path, "page.path"),
        readySelector: text(item.readySelector, "page.readySelector"),
        screenshotName: text(item.screenshotName, "page.screenshotName"),
        interaction: interaction(item.interaction),
      };
    }),
    viewport: { width: integer(viewport.width, "viewport.width"), height: integer(viewport.height, "viewport.height") },
    navigationTimeoutMs: integer(input.navigationTimeoutMs, "navigationTimeoutMs"),
    settleMs: integer(input.settleMs, "settleMs"),
    commandTimeoutSeconds: integer(input.commandTimeoutSeconds, "commandTimeoutSeconds"),
    outputLimits: {
      failures: integer(limits.failures, "outputLimits.failures"),
      network: integer(limits.network, "outputLimits.network"),
      console: integer(limits.console, "outputLimits.console"),
    },
  };
}

function readUserCredential(config: SuperApiWebProbeConfig): {
  username: string;
  password: string;
  sourceRef: string;
  keys: string[];
  authentication: SuperApiWebProbeConfig["userAuthentication"];
} {
  const source = config.userAuthentication.credentials;
  const parsed = record(
    JSON.parse(readFileSync(source.sourceRef, "utf8")) as unknown,
    "webProbe.userAuthentication.credentials source",
  );
  return {
    username: text(parsed[source.usernameKey], `credential.${source.usernameKey}`),
    password: text(parsed[source.passwordKey], `credential.${source.passwordKey}`),
    sourceRef: source.sourceRef,
    keys: [source.usernameKey, source.passwordKey],
    authentication: config.userAuthentication,
  };
}

function interaction(value: unknown): SmokePage["interaction"] {
  if (value === undefined) return "none";
  if (value === "github-scan" || value === "api-key-bindings" || value === "history-detail") return value;
  throw new Error("page.interaction 必须是 github-scan、api-key-bindings 或 history-detail");
}

function record(value: unknown, name: string): Record<string, unknown> {
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${name} 必须是对象`);
  return value as Record<string, unknown>;
}

function text(value: unknown, name: string): string {
  if (typeof value !== "string" || value.trim() === "") throw new Error(`${name} 必须是非空字符串`);
  return value.trim();
}

function integer(value: unknown, name: string): number {
  if (!Number.isSafeInteger(value) || Number(value) < 0) throw new Error(`${name} 必须是非负整数`);
  return Number(value);
}

function pathValue(value: unknown, name: string): string {
  const parsed = text(value, name);
  if (!parsed.startsWith("/")) throw new Error(`${name} 必须是绝对 URL path`);
  return parsed;
}

function absolutePath(value: unknown, name: string): string {
  const parsed = text(value, name);
  if (!parsed.startsWith("/")) throw new Error(`${name} 必须是绝对路径`);
  return parsed;
}

function httpsUrl(value: unknown, name: string): string {
  const parsed = new URL(text(value, name));
  if (parsed.protocol !== "https:") throw new Error(`${name} 必须使用 HTTPS`);
  return parsed.toString().replace(/\/$/u, "");
}

function proxyMode(value: unknown): "auto" | "direct" {
  if (value !== "auto" && value !== "direct") throw new Error("webProbe.origin.browserProxyMode 必须是 auto 或 direct");
  return value;
}
