#!/usr/bin/env tsx
/**
 * 必做任务 1：真人完整链路录证（找 commit 真正发生瞬间）
 *
 * 产出（.rpa/human-commit-baseline/{runId}/）:
 *   baseline.har          — 全量 HAR
 *   trace.zip             — Playwright Trace（含 CDP / 截图 / DOM）
 *   video/*.webm          — 屏幕录像
 *   console.ndjson        — 浏览器 Console
 *   network.ndjson        — axel/location + axel/quote + Google Places 网络
 *   checkpoints.json      — 人工检查点时间轴
 *   commit-moments.json   — 自动识别的 GET place/{id} 200 时刻
 *   summary.json          — 录证摘要
 *
 * 用法:
 *   npm run record:human-commit-baseline
 *   $env:HUMAN_RECORD_FILE_TRIGGER="1"; npm run record:human-commit-baseline
 *
 * 检查点触发文件（非 TTY 推荐）:
 *   .rpa/human-commit-baseline/{runId}/triggers/after-pickup
 *   .rpa/human-commit-baseline/{runId}/triggers/after-delivery
 *   .rpa/human-commit-baseline/{runId}/triggers/after-quote
 *   .rpa/human-commit-baseline/{runId}/triggers/finish
 */
import "dotenv/config";
import fs from "node:fs";
import { randomUUID } from "node:crypto";
import { chromium } from "playwright";
import { getPrimaryMothershipQuoteUrl } from "@/lib/rpa/env";
import {
  createSharedReadline,
  waitForHumanCheckpoint,
} from "./human-commit-baseline/checkpoint";
import { ConsoleLogger } from "./human-commit-baseline/console-logger";
import { NetworkLogger } from "./human-commit-baseline/network-logger";
import { createRunPaths } from "./human-commit-baseline/paths";

const PROMPTS = {
  afterPickup:
    "提货地址：键入联想 → 点选 → 确认 widget 已锁定（chip/摘要可见）。完成后触发检查点。",
  afterDelivery:
    "派送地址：键入联想 → 点选 → 确认双地址均在 widget。完成后触发检查点。",
  afterQuote:
    "货物已填、已点 Get Quote、报价档位已显示。完成后触发检查点。",
  finish: "确认录证结束，将写入 HAR / Trace / 摘要。",
};

function writeStatus(
  statusPath: string,
  payload: Record<string, unknown>,
): void {
  fs.writeFileSync(
    statusPath,
    `${JSON.stringify({ updatedAt: new Date().toISOString(), ...payload }, null, 2)}\n`,
    "utf8",
  );
}

function printBanner(runId: string, paths: ReturnType<typeof createRunPaths>): void {
  const tty = process.stdin.isTTY ? "是" : "否";
  const mode =
    process.env.HUMAN_RECORD_FILE_TRIGGER === "1" || !process.stdin.isTTY
      ? "文件触发"
      : "Enter 键";

  console.log(`
╔══════════════════════════════════════════════════════════════╗
║  真人完整链路录证 — 定位 place commit 瞬间                    ║
╚══════════════════════════════════════════════════════════════╝

runId: ${runId}
产出目录: ${paths.root}
stdin TTY: ${tty} → 检查点: ${mode}

冻结说明（录证期间禁止新增）:
  - fix-place/*
  - place-commit-strategy / selection-trace
  - 新回退策略 / 新点击方式

操作顺序:
  1. 浏览器打开 MotherShip 报价页
  2. 人工完成提货地址点选 → 检查点「提货 commit」
  3. 人工完成派送地址点选 → 检查点「派送 commit」
  4. 填货 + 提交 + 看到报价 → 检查点「报价完成」
  5. 结束录证

自动标记: 每次 GET .../axel/location/place/{id} 200 会打印 ★ 并写入 commit-moments.json

查看 Trace: npx playwright show-trace "${paths.trace}"
`);
}

