import { createHash } from "node:crypto";

import { hwlabRuntimeLaneSpecForNode, isHwlabRuntimeLane } from "./hwlab-node-lanes";
import { readSub2RankConfig, resolveSub2RankTarget, sub2RankConfigLabel, type Sub2RankWebProbeSmokeProfile } from "./platform-infra-sub2rank-config";
import { readDeclaredSecretValues } from "./secrets";
import type { NodeWebProbeScriptOptions } from "./hwlab-node/entry";
import { runManagedNodeWebProbeScript } from "./hwlab-node/web-observe-scripts";

export interface ApiStateProductSmokeOptions {
  readonly target: string;
  readonly profile: string | null;
  readonly runtime: string | null;
}

export function runWebProbeApiStateSmoke(options: ApiStateProductSmokeOptions): Record<string, unknown> {
  const config = readSub2RankConfig();
  const target = resolveSub2RankTarget(config, options.target);
  const webProbe = config.webProbe;
  if (!webProbe.enabled) throw new Error(`${sub2RankConfigLabel}.webProbe.enabled=false，禁止执行 ApiState Web smoke`);
  const profileName = options.profile ?? webProbe.defaultSmokeProfile;
  const runtime = options.runtime ?? "deployed";
  if (runtime !== "deployed" && runtime !== "native") throw new Error("ApiState product-smoke --runtime 只支持 deployed|native");
  const profile = webProbe.smokeProfiles[profileName];
  if (profile === undefined) throw new Error(`未知 --profile ${profileName}；可用项：${Object.keys(webProbe.smokeProfiles).sort().join(", ")}`);
  if (!isHwlabRuntimeLane(webProbe.runner.lane)) throw new Error(`ApiState Web smoke runner lane 未在 HWLAB YAML 中声明：${webProbe.runner.lane}`);
  const laneSpec = hwlabRuntimeLaneSpecForNode(webProbe.runner.lane, webProbe.runner.node);
  const passwordKey = webProbe.authentication.passwordTargetKey;
  const declared = readDeclaredSecretValues({
    configPath: config.runtime.secret.configRef,
    targetId: config.runtime.secret.targetId,
    secretName: config.runtime.secret.declarationId,
    targetKeys: [passwordKey],
  });
  const password = declared.values[passwordKey];
  if (password === undefined) throw new Error("ApiState Web smoke 密码声明不完整");
  const configRef = `${sub2RankConfigLabel}#webProbe.smokeProfiles.${profileName}`;
  const commandLabel = `web-probe product-smoke --product apistate --target ${target.id} --profile ${profileName} --runtime ${runtime}`;
  const scriptText = apiStateOperationsSmokeScript(profileName, profile, configRef);
  const url = runtime === "native" ? webProbe.origin.nativeBaseUrl : target.publicExposure.publicBaseUrl;
  const scriptOptions: NodeWebProbeScriptOptions = {
    action: "script",
    node: webProbe.runner.node,
    lane: webProbe.runner.lane,
    url,
    originName: runtime === "native" ? "custom" : "public",
    originMode: runtime === "native" ? "custom-url" : "public",
    originConfigPath: runtime === "native"
      ? `${sub2RankConfigLabel}#webProbe.origin.nativeBaseUrl`
      : `${sub2RankConfigLabel}#targets.${target.id}.publicExposure.publicBaseUrl`,
    browserProxyMode: webProbe.origin.browserProxyMode,
    browserProxyModeSource: "yaml-origin",
    timeoutMs: profile.navigationTimeoutMs,
    viewport: `${profile.viewport.width}x${profile.viewport.height}`,
    commandTimeoutSeconds: profile.commandTimeoutSeconds,
    scriptText,
    commandLabel,
    suppressAdHocWarning: true,
    generatedHints: [
      "该命令由 Sub2Rank owning YAML 驱动，并复用受控认证、远端浏览器、内存门禁和证据恢复链。",
      "smoke 只读取评分、用量和抽奖页面，不点击刷新或抽奖按钮。",
    ],
    generatedPreferredCommands: { rerun: commandLabel },
    scriptSource: {
      kind: "generated",
      path: `builtin:product-smoke/apistate/${runtime}/${profileName}`,
      byteCount: Buffer.byteLength(scriptText),
      sha256: `sha256:${createHash("sha256").update(scriptText).digest("hex")}`,
    },
  };
  const result = runManagedNodeWebProbeScript(
    scriptOptions,
    laneSpec,
    { username: config.application.webAuthUsername, password },
    { product: "apistate", target: target.id, source: declared.summary, valuesPrinted: false },
    {
      mode: "json-post-session-cookie",
      loginPath: webProbe.authentication.path,
      usernameField: webProbe.authentication.usernameField,
      passwordField: webProbe.authentication.passwordField,
      cookieNames: webProbe.authentication.cookieNames,
    },
  );
  return {
    ...result,
    productSmoke: { product: "apistate", target: target.id, profile: profileName, runtime, configRef, mutation: false, valuesPrinted: false },
    valuesPrinted: false,
  };
}

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

