#!/usr/bin/env tsx
/**
 * 对比人工录制 vs 自动化：联想 DOM 定位诊断
 * 输出：.rpa/suggestion-compare/report-{id}.json + 截图
 */
import { randomUUID } from "node:crypto";
import { mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import type { Page, Locator } from "playwright";
import { loadDevEnv } from "@/lib/dev-env";
import { COLLECT_SUGGESTION_MOUSE_TARGETS, COLLECT_WIDGET_SUGGESTION_LABELS } from "@/workers/rpa/kernel/browser-scripts";
import { collectSuggestionMouseTargets } from "@/workers/rpa/address-suggestion-mouse";
import {
  collectAddressSuggestionLabels,
  isAddressSuggestionDropdownOpen,
} from "@/workers/rpa/address-suggestions";
import {
  isAddressPresentInWidget,
  readWidgetCombinedText,
} from "@/workers/rpa/address-commit";
import { RPAContext } from "@/workers/rpa/kernel/context";
import {
  stabilizeQuoteLandingPage,
  waitForQuoterWidgetHydrated,
} from "@/workers/rpa/page-prep";
import { closeBrowser, openQuotePage } from "@/workers/rpa/session-manager";
import { quotePageAdapter } from "@/workers/rpa/quote-page-adapter";
import { openAddressSide, visibleStreetInput } from "@/workers/rpa/address-side";

loadDevEnv();

process.env.RPA_HEADLESS = process.env.RPA_HEADLESS ?? "false";
process.env.RPA_KEEP_BROWSER_OPEN = "false";

const PICKUP = {
  street: "1234 Warehouse Street",
  city: "Los Angeles",
  state: "CA",
  mothershipDisplayLabel: "1234 Warehouse Street, Los Angeles, California, USA",
};

/** 人工录制用的 partial text（codegen 生成） */
const MANUAL_PROBES = {
  pickupZh: "1234 Warehouse StreetLos",
  pickupEn: "1234 Warehouse StreetLos",
  deliveryZh: "600 East 7th StreetPomona,",
};

const DOM_PROBE_SCRIPT = `(streetNum) => {
  var skipRe = /use my location|pick up from|deliver to|where to|freight details|search by address/i;
  var out = [];
  document.querySelectorAll("div, li, span, [role='option'], .pac-item").forEach(function(el) {
    if (el.tagName === "INPUT" || el.tagName === "TEXTAREA") return;
    var text = (el.innerText || "").replace(/\\s+/g, " ").trim();
    if (!text || text.length < 8 || skipRe.test(text)) return;
    if (streetNum && text.indexOf(streetNum) < 0) return;
    var rect = el.getBoundingClientRect();
    if (!rect.width || !rect.height || rect.width < 80 || rect.height < 16) return;
    out.push({
      tag: el.tagName,
      className: (el.className || "").toString().slice(0, 60),
      role: el.getAttribute("role"),
      text: text.slice(0, 100),
      x: Math.round(rect.left + rect.width / 2),
      y: Math.round(rect.top + rect.height / 2),
      w: Math.round(rect.width),
      h: Math.round(rect.height),
    });
  });
  out.sort(function(a, b) { return a.y - b.y || a.w * a.h - b.w * b.h; });
  return out.slice(0, 30);
}`;

async function probeGetByText(page: Page, probe: string) {
  const loc = page.getByText(probe, { exact: false }).first();
  const visible = await loc.isVisible().catch(() => false);
  const box = visible ? await loc.boundingBox().catch(() => null) : null;
  const count = await page.getByText(probe, { exact: false }).count().catch(() => 0);
  return { probe, visible, count, box };
}

async function snapshotPhase(
  page: Page,
  input: Locator,
  widgetSelector: string,
  outDir: string,
  phase: string,
) {
  const shot = join(outDir, `${phase}.png`);
  await page.screenshot({ path: shot, fullPage: false }).catch(() => undefined);

  const streetNum = PICKUP.street.match(/^\d+/)?.[0] ?? "";
  const domHits = (await page.evaluate(DOM_PROBE_SCRIPT, streetNum)) as unknown[];
  const widgetLabels = (await page.evaluate(COLLECT_WIDGET_SUGGESTION_LABELS).catch(
    () => [],
  )) as string[];
  const mouseTargets = await collectSuggestionMouseTargets(page, input);
  const autoLabels = await collectAddressSuggestionLabels(page, {
    street: PICKUP.street,
    city: PICKUP.city,
    state: PICKUP.state,
  });
  const dropdownOpen = await isAddressSuggestionDropdownOpen(page);
  const visualPresent = await isAddressPresentInWidget(page, widgetSelector, PICKUP);
  const committedVisually = await import("@/workers/rpa/address-suggestion-mouse").then(
    (m) => m.isAddressCommittedVisually(page, widgetSelector, PICKUP, input),
  );
  const widgetText = await readWidgetCombinedText(page, widgetSelector);
  const inputValue = await input.inputValue().catch(() => "");

  const manualProbes = await Promise.all([
    probeGetByText(page, MANUAL_PROBES.pickupZh),
    probeGetByText(page, MANUAL_PROBES.pickupEn),
  ]);

  return {
    phase,
    screenshot: shot,
    inputValue: inputValue.slice(0, 120),
    dropdownOpen,
    visualPresent,
    committedVisually,
    widgetTextPreview: widgetText.slice(0, 200),
    collectWidgetLabels: widgetLabels,
    collectAutoLabels: autoLabels,
    mouseTargets,
    domHits,
    manualProbes,
    listboxCount: await page.locator('[role="listbox"]').count(),
    pacItemCount: await page.locator(".pac-item").count(),
    pacVisible: await page.locator(".pac-container").first().isVisible().catch(() => false),
  };
}

async function main(): Promise<void> {
  const runId = randomUUID().slice(0, 8);
  const outDir = join(process.cwd(), ".rpa", "suggestion-compare", runId);
  mkdirSync(outDir, { recursive: true });

  console.log(`[suggestion-compare] run=${runId} out=${outDir}`);

  const opened = await openQuotePage(quotePageAdapter, false, undefined, {
    freshCargoContext: true,
  });
  const ctx = RPAContext.fromSession(opened.page, opened.context);
  const page = ctx.page;
  const widgetSelector = ctx.selectors.resolve("RPA_SELECTOR_QUOTE_WIDGET");

  await stabilizeQuoteLandingPage(ctx);
  await waitForQuoterWidgetHydrated(page, 90_000);

  const phases: unknown[] = [];

  await openAddressSide(ctx, "pickup", widgetSelector);
  const input = await visibleStreetInput(ctx, "pickup");
  if (!input) {
    throw new Error("提货输入框未找到");
  }

  phases.push(
    await snapshotPhase(page, input, widgetSelector, outDir, "01-after-open-pickup"),
  );

  const query = `${PICKUP.street}, ${PICKUP.city}, California`;
  await input.click({ force: true });
  await input.fill(query);
  await page.waitForTimeout(800);

  phases.push(
    await snapshotPhase(page, input, widgetSelector, outDir, "02-after-fill-800ms"),
  );

  await page.waitForTimeout(1200);
  phases.push(
    await snapshotPhase(page, input, widgetSelector, outDir, "03-after-fill-2s"),
  );

  // 尝试人工录制同款 getByText 点击
  const manualProbe = MANUAL_PROBES.pickupEn;
  const manualLoc = page.getByText(manualProbe, { exact: false }).first();
  let manualClickResult: Record<string, unknown> = { attempted: false };
  if (await manualLoc.isVisible().catch(() => false)) {
    manualClickResult = { attempted: true, probe: manualProbe };
    await manualLoc.click({ force: true, timeout: 3_000 }).catch((e) => {
      manualClickResult.error = String(e);
    });
    await page.waitForTimeout(600);
    phases.push(
      await snapshotPhase(page, input, widgetSelector, outDir, "04-after-manual-getByText-click"),
    );
    manualClickResult.afterClick = phases[phases.length - 1];
  } else {
    manualClickResult = {
      attempted: false,
      probe: manualProbe,
      reason: "getByText 不可见",
    };
  }

  const report = {
    runId,
    finishedAt: new Date().toISOString(),
    manualRecording: {
      file: ".rpa/recorded-mothership.ts",
      pickupClick: "page.getByText('1234 Warehouse StreetLos').click()",
      uiLocale: "zh-CN（用户系统浏览器）",
      note: "RPA 使用 en-US，placeholder=Search by address",
    },
    automationEnv: {
      locale: process.env.RPA_BROWSER_LOCALE ?? "en-US",
      headless: process.env.RPA_HEADLESS,
    },
    phases,
    manualClickResult,
    diagnosis: [] as string[],
  };

  const p2 = phases[2] as { visualPresent?: boolean; dropdownOpen?: boolean; mouseTargets?: unknown[]; manualProbes?: { visible: boolean }[]; collectWidgetLabels?: string[] };
  if (p2.visualPresent && p2.dropdownOpen) {
    report.diagnosis.push(
      "误判：isAddressPresentInWidget=true 但 dropdown 仍打开 → 输入框文字被当成已确认",
    );
  }
  if ((p2.mouseTargets?.length ?? 0) === 0 && (p2.collectWidgetLabels?.length ?? 0) === 0) {
    report.diagnosis.push(
      "定位失败：COLLECT_WIDGET_SUGGESTION_LABELS 与 mouseTargets 均为空；listbox/PAC 不存在，styled-components 阈值或时机问题",
    );
  }
  if (p2.manualProbes?.some((p) => p.visible)) {
    report.diagnosis.push(
      "人工录制 getByText 探针可见 → 应优先用 partial text 点击，而非 listbox/PAC 选择器",
    );
  } else {
    report.diagnosis.push(
      "人工录制 getByText 探针也不可见 → 联想尚未渲染或 DOM 结构与录制时不一致",
    );
  }

  const reportPath = join(outDir, "report.json");
  writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");

  console.log("\n════════ 联想定位对比 ════════");
  for (const line of report.diagnosis) {
    console.log(`  • ${line}`);
  }
  console.log(`报告: ${reportPath}`);

  await closeBrowser();
}

main().catch(async (err) => {
  console.error("[suggestion-compare] 失败:", err);
  await closeBrowser().catch(() => undefined);
  process.exitCode = 1;
});
