/**
 * P1：headed 解剖 Axel Search / 地址输入组件
 *
 * 自动化采集 gm_*、Fiber、联想 DOM、P2 fetch 栈；可选 page.pause() 供人工点选对照。
 *
 * PowerShell:
 *   $env:RPA_HEADLESS="false"
 *   $env:RPA_PLACE_DIAG="true"
 *   $env:PROBE_ANATOMY_PAUSE="true"   # 打开 Playwright Inspector 暂停
 *   npm run probe:axel-anatomy
 */
process.env.RPA_PLACE_DIAG = "true";
process.env.RPA_HEADLESS = "false";
process.env.RPA_KEEP_BROWSER_OPEN = "true";

import { randomUUID } from "node:crypto";
import { mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import type { Page, Request, Response } from "playwright";
import { loadDevEnv } from "@/lib/dev-env";
import { hostFetchMothershipCandidates } from "@/lib/frontend/api-client";
import { applyMothershipCandidate } from "@/lib/frontend/mothership-address";
import {
  SCAN_GLOBAL_AXEL_HOOKS,
  SCAN_INPUT_GM_AND_DOM,
  SCAN_REACT_FIBER_ADDRESS,
  SCAN_SUGGESTION_ROWS,
} from "@/workers/rpa/fix-place/axel-anatomy-scripts";
import {
  P0_PLACE_DIAG_INIT,
  P2_FETCH_HIJACK_INIT,
} from "@/workers/rpa/fix-place/place-diagnostic-init";
import {
  collectPlaceDiagSnapshot,
  interpretPlaceDiagnostic,
} from "@/workers/rpa/fix-place/place-diagnostic-probe";
import { openAddressSide, visibleStreetInput } from "@/workers/rpa/address-side";
import { ensureAddressSuggestions } from "@/workers/rpa/address-suggestions";
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 { waitForStyledSuggestionRows } from "@/workers/rpa/mothership-styled-suggestion-click";

loadDevEnv();
process.env.RPA_MOCK_MODE = "false";
process.env.RPA_ADDRESS_MODE = "real";

const BASE = process.env.PROVE_BASE_URL ?? "http://localhost:3000";
const TOKEN = "demo-host-token";
const CUSTOMER_ID = "CUST_001";
const OUT_DIR = join(process.cwd(), ".rpa", "axel-anatomy");

const PICKUP = {
  street: "Washington State Convention Center Main Garage, Pike Street",
  city: "Seattle",
  state: "WA",
} as const;

type NetEvent = {
  phase: string;
  method: string;
  url: string;
  status?: number;
  headers: Record<string, string>;
  at: number;
};

function evalScan<T>(page: Page, fn: string): Promise<T> {
  return page.evaluate((body) => {
    const runner = eval(`(${body})`) as () => T;
    return runner();
  }, fn);
}

function attachNetworkTap(page: Page, phase: string, log: NetEvent[]): void {
  const onReq = (req: Request): void => {
    const url = req.url();
    if (!url.includes("axel/location/")) {
      return;
    }
    const headers = req.headers();
    const axelHeaders: Record<string, string> = {};
    for (const [k, v] of Object.entries(headers)) {
      if (/x-axel|x-mothership|authorization|cookie/i.test(k)) {
        axelHeaders[k] = k.toLowerCase() === "cookie" ? "(redacted)" : v;
      }
    }
    log.push({
      phase,
      method: req.method(),
      url: url.slice(0, 280),
      headers: axelHeaders,
      at: Date.now(),
    });
  };
  const onRes = (res: Response): void => {
    const url = res.url();
    if (!url.includes("axel/location/place/")) {
      return;
    }
    const row = log.find((e) => e.url === url.slice(0, 280) && e.status === undefined);
    if (row) {
      row.status = res.status();
    } else {
      log.push({
        phase,
        method: res.request().method(),
        url: url.slice(0, 280),
        status: res.status(),
        headers: {},
        at: Date.now(),
      });
    }
  };
  page.on("request", onReq);
  page.on("response", onRes);
}

async function capturePhase(
  page: Page,
  phase: string,
): Promise<Record<string, unknown>> {
  const [globals, inputs, fiber, suggestions, p0] = await Promise.all([
    evalScan<Record<string, unknown>>(page, SCAN_GLOBAL_AXEL_HOOKS),
    evalScan<Record<string, unknown>>(page, SCAN_INPUT_GM_AND_DOM),
    evalScan<Record<string, unknown>>(page, SCAN_REACT_FIBER_ADDRESS),
    evalScan<Record<string, unknown>>(page, SCAN_SUGGESTION_ROWS),
    collectPlaceDiagSnapshot(page),
  ]);
  return {
    phase,
    at: new Date().toISOString(),
    globals,
    inputs,
    fiber,
    suggestions,
    p0,
    p0Interpretation: interpretPlaceDiagnostic(p0, phase),
  };
}

function summarizeVerdict(phases: Record<string, unknown>[]): string[] {
  const lines: string[] = ["=== 解剖结论（自动）==="];
  const afterSearch = phases.find((p) => p.phase === "after-search");
  const inputScan = (afterSearch?.inputs ?? {}) as {
    inputs?: Array<{ gmKeys?: string[]; pacTarget?: boolean }>;
    pacItemCount?: number;
    styledRowCount?: number;
  };
  const gmInputs = (inputScan.inputs ?? []).filter((r) => (r.gmKeys?.length ?? 0) > 0);
  if (gmInputs.length > 0) {
    lines.push("→ 输入框存在 gm_*：底层可能仍用 Google PAC，但 P0 未 patch 到（或实例在 iframe/早于 patch）");
  } else {
    lines.push("→ 输入框无 gm_*：倾向 Axel 自研 Search UI（非标准 PAC 输入）");
  }
  if ((inputScan.pacItemCount ?? 0) > 0) {
    lines.push(`→ 可见 .pac-item × ${inputScan.pacItemCount}`);
  }
  if ((inputScan.styledRowCount ?? 0) > 0) {
    lines.push(`→ styled-components 联想行容器 × ${inputScan.styledRowCount}`);
  }
  const fiberHits = ((afterSearch?.fiber as { hits?: unknown[] })?.hits ?? []).length;
  lines.push(`→ React Fiber 地址相关节点 × ${fiberHits}`);
  return lines;
}

async function main(): Promise<void> {
  const runId = randomUUID().slice(0, 8);
  mkdirSync(OUT_DIR, { recursive: true });
  const netLog: NetEvent[] = [];
  const phases: Record<string, unknown>[] = [];

  const candRes = await hostFetchMothershipCandidates(
    BASE,
    TOKEN,
    CUSTOMER_ID,
    { ...PICKUP, zip: "" },
    { street: "Ocean Drive", city: "Miami Beach", state: "FL", zip: "" },
  );
  if (candRes.code !== 0 || !candRes.data?.pickup_candidates[0]) {
    throw new Error("候选 API 失败");
  }
  const pickupCand = candRes.data.pickup_candidates[0];
  const label = pickupCand.display_label;

  console.log(`\n=== Axel Search 解剖 run=${runId} ===`);
  console.log(`地址：${label}\n`);

  const opened = await openQuotePage(quotePageAdapter, false, undefined, {
    freshCargoContext: true,
  });
  await opened.context.addInitScript({ content: P0_PLACE_DIAG_INIT });
  await opened.context.addInitScript({ content: P2_FETCH_HIJACK_INIT });
  await opened.page.reload({ waitUntil: "domcontentloaded" });

  const ctx = RPAContext.fromSession(opened.page, opened.context);
  attachNetworkTap(ctx.page, "session", netLog);

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

  phases.push(await capturePhase(ctx.page, "widget-hydrated"));

  await openAddressSide(ctx, "pickup");
  const street = await visibleStreetInput(ctx, "pickup");
  if (!street) {
    throw new Error("提货输入框不可见");
  }

  phases.push(await capturePhase(ctx.page, "pickup-input-focused"));

  await ensureAddressSuggestions(ctx.page, street, PICKUP, {
    mothershipLabel: label,
    preferConfirmedLabel: true,
  });
  await waitForStyledSuggestionRows(ctx.page, [], PICKUP, 15_000);

  phases.push(await capturePhase(ctx.page, "after-search"));

  console.log("\n--- 人工步骤（headed）---");
  console.log("1. 在浏览器中手动点选一条联想");
  console.log("2. DevTools → Elements：检查 input 是否有 gm_*");
  console.log("3. DevTools → Network：点选后是否 GET axel/location/place/{id}");
  console.log("4. React DevTools：找 Search/Candidate/Place 组件与 onSelect");
  console.log("5. 完成后在 Playwright Inspector 点 Resume\n");

  if (process.env.PROBE_ANATOMY_PAUSE !== "false") {
    await ctx.page.pause();
  }

  phases.push(await capturePhase(ctx.page, "after-manual-or-pause"));

  const report = {
    runId,
    label,
    placeId: pickupCand.option_id,
    phases,
    network: netLog,
    verdict: summarizeVerdict(phases),
    finishedAt: new Date().toISOString(),
  };

  const reportPath = join(OUT_DIR, `${runId}-anatomy.json`);
  writeFileSync(reportPath, JSON.stringify(report, null, 2), "utf8");

  console.log("\n" + report.verdict.join("\n"));
  console.log(`\n报告：${reportPath}`);
  console.log("浏览器保持打开（RPA_KEEP_BROWSER_OPEN=true），关闭窗口或 Ctrl+C 结束。");

  if (process.env.PROBE_ANATOMY_CLOSE === "true") {
    await opened.page.close().catch(() => undefined);
    await opened.context.close().catch(() => undefined);
    await closeBrowser();
  }
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});
