#!/usr/bin/env tsx
/**
 * 真人 vs RPA 网络差分（同一地址对，不新增 fix-place 策略）
 *
 * 默认对齐录证 5519e0ea：293 Pike St Seattle → 826 Ocean Dr Miami Beach
 *
 * 用法:
 *   npm run compare:human-rpa-network
 *   $env:HUMAN_RUN_ID="5519e0ea"; $env:RPA_HEADLESS="false"; npm run compare:human-rpa-network
 */
import { randomUUID } from "node:crypto";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { loadDevEnv } from "@/lib/dev-env";
import { shouldKeepBrowserOpen } from "@/lib/rpa/env";
import { hostFetchMothershipCandidates } from "@/lib/frontend/api-client";
import { applyMothershipCandidate } from "@/lib/frontend/mothership-address";
import { MotherShipAddressAdapter } from "@/workers/rpa/address-adapter/mothership-address-adapter";
import { openAddressSide } from "@/workers/rpa/address-side";
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 type { QuoteRequest } from "@/modules/providers/quote-provider";
import {
  diffHumanRpaNetwork,
  loadNetworkNdjson,
} from "./human-commit-baseline/network-diff";
import { NetworkLogger } from "./human-commit-baseline/network-logger";

process.env.RPA_MOCK_MODE = "false";
process.env.RPA_ADDRESS_MODE = "real";

loadDevEnv();

const HUMAN_RUN_ID = process.env.HUMAN_RUN_ID ?? "5519e0ea";
const BASE = process.env.PROVE_BASE_URL ?? "http://localhost:3000";
const TOKEN = "demo-host-token";
const CUSTOMER_ID = "CUST_001";

/** 与真人录证 5519e0ea 一致的用户输入（候选 API 会解析为具体 display_label） */
const BASELINE_PAIR = {
  label: "5519e0ea 基准：Seattle Pike St → Miami Beach Ocean Dr",
  pickup: { street: "293 Pike St", city: "Seattle", state: "WA" },
  delivery: { street: "826 Ocean Dr", city: "Miami Beach", state: "FL" },
};

function draftAddress(d: { street: string; city: string; state: string }) {
  const street = d.street.trim();
  const city = d.city.trim();
  const state = d.state.trim();
  return {
    street,
    city,
    state,
    zip: "",
    place_id: `draft_${street}_${city}_${state}`.replace(/\s+/g, "_").slice(0, 64),
    formatted_address: `${street}, ${city}, ${state}`,
    selected_from_suggestions: true,
  };
}

async function selectSide(
  ctx: RPAContext,
  side: "pickup" | "delivery",
  address: QuoteRequest["pickup"],
  logger: NetworkLogger,
  pickupAnchor?: QuoteRequest["pickup"],
): Promise<{ ok: boolean; error?: string; commitState?: string }> {
  const widgetSelector = ctx.selectors.resolve("RPA_SELECTOR_QUOTE_WIDGET");
  logger.setCheckpoint(side === "pickup" ? "rpa_pickup" : "rpa_delivery");

  const street = await openAddressSide(ctx, side);
  if (!street) {
    return { ok: false, error: `${side} 输入框不可见` };
  }

  const adapter = new MotherShipAddressAdapter();
  try {
    await adapter.selectAddress({
      ctx,
      side,
      address,
      street,
      widgetSelector,
      pickupAnchor: side === "delivery" ? pickupAnchor : undefined,
    });
    await adapter.waitForCommit();
  } catch (e) {
    return {
      ok: false,
      error: e instanceof Error ? e.message : String(e),
      commitState: adapter.getCommitState(),
    };
  }

  const diag = adapter.getDiagnostics();
  return {
    ok: diag.placeRequestObserved && adapter.getCommitState() === "COMMITTED",
    commitState: adapter.getCommitState(),
    error: diag.placeRequestObserved ? undefined : "placeRequestObserved=false",
  };
}

