/**
 * Priority1 纯人工录制 — 仅打开页面 + 记录操作，不自动填表
 */

import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { chromium, type Page } from "playwright";
import { PRIORITY1_FORM_META } from "@/workers/rpa/priority1/form-logic-map";
import {
  buildAccessorialLabelPlaywright,
  drainHumanAssistEvents,
  formatHumanEventsAsPlaywright,
  type HumanAccessorialRecording,
  type HumanAssistEvent,
} from "@/workers/rpa/priority1/human-assist-recorder";
import { extractPriority1VisibleQuotes } from "@/workers/rpa/priority1/quote-extract";

const BROWSER_RECORDER_JS = join(
  dirname(fileURLToPath(import.meta.url)),
  "human-assist-recorder.browser.js",
);

export type ManualRecordingOptions = {
  pathId: string;
  title: string;
  outDir?: string;
  entryUrl?: string;
  checklist?: readonly string[];
  maxWaitMs?: number;
};

function loadRecorderScript(): string {
  return readFileSync(BROWSER_RECORDER_JS, "utf8");
}

function attachQuoteNetworkCapture(page: Page, bodies: string[]): void {
  page.on("response", async (res) => {
    const url = res.url();
    if (!PRIORITY1_FORM_META.quoteApiPattern.test(url)) return;
    try {
      const json = await res.json();
      bodies.push(JSON.stringify(json));
    } catch {
      // ignore
    }
  });
}