export default async function apiStateOperationsSmoke({ page, goto, wait, screenshot, jsonArtifact, recordStep }) {
  const failures = [];
  const network = [];
  const pages = [];
  const screenshots = [];
  let networkCount = 0;
  const addFailure = (value) => {
    if (failures.length < config.outputLimits.failures) failures.push(String(value).replace(/\s+/gu, " ").slice(0, 300));
  };
  const addNetwork = (value) => {
    networkCount += 1;
    if (network.length < config.outputLimits.network) network.push(value);
  };
  page.on("response", (response) => {
    const parsed = safeUrl(response.url());
    if (parsed === null || !parsed.pathname.startsWith("/api/")) return;
    addNetwork({ method: response.request().method(), path: parsed.pathname, status: response.status() });
  });
  page.on("requestfailed", (request) => {
    const parsed = safeUrl(request.url());
    if (parsed !== null) addNetwork({ method: request.method(), path: parsed.pathname, status: null, failure: String(request.failure()?.errorText || "request failed").slice(0, 160) });
  });

  for (const item of config.pages) {
    try {
      await goto(item.path, { selectors: [item.readySelector], readinessTimeoutMs: config.navigationTimeoutMs, attempts: 3 });
      await page.waitForFunction((pageId) => {
        const fatal = document.querySelector(".fatal-state")?.textContent?.trim();
        if (fatal) return false;
        if (pageId === "scores") return document.querySelector("#score-state")?.getAttribute("data-state") !== null;
        if (pageId === "ranking") return document.querySelector("#ranking-range")?.textContent?.trim() !== "读取中";
        if (pageId === "lottery") return document.querySelector("#lottery-mode")?.textContent?.trim() !== "读取中";
        return false;
      }, item.id, { timeout: config.navigationTimeoutMs });
      if (config.settleMs > 0) await wait(config.settleMs);
      const dom = await page.evaluate((pageId) => ({
        pageId: document.body.dataset.page || null,
        title: document.title,
        heading: document.querySelector("h1")?.textContent?.trim() || null,
        activeNav: document.querySelector('nav a[aria-current="page"]')?.textContent?.trim() || null,
        rowCount: pageId === "scores"
          ? document.querySelectorAll("#score-body tr").length
          : pageId === "ranking"
            ? document.querySelectorAll("#ranking-body tr").length
            : document.querySelectorAll("#record-list li").length,
        fatal: document.querySelector(".fatal-state")?.textContent?.trim() || null,
        drawButtonDisabled: pageId === "lottery" ? document.querySelector("#draw-button")?.disabled === true : null,
      }), item.id);
      if (dom.pageId !== item.id || dom.fatal) throw new Error("页面 " + item.id + " DOM 未就绪");
      const shot = await screenshot(item.screenshotName);
      screenshots.push(shot);
      pages.push({ id: item.id, path: new URL(page.url()).pathname, dom, screenshot: shot });
      recordStep("读取 " + item.id + " 页面", { ok: true, path: new URL(page.url()).pathname, rowCount: dom.rowCount });
    } catch (error) {
      addFailure(item.id + ": " + (error instanceof Error ? error.message : String(error)));
      recordStep("读取 " + item.id + " 页面失败", { ok: false, failure: failures[failures.length - 1] || "unknown" });
      break;
    }
  }

  const mutatingRequests = network.filter((item) => item.method !== "GET" && item.path !== "/api/login");
  if (mutatingRequests.length > 0) addFailure("smoke 观察到非登录写请求");
  const evidence = {
    profileName: config.profileName,
    configRef: config.configRef,
    pageCount: pages.length,
    pages,
    network: { count: networkCount, items: network, mutatingRequests },
    failures,
    mutation: false,
    valuesRedacted: true,
  };
  const report = await jsonArtifact("apistate-operations-smoke.json", evidence).catch((error) => {
    addFailure("报告写入失败：" + (error instanceof Error ? error.message : String(error)));
    return null;
  });
  const ok = failures.length === 0 && pages.length === config.pages.length && mutatingRequests.length === 0;
  return {
    ok,
    status: ok ? "pass" : "blocked",
    ...evidence,
    report,
    screenshots,
    issueEvidence: {
      ok,
      status: ok ? "pass" : "blocked",
      failedCondition: ok ? null : failures[0] || "ApiState 多页面 smoke 未完成",
      result: { pageCount: pages.length, pages, network: evidence.network, mutation: false },
      reportPath: report?.path || null,
      reportSha256: report?.sha256 || null,
      screenshots,
      valuesRedacted: true,
    },
    valuesRedacted: true,
  };

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