async function main(): Promise<void> {
  const humanNetworkPath = join(
    process.cwd(),
    ".rpa",
    "human-commit-baseline",
    HUMAN_RUN_ID,
    "network.ndjson",
  );
  if (!existsSync(humanNetworkPath)) {
    throw new Error(`找不到真人 network: ${humanNetworkPath}`);
  }

  const humanEvents = loadNetworkNdjson(humanNetworkPath);
  const rpaRunId = randomUUID().slice(0, 8);
  const outDir = join(
    process.cwd(),
    ".rpa",
    "human-commit-baseline",
    HUMAN_RUN_ID,
    `rpa-diff-${rpaRunId}`,
  );
  mkdirSync(outDir, { recursive: true });
  const rpaNetworkPath = join(outDir, "rpa-network.ndjson");

  console.log(`[diff] 真人基准: ${HUMAN_RUN_ID}`);
  console.log(`[diff] RPA 录网: ${BASELINE_PAIR.label}`);
  console.log(`[diff] 产出: ${outDir}`);

  const userPickup = draftAddress(BASELINE_PAIR.pickup);
  const userDelivery = draftAddress(BASELINE_PAIR.delivery);

  const cand = await hostFetchMothershipCandidates(
    BASE,
    TOKEN,
    CUSTOMER_ID,
    userPickup,
    userDelivery,
  );
  if (cand.code !== 0 || !cand.data?.pickup_candidates[0] || !cand.data.delivery_candidates[0]) {
    throw new Error(cand.message ?? "候选 API 失败");
  }

  const pc = cand.data.pickup_candidates[0];
  const dc = cand.data.delivery_candidates[0];
  const pickupApplied = applyMothershipCandidate(userPickup, pc);
  const deliveryApplied = applyMothershipCandidate(userDelivery, dc);

  const pickup: QuoteRequest["pickup"] = {
    street: pickupApplied.street,
    city: pickupApplied.city,
    state: pickupApplied.state,
    zip: pickupApplied.zip ?? "",
    placeId: pc.option_id,
    formattedAddress: pickupApplied.formatted_address,
    selectedFromSuggestions: true,
    mothershipOptionId: pc.option_id,
    mothershipDisplayLabel: pc.display_label,
    selectedFromMothership: true,
  };
  const delivery: QuoteRequest["delivery"] = {
    street: deliveryApplied.street,
    city: deliveryApplied.city,
    state: deliveryApplied.state,
    zip: deliveryApplied.zip ?? "",
    placeId: dc.option_id,
    formattedAddress: deliveryApplied.formatted_address,
    selectedFromSuggestions: true,
    mothershipOptionId: dc.option_id,
    mothershipDisplayLabel: dc.display_label,
    selectedFromMothership: true,
  };

  console.log(`[diff] 确认提货: ${pc.display_label}`);
  console.log(`[diff] 确认派送: ${dc.display_label}`);

  const logger = new NetworkLogger(rpaNetworkPath);
  const opened = await openQuotePage(quotePageAdapter, false, undefined, {
    freshCargoContext: true,
  });
  logger.setCheckpoint("rpa_page_loaded");
  logger.attach(opened.page);

  const ctx = RPAContext.fromSession(opened.page, opened.context);
  console.log("[diff] 页面已打开，正在处理 Cookie 并滚动到报价区…");
  await stabilizeQuoteLandingPage(ctx);
  console.log(
    "[diff] 等待 MotherShip 报价 widget 加载（含地图脚本，界面可能数十秒无输入，属正常）…",
  );
  const hydration = await waitForQuoterWidgetHydrated(ctx.page, 90_000);
  if (hydration.hydrated) {
    console.log(
      `[diff] widget 已就绪（可编辑地址框 ${hydration.editableSearchInputs} 个）`,
    );
  } else {
    console.warn(
      `[diff] widget 未完全就绪，继续尝试：${hydration.widgetTextPreview.slice(0, 80)}`,
    );
  }

  console.log("[diff] 开始填写提货地址…");
  const rpaResults: Record<string, unknown> = {};

  const pickupResult = await selectSide(ctx, "pickup", pickup, logger);
  rpaResults.pickup = pickupResult;
  console.log("[diff] RPA pickup:", pickupResult);

  let deliveryResult: Awaited<ReturnType<typeof selectSide>> | null = null;
  if (pickupResult.ok) {
    console.log("[diff] 提货完成，开始填写派送地址…");
    deliveryResult = await selectSide(ctx, "delivery", delivery, logger, pickup);
    rpaResults.delivery = deliveryResult;
    console.log("[diff] RPA delivery:", deliveryResult);
  } else {
    rpaResults.delivery = { skipped: true, reason: "pickup failed" };
  }

  logger.flush();

  const dwellMs = Number(process.env.RPA_DWELL_MS ?? 0);
  if (dwellMs > 0) {
    console.log(`[diff] 完成后停留 ${dwellMs}ms 便于观察…`);
    await opened.page.waitForTimeout(dwellMs).catch(() => undefined);
  }

  if (!shouldKeepBrowserOpen()) {
    await opened.page.close().catch(() => undefined);
    await opened.context.close().catch(() => undefined);
    await closeBrowser();
  } else {
    console.log(
      "[diff] 浏览器保持打开（RPA_HEADLESS=false 默认行为；关闭窗口或 Ctrl+C 结束）",
    );
  }
  const rpaEvents = loadNetworkNdjson(rpaNetworkPath);
  const diff = diffHumanRpaNetwork(humanEvents, rpaEvents, HUMAN_RUN_ID, rpaRunId);

  const humanPlaceIds = readFileSync(
    join(process.cwd(), ".rpa", "human-commit-baseline", HUMAN_RUN_ID, "commit-moments.json"),
    "utf8",
  );
  const report = {
    finishedAt: new Date().toISOString(),
    humanRunId: HUMAN_RUN_ID,
    rpaRunId,
    baselinePair: BASELINE_PAIR,
    confirmedPickup: pc.display_label,
    confirmedDelivery: dc.display_label,
    humanCommitMoments: JSON.parse(humanPlaceIds),
    rpaSelectResults: rpaResults,
    diff,
    artifacts: {
      humanNetwork: humanNetworkPath,
      rpaNetwork: rpaNetworkPath,
      report: join(outDir, "diff-report.json"),
    },
  };

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

  console.log("\n════════ 差分摘要 ════════");
  console.log(`verdict: ${diff.verdict}`);
  console.log(
    `place GET 200 — 真人: ${diff.human.placeGet200} | RPA: ${diff.rpa.placeGet200}`,
  );
  console.log(
    `search→place 完整链 — 真人: ${diff.human.chains.filter((c) => c.complete).length}/${diff.human.chains.length} | RPA: ${diff.rpa.chains.filter((c) => c.complete).length}/${diff.rpa.chains.length}`,
  );
  if (diff.gaps.length) {
    console.log("\n差异:");
    for (const g of diff.gaps) {
      console.log(`  - ${g}`);
    }
  }
  if (diff.recommendations.length) {
    console.log("\n建议:");
    for (const r of diff.recommendations) {
      console.log(`  → ${r}`);
    }
  }
  console.log(`\n完整报告: ${join(outDir, "diff-report.json")}`);

  const exitCode = diff.verdict === "MATCH" ? 0 : 1;
  if (!shouldKeepBrowserOpen()) {
    process.exit(exitCode);
  }

  console.log(
    "[diff] 填址已完成。按 Ctrl+C 结束会话（headed 模式默认保持浏览器窗口）",
  );
  await new Promise<void>((resolve) => {
    const done = () => resolve();
    process.once("SIGINT", done);
    process.once("SIGTERM", done);
  });
  await opened.page.close().catch(() => undefined);
  await opened.context.close().catch(() => undefined);
  await closeBrowser();
  process.exit(exitCode);
}

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