function saveRecording(
  outDir: string,
  opts: ManualRecordingOptions,
  events: HumanAssistEvent[],
  quoteDetected: boolean,
): { jsonPath: string; jsPath: string } {
  const ts = new Date().toISOString().replace(/[:.]/g, "").slice(0, 15);
  const recordingsDir = join(process.cwd(), ".rpa", "recordings");
  mkdirSync(recordingsDir, { recursive: true });
  const base = `priority1-${opts.pathId}-manual-${ts}`;
  const checklist = opts.checklist ?? [];
  const usedChecklistFallback =
    events.length === 0 && quoteDetected && checklist.length > 0;
  const playwrightScript = usedChecklistFallback
    ? buildAccessorialLabelPlaywright(checklist)
    : formatHumanEventsAsPlaywright(events);
  const payload: HumanAccessorialRecording & {
    quoteDetected: boolean;
    mode: string;
    humanVerified?: boolean;
    note?: string;
  } = {
    pathId: opts.pathId,
    title: opts.title,
    recordedAt: new Date().toISOString(),
    expectedAccessorials: checklist,
    events,
    playwrightScript,
    quoteDetected,
    mode: "manual-only",
    ...(usedChecklistFallback
      ? {
          humanVerified: true,
          note: "人工实机勾选并出报价；DOM 事件未捕获，回放脚本由 11 项清单生成",
        }
      : {}),
  };
  const jsonPath = join(recordingsDir, `${base}.json`);
  const jsPath = join(recordingsDir, `${base}.js`);
  writeFileSync(jsonPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
  writeFileSync(jsPath, `${playwrightScript}\n`, "utf8");
  writeFileSync(join(outDir, "human-recording.json"), `${JSON.stringify(payload, null, 2)}\n`, "utf8");
  writeFileSync(join(outDir, "human-recording.js"), `${playwrightScript}\n`, "utf8");
  return { jsonPath, jsPath };
}

function printChecklist(opts: ManualRecordingOptions): void {
  console.log("\n════════ 纯人工操作 — 脚本不自动填表 ════════");
  console.log(`[manual] 路径: ${opts.pathId} — ${opts.title}`);
  console.log("[manual] 请在浏览器中从头完成全部字段，直至出现报价。");
  if (opts.checklist?.length) {
    console.log("\n[manual] 参考清单（P08 附加服务 11 项）：");
    for (let i = 0; i < opts.checklist.length; i += 1) {
      console.log(`  ${i + 1}. ${opts.checklist[i]}`);
    }
  }
  console.log("\n[manual] 操作会被持续记录。");
  console.log("[manual] 出现报价后可继续核对；按 Ctrl+C 结束并保存录制。\n");
}

export async function runPriority1ManualRecordingSession(
  opts: ManualRecordingOptions,
): Promise<void> {
  const outDir =
    opts.outDir ?? join(process.cwd(), ".rpa", "recordings", `${opts.pathId}-manual-session`);
  mkdirSync(outDir, { recursive: true });

  const entryUrl = opts.entryUrl ?? PRIORITY1_FORM_META.entryUrl;
  const script = loadRecorderScript();
  const networkBodies: string[] = [];

  const browser = await chromium.launch({
    headless: false,
    channel: (process.env.RPA_BROWSER_CHANNEL?.trim() || "chrome") as "chrome",
  });
  const context = await browser.newContext({
    viewport: { width: 1400, height: 900 },
    locale: "en-US",
  });
  const page = await context.newPage();
  page.setDefaultTimeout(120_000);

  attachQuoteNetworkCapture(page, networkBodies);
  await page.addInitScript({ content: script });

  console.log(`[manual] 打开: ${entryUrl}`);
  await page.goto(entryUrl, { waitUntil: "domcontentloaded" });
  await page.waitForSelector("#container-mh", { timeout: 60_000 }).catch(() => undefined);
  await page.evaluate(script).catch(() => undefined);

  printChecklist(opts);

  const maxWait = opts.maxWaitMs ?? 2 * 60 * 60 * 1000;
  const pollMs = 2_000;
  const started = Date.now();
  let lastEventCount = 0;
  let quoteDetected = false;
  let accumulatedEvents: HumanAssistEvent[] = [];

  let shuttingDown = false;

  const finish = async (reason: string) => {
    if (shuttingDown) return;
    shuttingDown = true;
    const drained = await drainHumanAssistEvents(page).catch(() => [] as HumanAssistEvent[]);
    const events =
      drained.length >= accumulatedEvents.length ? drained : accumulatedEvents;
    const { jsonPath, jsPath } = saveRecording(outDir, opts, events, quoteDetected);
    console.log(`\n[manual] ${reason}`);
    console.log(`[manual] 记录事件 ${events.length} 条`);
    console.log(`[manual] JSON: ${jsonPath}`);
    console.log(`[manual] JS:   ${jsPath}`);
    await context.close().catch(() => undefined);
    await browser.close().catch(() => undefined);
    process.exit(0);
  };

  process.once("SIGINT", () => {
    void finish("用户 Ctrl+C — 已保存录制");
  });
  process.once("SIGTERM", () => {
    void finish("收到终止信号 — 已保存录制");
  });

  while (Date.now() - started < maxWait && !shuttingDown) {
    if (page.isClosed()) {
      await finish("浏览器已关闭 — 已保存已记录操作");
      return;
    }

    const drained = await drainHumanAssistEvents(page).catch(() => [] as HumanAssistEvent[]);
    if (drained.length > accumulatedEvents.length) {
      accumulatedEvents = drained;
    }
    if (accumulatedEvents.length > lastEventCount) {
      const latest = accumulatedEvents[accumulatedEvents.length - 1]!;
      console.log(
        `[manual] 记录 #${accumulatedEvents.length}: ${latest.kind} ${latest.label || latest.selector || latest.text.slice(0, 40)}`,
      );
      lastEventCount = accumulatedEvents.length;
    }

    const domText = await page.locator("body").innerText().catch(() => "");
    const visible = extractPriority1VisibleQuotes(networkBodies, domText);
    if (visible.length > 0 && !quoteDetected) {
      quoteDetected = true;
      console.log(`\n[manual] ✓ 已检测到报价 ${visible.length} 条（可继续核对，Ctrl+C 保存）`);
      await page.screenshot({
        path: join(outDir, "quote-result-full.png"),
        fullPage: true,
      });
    }

    await page.waitForTimeout(pollMs).catch(async () => {
      await finish("浏览器已关闭 — 已保存已记录操作");
    });
  }

  await finish("等待超时 — 已保存录制");
}
