#!/usr/bin/env tsx
/**
 * 探测 Priority1 三种 Shipment Type 的 Step 2 表单差异
 *   npx tsx scripts/probe-priority1-shipment-types.ts
 */
import { mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { chromium, type Page } from "playwright";

const ENTRY =
  "https://www.priority1.com/instant-freight-quotes/#New%20Step%201";
const CLICK = { noWaitAfter: true as const };

type ShipmentProbe = {
  id: string;
  label: string;
  checkboxSelector: string;
  hashFallback?: string;
};

const TYPES: ShipmentProbe[] = [
  {
    id: "ltl",
    label: "Less Than Truckload",
    checkboxSelector: "#cb_st_ltl",
    hashFallback: "#V4.1%20Step%202%20-%20LTL",
  },
  {
    id: "ftl",
    label: "Full Truckload",
    checkboxSelector: "#cb_st_ftl",
    hashFallback: "#V4.1%20Step%202%20-%20FTL",
  },
  {
    id: "expedited",
    label: "Expedited",
    checkboxSelector: "#cb_st_expedited",
    hashFallback: "#V4.1%20Step%202%20-%20Expedited",
  },
];

async function discoverCheckboxes(page: Page): Promise<string[]> {
  return page.evaluate(() => {
    const out: string[] = [];
    const cbs = document.querySelectorAll(
      '#container-mh input[type="checkbox"], #container-mh input[type="radio"]',
    );
    for (let i = 0; i < cbs.length; i += 1) {
      const el = cbs[i] as HTMLInputElement;
      const id = el.id || `(no-id-${i})`;
      const label =
        el.labels?.[0]?.textContent?.replace(/\s+/g, " ").trim().slice(0, 80) ||
        el.getAttribute("aria-label") ||
        "";
      out.push(`${id} | checked=${el.checked} | ${label}`);
    }
    return out;
  });
}

async function inventoryStep2(page: Page) {
  return page.evaluate(() => {
    const root = document.querySelector("#container-mh");
    if (!root) return { title: "", fields: [] as Array<Record<string, string>> };

    const title =
      root.querySelector("h1,h2,h3")?.textContent?.replace(/\s+/g, " ").trim() ||
      document.title;

    const fields: Array<Record<string, string>> = [];

    const inputs = root.querySelectorAll("input, select, textarea");
    for (let i = 0; i < inputs.length; i += 1) {
      const el = inputs[i] as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
      const r = el.getBoundingClientRect();
      if (r.width <= 0 || r.height <= 0) continue;

      let label = "";
      if (el.id) {
        const lbl = root.querySelector(`label[for="${el.id}"]`);
        if (lbl) label = lbl.textContent?.replace(/\s+/g, " ").trim() || "";
      }
      if (!label && el.labels?.[0]) {
        label = el.labels[0].textContent?.replace(/\s+/g, " ").trim() || "";
      }

      let options = "";
      if (el.tagName === "SELECT") {
        const sel = el as HTMLSelectElement;
        options = [...sel.options]
          .map((o) => o.text.trim())
          .filter(Boolean)
          .slice(0, 12)
          .join(" | ");
      }

      fields.push({
        tag: el.tagName.toLowerCase(),
        type: (el as HTMLInputElement).type || "",
        id: el.id || "",
        name: el.getAttribute("name") || "",
        label: label.slice(0, 120),
        placeholder: el.getAttribute("placeholder") || "",
        value: String(
          el.tagName === "SELECT"
            ? (el as HTMLSelectElement).selectedOptions[0]?.text || ""
            : (el as HTMLInputElement).value || "",
        ).slice(0, 80),
        options,
      });
    }

    const cards = [...root.querySelectorAll("*")].filter((el) => {
      const t = (el.textContent || "").replace(/\s+/g, " ").trim();
      return (
        t.length > 10 &&
        t.length < 200 &&
        /Dry Van|Open Deck|Temperature|Sprinter|Trailer|Partial PTL|Team Drivers/i.test(t)
      );
    });

    const cardTexts = [...new Set(cards.map((el) => el.textContent!.replace(/\s+/g, " ").trim()))]
      .slice(0, 20);

    return { title, fields, cardTexts };
  });
}

async function fillStep1(page: Page): Promise<void> {
  await page.locator("#container-mh #origin-zip").fill("32801");
  await page.locator("#container-mh #destination-zip").fill("28202");
  await page.locator("#container-mh #freight-shipment").selectOption("One Time Shipment");
  await page.locator("#container-mh #pickup-date").fill("07/14/2026");
  await page.locator("#container-mh #business-email").fill("niqijiti542@gmail.com");
  const phone = page.locator("#container-mh #client-phone");
  await phone.click(CLICK);
  await phone.fill("");
  await phone.pressSequentially("7536407420", { delay: 20 });
}

async function selectShipmentType(
  page: Page,
  probe: ShipmentProbe,
): Promise<boolean> {
  const cb = page.locator(`#container-mh ${probe.checkboxSelector}`);
  if ((await cb.count()) === 0) {
    await page
      .locator("#container-mh")
      .getByText(new RegExp(probe.label, "i"))
      .first()
      .click(CLICK)
      .catch(() => undefined);
  } else {
    await cb.click({ force: true, ...CLICK });
  }
  await page.waitForTimeout(600);
  await page
    .locator("#container-mh")
    .getByText(new RegExp(`^${probe.label.split(" ")[0]}`, "i"))
    .first()
    .click(CLICK)
    .catch(() => undefined);
  await page.waitForTimeout(500);

  return page.evaluate((sel) => {
    const cb = document.querySelector(sel) as HTMLInputElement | null;
    return Boolean(cb?.checked);
  }, `#container-mh ${probe.checkboxSelector}`);
}

async function goToStep2(
  page: Page,
  probe: ShipmentProbe,
): Promise<"submit" | "hash"> {
  const btn = page
    .locator("#container-mh")
    .getByRole("button", { name: /GET INSTANT QUOTES/i })
    .first();
  await btn.click(CLICK);
  await page.waitForTimeout(2000);
  await btn.click(CLICK).catch(() => undefined);

  const appeared = await page
    .waitForFunction(
      () => {
        const root = document.querySelector("#container-mh");
        if (!root) return false;
        const text = root.textContent || "";
        return (
          /Step\s*2\s*of\s*2/i.test(text) &&
          !/Pickup ZIP Code/i.test(text.slice(0, 500))
        );
      },
      { timeout: 25_000 },
    )
    .then(() => true)
    .catch(() => false);

  if (appeared) return "submit";

  if (probe.hashFallback) {
    await page.goto(
      `https://www.priority1.com/instant-freight-quotes/${probe.hashFallback}`,
      { waitUntil: "domcontentloaded" },
    );
    await page.waitForTimeout(1500);
    return "hash";
  }
  return "hash";
}

async function main(): Promise<void> {
  const outDir = join(process.cwd(), ".rpa", "priority1-shipment-probe");
  mkdirSync(outDir, { recursive: true });

  const browser = await chromium.launch({
    headless: process.env.RPA_HEADLESS !== "false",
    channel: (process.env.RPA_BROWSER_CHANNEL?.trim() || "chrome") as "chrome",
  });
  const page = await browser.newPage({ viewport: { width: 1400, height: 900 } });

  const report: Record<string, unknown> = {
    probedAt: new Date().toISOString(),
    types: [] as unknown[],
  };

  await page.goto(ENTRY, { waitUntil: "domcontentloaded" });
  await page.locator("#container-mh").waitFor({ state: "visible", timeout: 60_000 });
  report.step1Checkboxes = await discoverCheckboxes(page);

  for (const probe of TYPES) {
    console.log(`\n[probe] ▶ ${probe.id} — ${probe.label}`);
    await page.goto(ENTRY, { waitUntil: "domcontentloaded" });
    await page.locator("#container-mh").waitFor({ state: "visible" });
    await page.waitForTimeout(800);

    const selected = await selectShipmentType(page, probe);
    await fillStep1(page);
    await page.screenshot({
      path: join(outDir, `${probe.id}-step1-filled.png`),
      fullPage: true,
    });

    const nav = await goToStep2(page, probe);
    await page.waitForTimeout(1000);
    const inv = await inventoryStep2(page);

    await page.screenshot({
      path: join(outDir, `${probe.id}-step2-full.png`),
      fullPage: true,
    });

    const entry = {
      id: probe.id,
      label: probe.label,
      checkboxSelector: probe.checkboxSelector,
      selected,
      navigatedVia: nav,
      finalUrl: page.url(),
      step2Title: inv.title,
      fieldCount: inv.fields.length,
      fields: inv.fields,
      optionCards: inv.cardTexts,
    };
    (report.types as unknown[]).push(entry);

    writeFileSync(
      join(outDir, `${probe.id}-step2-fields.json`),
      `${JSON.stringify(entry, null, 2)}\n`,
      "utf8",
    );

    console.log(`  selected=${selected} nav=${nav} fields=${inv.fields.length}`);
    console.log(`  title: ${inv.title}`);
    for (const f of inv.fields.slice(0, 15)) {
      console.log(`    - [${f.tag}/${f.type}] #${f.id} ${f.label || f.placeholder}`);
    }
    if (inv.fields.length > 15) {
      console.log(`    ... +${inv.fields.length - 15} more`);
    }
  }

  writeFileSync(
    join(outDir, "shipment-types-report.json"),
    `${JSON.stringify(report, null, 2)}\n`,
    "utf8",
  );
  console.log(`\n[probe] 报告: ${join(outDir, "shipment-types-report.json")}`);

  await browser.close();
}

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