async function main(): Promise<void> {
  const runId = randomUUID().slice(0, 8);
  const paths = createRunPaths(runId);
  fs.mkdirSync(paths.triggerDir, { recursive: true });
  fs.mkdirSync(paths.videoDir, { recursive: true });

  const entryUrl = getPrimaryMothershipQuoteUrl();
  writeStatus(paths.status, { phase: "started", runId, entryUrl });
  printBanner(runId, paths);

  const channel = process.env.RPA_BROWSER_CHANNEL?.trim() || "chrome";
  const browser = await chromium.launch({
    headless: false,
    channel: channel as "chrome",
    slowMo: Number(process.env.RPA_SLOW_MO_MS ?? 0) || undefined,
  });

  const context = await browser.newContext({
    locale: process.env.RPA_BROWSER_LOCALE?.trim() || undefined,
    viewport: { width: 1400, height: 900 },
    recordHar: { path: paths.har, mode: "full", content: "embed" },
    recordVideo: { dir: paths.videoDir, size: { width: 1400, height: 900 } },
  });

  await context.tracing.start({
    screenshots: true,
    snapshots: true,
    sources: true,
    title: `human-commit-baseline-${runId}`,
  });

  const page = await context.newPage();
  page.setDefaultTimeout(120_000);

  const networkLogger = new NetworkLogger(paths.networkLog);
  const consoleLogger = new ConsoleLogger(paths.consoleLog);
  networkLogger.attach(page);
  consoleLogger.attach(page);

  const checkpoints: Array<{
    id: string;
    label: string;
    ts: number;
    iso: string;
    pageUrl: string;
    placeCommitsSoFar: number;
  }> = [];

  const rl =
    process.stdin.isTTY && process.env.HUMAN_RECORD_FILE_TRIGGER !== "1"
      ? createSharedReadline()
      : null;

  function markCheckpoint(id: string, label: string): void {
    networkLogger.setCheckpoint(id);
    consoleLogger.setPhase(id);
    checkpoints.push({
      id,
      label,
      ts: Date.now(),
      iso: new Date().toISOString(),
      pageUrl: page.url(),
      placeCommitsSoFar: networkLogger.getPlaceCommits().length,
    });
    writeStatus(paths.status, {
      phase: id,
      runId,
      checkpoints: checkpoints.length,
      placeCommits: networkLogger.getPlaceCommits().length,
    });
  }

  console.log(`[录证] 导航 ${entryUrl}`);
  await page.goto(entryUrl, { waitUntil: "domcontentloaded", timeout: 90_000 });
  markCheckpoint("page_loaded", "页面已加载");

  await waitForHumanCheckpoint({
    label: "检查点 1 — 提货 commit 后",
    message: PROMPTS.afterPickup,
    triggerPath: paths.triggerAfterPickup,
    rl,
  });
  markCheckpoint("after_pickup", "提货地址已点选锁定");

  await waitForHumanCheckpoint({
    label: "检查点 2 — 派送 commit 后",
    message: PROMPTS.afterDelivery,
    triggerPath: paths.triggerAfterDelivery,
    rl,
  });
  markCheckpoint("after_delivery", "派送地址已点选锁定");

  await waitForHumanCheckpoint({
    label: "检查点 3 — 报价已显示",
    message: PROMPTS.afterQuote,
    triggerPath: paths.triggerAfterQuote,
    rl,
  });
  markCheckpoint("after_quote", "报价档位已显示");

  await waitForHumanCheckpoint({
    label: "检查点 4 — 结束录证",
    message: PROMPTS.finish,
    triggerPath: paths.triggerFinish,
    rl,
  });
  markCheckpoint("finish", "录证结束");

  if (rl) {
    rl.close();
  }

  networkLogger.flush();
  const placeCommits = networkLogger.getPlaceCommits();

  await context.tracing.stop({ path: paths.trace });
  await page.close();
  await context.close();
  await browser.close();

  fs.writeFileSync(
    paths.checkpoints,
    `${JSON.stringify({ runId, checkpoints }, null, 2)}\n`,
    "utf8",
  );
  fs.writeFileSync(
    paths.commitMoments,
    `${JSON.stringify({ runId, placeCommits }, null, 2)}\n`,
    "utf8",
  );

  const summary = {
    runId,
    finishedAt: new Date().toISOString(),
    entryUrl,
    artifacts: {
      har: fs.existsSync(paths.har) ? paths.har : null,
      trace: fs.existsSync(paths.trace) ? paths.trace : null,
      videoDir: paths.videoDir,
      consoleLog: paths.consoleLog,
      networkLog: paths.networkLog,
      checkpoints: paths.checkpoints,
      commitMoments: paths.commitMoments,
    },
    placeCommitCount: placeCommits.length,
    placeCommits,
    checkpoints,
    analysisHints: [
      "对比 commit-moments 与 checkpoints：place 200 应落在 after_pickup / after_delivery 检查点之前或紧邻",
      "trace.zip: npx playwright show-trace 查看点击瞬间 DOM + Network",
      "baseline.har: 在 DevTools HAR 视图中过滤 axel/location/place",
      "若人工操作有 place 200 而 RPA 无 → 问题在自动化事件序列，非 API 层",
    ],
  };
  fs.writeFileSync(paths.summary, `${JSON.stringify(summary, null, 2)}\n`, "utf8");
  writeStatus(paths.status, { phase: "completed", runId, summary: paths.summary });

  console.log(`
[录证] 完成 runId=${runId}
  HAR:     ${paths.har}
  Trace:   ${paths.trace}
  Video:   ${paths.videoDir}
  Network: ${paths.networkLog}
  Console: ${paths.consoleLog}
  Commits: ${placeCommits.length} 次 GET place 200
  摘要:    ${paths.summary}

下一步:
  npx playwright show-trace "${paths.trace}"
`);
}

main().catch((error) => {
  console.error(
    "[录证] 失败:",
    error instanceof Error ? error.message : error,
  );
  process.exit(1);
});
