/**
 * Priority1 表单交互实现 — 与 form-logic-map.ts 字段定义一一对应
 * form-logic-map 描述「填什么」；本文件描述「怎么点/怎么选」
 *
 * 录制参考：.rpa/recordings/priority1-*.js
 * - Step1 FTL: #cb_st_ftl_sel（回退 #cb_st_ftl）
 * - Open Deck Type: select 选项如 48 Foot Flatbed / 48 Foot Step Deck
 * - 温控: #minimum-temperature / #maximum-temperature
 * - FTL 附加: #cb_as_ftl1 / #cb_as_ftl2 / #cb_as_ftl3
 */

import type { Locator, Page } from "playwright";

const CLICK = { force: true, noWaitAfter: true as const };

const OPEN_DECK_OPTION_RE =
  /Foot Flatbed|Foot Step Deck|Removable Gooseneck|Double Drop|Conestoga|Hot Shot/i;
const EXCLUDED_SELECT_ID_RE =
  /PickupLocation|DropoffLocation|freight-shipment|freight-package|freight-type/i;
/** F04 人工录制：Open Deck Type 在 Feathery 误命名 select #PickupLocation3 */
export const OPEN_DECK_TYPE_SELECT_SELECTOR = "#PickupLocation3";

/** 逻辑子类型 → 页面 option 文本模糊匹配 */
export function normalizeOpenDeckSubtypeQuery(subtype: string): string {
  const s = subtype.trim();
  if (/Foot Flatbed|Foot Step Deck|Removable Gooseneck|Double Drop|Conestoga|Hot Shot/i.test(s)) {
    return s;
  }
  const aliases: Record<string, string> = {
    Flatbed: "Foot Flatbed",
    "Step Deck": "Foot Step Deck",
    "Goose Neck RGN": "Removable Gooseneck",
    Gooseneck: "Removable Gooseneck",
  };
  for (const [k, v] of Object.entries(aliases)) {
    if (s === k) return v;
  }
  return s;
}

function optionMatchesSubtype(optionText: string, subtype: string): boolean {
  const opt = optionText.trim().toLowerCase();
  const sub = subtype.trim().toLowerCase();
  const normalized = normalizeOpenDeckSubtypeQuery(subtype).toLowerCase();
  return (
    opt === sub ||
    opt.includes(sub) ||
    sub.includes(opt) ||
    opt.includes(normalized) ||
    normalized.includes(opt)
  );
}

export async function waitForOpenDeckSubtypeUi(page: Page): Promise<void> {
  const started = Date.now();
  while (Date.now() - started < 25_000) {
    const ready = await page.evaluate(function (args) {
      const optionRe = new RegExp(args.optionReSource, "i");
      const excludeRe = new RegExp(args.excludeReSource, "i");
      const container = document.querySelector("#container-mh");
      if (!container) return false;
      const deckSel = container.querySelector("#PickupLocation3");
      if (deckSel instanceof HTMLSelectElement && deckSel.options.length >= 2) return true;
      const selects = container.querySelectorAll("select");
      for (let s = 0; s < selects.length; s += 1) {
        const el = selects[s];
        let deckOptions = 0;
        for (let j = 0; j < el.options.length; j += 1) {
          if (optionRe.test(el.options[j].text.trim())) deckOptions += 1;
        }
        if (excludeRe.test(el.id) && deckOptions < 2) continue;
        if (deckOptions >= 2) return true;
      }
      return /Open Deck Type|Foot Flatbed|Foot Step Deck/i.test(
        container.textContent || "",
      );
    }, {
      optionReSource: OPEN_DECK_OPTION_RE.source,
      excludeReSource: EXCLUDED_SELECT_ID_RE.source,
    });
    if (ready) return;
    await page.waitForTimeout(400);
  }
}

export async function waitForFeatheryCheckbox(
  page: Page,
  selector: string,
  timeoutMs = 25_000,
): Promise<void> {
  await page
    .locator(`#container-mh ${selector}`)
    .first()
    .waitFor({ state: "attached", timeout: timeoutMs });
  await page.waitForTimeout(600);
}

/** Feathery 隐藏 checkbox — DOM 强制勾选并派发事件（避免 headless click 无效） */
export async function forceFeatheryCheckbox(
  page: Page,
  selector: string,
): Promise<boolean> {
  return page.evaluate(function (sel) {
    const root = document.querySelector("#container-mh");
    if (!root) return false;
    const cb = root.querySelector(sel);
    if (!(cb instanceof HTMLInputElement) || cb.type !== "checkbox") return false;
    if (!cb.checked) {
      cb.checked = true;
      cb.dispatchEvent(new Event("input", { bubbles: true }));
      cb.dispatchEvent(new Event("change", { bubbles: true }));
      cb.dispatchEvent(new Event("click", { bubbles: true }));
    }
    return cb.checked;
  }, selector);
}

export function freightClassOptionMatches(
  optionText: string,
  expected: string,
): boolean {
  const t = optionText.trim().toLowerCase();
  const w = expected.trim().toLowerCase();
  if (t === w || optionText.trim() === expected.trim()) return true;
  if (w === "not sure" && t.includes("not sure")) return true;
  return false;
}

/** form-logic-map → FTL_CONDITIONAL_BRANCHES.ftl_open_deck.openDeckSubtype */
export async function selectOpenDeckSubtype(
  page: Page,
  subtype: string,
): Promise<{ locator: Locator; selectedText: string }> {
  await page.waitForTimeout(1200);
  await waitForOpenDeckSubtypeUi(page);

  const query = normalizeOpenDeckSubtypeQuery(subtype);
  const deckLoc = page.locator(`#container-mh ${OPEN_DECK_TYPE_SELECT_SELECTOR}`).first();
  if (await deckLoc.count()) {
    await deckLoc.selectOption({ label: query }).catch(() =>
      deckLoc.selectOption({ label: subtype.trim() }),
    );
    await page.waitForTimeout(400);
    const selectedText = await readSelectVisibleText(deckLoc);
    if (optionMatchesSubtype(selectedText, subtype)) {
      console.log(
        `[form-interactions] Open Deck Type → ${selectedText} (${OPEN_DECK_TYPE_SELECT_SELECTOR})`,
      );
      return { locator: deckLoc, selectedText };
    }
  }

  const picked = await page.evaluate(
    function (args) {
      const excludeRe = new RegExp(args.excludeReSource, "i");
      const optionRe = new RegExp(args.optionReSource, "i");
      const container = document.querySelector("#container-mh");
      if (!container) return null;

      const selects = container.querySelectorAll("select");
      for (let s = 0; s < selects.length; s += 1) {
        const sel = selects[s];
        let deckOptions = 0;
        for (let j = 0; j < sel.options.length; j += 1) {
          if (optionRe.test(sel.options[j].text.trim())) deckOptions += 1;
        }
        if (excludeRe.test(sel.id) && deckOptions < 2) continue;
        for (let i = 0; i < sel.options.length; i += 1) {
          const opt = sel.options[i];
          const optText = opt.text.trim().toLowerCase();
          const needle = args.sub.trim().toLowerCase();
          if (optText.indexOf(needle) < 0 && needle.indexOf(optText) < 0) continue;
          sel.selectedIndex = i;
          sel.value = opt.value;
          sel.dispatchEvent(new Event("input", { bubbles: true }));
          sel.dispatchEvent(new Event("change", { bubbles: true }));
          return { id: sel.id, text: opt.text.trim(), index: s };
        }
      }
      return null;
    },
    {
      sub: query,
      excludeReSource: EXCLUDED_SELECT_ID_RE.source,
      optionReSource: OPEN_DECK_OPTION_RE.source,
    },
  );

  if (picked) {
    console.log(
      `[form-interactions] Open Deck Type → ${picked.text} (#${picked.id || picked.index})`,
    );
    const locator = picked.id
      ? page.locator(`#container-mh #${picked.id}`)
      : page.locator("#container-mh select").nth(picked.index);
    return { locator, selectedText: picked.text };
  }

  throw new Error(`Open Deck Type 未找到选项: ${subtype}`);
}

/** form-logic-map → FTL_CONDITIONAL_BRANCHES.ftl_temp */
export async function fillTemperatureRange(
  page: Page,
  minF: string,
  maxF: string,
): Promise<{ min: Locator; max: Locator }> {
  const root = page.locator("#container-mh");
  const min = root.locator("#minimum-temperature").first();
  const max = root.locator("#maximum-temperature").first();
  await min.waitFor({ state: "visible", timeout: 15_000 });
  await min.fill("");
  await min.pressSequentially(minF, { delay: 30 });
  await min.evaluate(function (el) {
    el.dispatchEvent(new Event("input", { bubbles: true }));
    el.dispatchEvent(new Event("change", { bubbles: true }));
  });
  await max.fill("");
  await max.pressSequentially(maxF, { delay: 30 });
  await max.evaluate(function (el) {
    el.dispatchEvent(new Event("input", { bubbles: true }));
    el.dispatchEvent(new Event("change", { bubbles: true }));
  });
  return { min, max };
}

/** Feathery 隐藏 checkbox 是否已勾选（避免 locator.isChecked 90s 超时） */
export async function isFeatheryCheckboxChecked(
  page: Page,
  selector: string,
): Promise<boolean> {
  return page.evaluate(function (sel) {
    const root = document.querySelector("#container-mh");
    if (!root) return false;
    const cb = root.querySelector(sel);
    return cb instanceof HTMLInputElement && cb.type === "checkbox" && cb.checked;
  }, selector);
}

/** Feathery 卡片黑底高亮 = 已选 */
export async function isFeatheryCardSelected(
  page: Page,
  label: string,
): Promise<boolean> {
  return page.evaluate(function (labelText) {
    const nodes = document.querySelectorAll("#container-mh *");
    for (let i = 0; i < nodes.length; i += 1) {
      const el = nodes[i];
      const text = (el.textContent || "").trim();
      if (!text.startsWith(labelText) || text.length > 220) continue;
      let cur: Element | null = el;
      for (let d = 0; d < 6 && cur; d += 1) {
        const bg = getComputedStyle(cur).backgroundColor;
        if (bg === "rgb(0, 0, 0)" || bg === "black") return true;
        cur = cur.parentElement;
      }
    }
    return false;
  }, label);
}

/** form-logic-map → FTL_ADDITIONAL_FIELDS */
export async function selectFtlAdditionalService(
  page: Page,
  selector: string,
  label: string,
): Promise<boolean> {
  const root = page.locator("#container-mh");
  await root
    .getByText(/Additional Services/i)
    .first()
    .scrollIntoViewIfNeeded()
    .catch(() => undefined);
  await page.waitForTimeout(400);

  const cb = root.locator(selector).first();
  const labelRe = new RegExp(`^${label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "i");

  if (
    (await isFeatheryCheckboxChecked(page, selector)) ||
    (await isFeatheryCardSelected(page, label))
  ) {
    return true;
  }

  /** 人工录制 F01：getByText("Full FTL") 点击 Feathery 卡片 */
  const textHit = root.getByText(label, { exact: true }).first();
  if ((await textHit.count()) > 0) {
    await textHit.scrollIntoViewIfNeeded().catch(() => undefined);
    await textHit.click(CLICK);
    await page.waitForTimeout(500);
    if (await isFeatheryCheckboxChecked(page, selector)) return true;
    if (await isFeatheryCardSelected(page, label)) return true;
  }

  for (let attempt = 0; attempt < 5; attempt += 1) {
    await forceFeatheryCheckbox(page, selector);
    if ((await cb.count()) > 0) {
      await cb.click(CLICK).catch(() => undefined);
    }
    await root.getByText(labelRe).first().click(CLICK).catch(() => undefined);
    await page.waitForTimeout(600);
    if (await isFeatheryCheckboxChecked(page, selector)) return true;
    if (await isFeatheryCardSelected(page, label)) return true;
  }
  return (
    (await isFeatheryCheckboxChecked(page, selector)) ||
    (await isFeatheryCardSelected(page, label))
  );
}

/** LTL Freight Class — 优先可见 #freight-type，按 option 文本匹配并返回实际操作的 locator */
export async function selectFreightClass(
  page: Page,
  freightClass: string,
): Promise<Locator> {
  const root = page.locator("#container-mh");
  await root
    .locator("#freight-type, #freight-class")
    .first()
    .waitFor({ state: "visible", timeout: 10_000 });

  const picked = await page.evaluate(function (expected) {
    const container = document.querySelector("#container-mh");
    if (!container) return null;
    const ids = ["freight-type", "freight-class"];
    for (let idIdx = 0; idIdx < ids.length; idIdx += 1) {
      const el = container.querySelector(
        "#" + ids[idIdx],
      ) as HTMLSelectElement | null;
      if (!el) continue;
      const r = el.getBoundingClientRect();
      if (r.width <= 0 || r.height <= 0) continue;
      for (let i = 0; i < el.options.length; i += 1) {
        const opt = el.options[i];
        if (!opt) continue;
        const text = opt.text.trim();
        const t = text.toLowerCase();
        const w = String(expected).trim().toLowerCase();
        let isMatch = t === w || text === String(expected).trim();
        if (!isMatch && w === "not sure" && t.indexOf("not sure") >= 0) isMatch = true;
        if (!isMatch) continue;
        el.selectedIndex = i;
        el.value = opt.value;
        el.dispatchEvent(new Event("input", { bubbles: true }));
        el.dispatchEvent(new Event("change", { bubbles: true }));
        return { text: text, id: el.id };
      }
    }
    return null;
  }, freightClass);

  const sel = picked?.id
    ? root.locator(`#${picked.id}`).first()
    : root.locator("#freight-type").first();

  if (picked) {
    console.log(`[form-interactions] Freight Class → ${picked.text} (#${picked.id})`);
  } else {
    await sel.selectOption({ label: freightClass }).catch(() => sel.selectOption(freightClass));
  }

  await page.waitForTimeout(400);
  let observed = await readSelectVisibleText(sel);
  if (!freightClassOptionMatches(observed, freightClass)) {
    await sel.selectOption({ label: freightClass }).catch(() => undefined);
    await page.waitForTimeout(400);
    observed = await readSelectVisibleText(sel);
  }
  if (!freightClassOptionMatches(observed, freightClass)) {
    throw new Error(`Freight Class 未能选中 "${freightClass}"（当前: "${observed}"）`);
  }
  return sel;
}

/** LTL 附加服务 label 文本匹配（纯函数，供单测） */
export function ltlAccessorialLabelMatches(
  elementText: string,
  label: string,
): boolean {
  const text = elementText.replace(/\s+/g, " ").trim().toLowerCase();
  const want = label.trim().toLowerCase();
  if (!text || !want) return false;
  return text === want || text.includes(want);
}

/** canonical 标签 → Feathery 页面可见文案 */
export function ltlAccessorialDisplayText(label: string): string {
  if (label === "Limited Access Pickup" || label === "Limited Access Delivery") {
    return "Limited Access";
  }
  return label;
}

/** 附加项属于提货段还是派送段 */
export function ltlAccessorialIsPickupSection(label: string): boolean {
  return /Pickup/i.test(label);
}

async function findLtlAccessorialCheckboxSelector(
  page: Page,
  label: string,
): Promise<string | null> {
  const pageText = ltlAccessorialDisplayText(label);
  const wantPickup = ltlAccessorialIsPickupSection(label);
  return page.evaluate(
    function (args) {
      const container = document.querySelector("#container-mh");
      if (!container) return null;
      const cbs = container.querySelectorAll('input[type="checkbox"]');
      for (let i = 0; i < cbs.length; i += 1) {
        const cb = cbs[i];
        if (!(cb instanceof HTMLInputElement)) continue;
        let labelText = "";
        let node = cb.parentElement;
        for (let depth = 0; depth < 12 && node; depth += 1) {
          const spans = node.querySelectorAll("span");
          for (let s = 0; s < spans.length; s += 1) {
            const t = (spans[s].textContent || "").replace(/\s+/g, " ").trim();
            if (t.length > 2 && t.length < 50) {
              labelText = t;
              depth = 99;
              break;
            }
          }
          node = node.parentElement;
        }
        if (labelText !== args.pageText) continue;
        if (args.pageText === "Limited Access") {
          let limitedSeen = 0;
          for (let j = 0; j <= i; j += 1) {
            const prior = cbs[j];
            if (!(prior instanceof HTMLInputElement)) continue;
            let priorText = "";
            let priorNode = prior.parentElement;
            for (let depth = 0; depth < 12 && priorNode; depth += 1) {
              const spans = priorNode.querySelectorAll("span");
              for (let s = 0; s < spans.length; s += 1) {
                const t = (spans[s].textContent || "").replace(/\s+/g, " ").trim();
                if (t.length > 2 && t.length < 50) {
                  priorText = t;
                  depth = 99;
                  break;
                }
              }
              priorNode = priorNode.parentElement;
            }
            if (priorText === "Limited Access") limitedSeen += 1;
          }
          const inPickup = limitedSeen === 1;
          if (inPickup !== args.wantPickup) continue;
        }
        return cb.id ? "#" + cb.id : null;
      }
      return null;
    },
    { pageText, wantPickup },
  );
}

/** 读取 LTL 附加 checkbox 是否已勾选（Feathery #Checkbox* 隐藏 input） */
export async function isLtlAccessorialChecked(
  page: Page,
  label: string,
): Promise<boolean> {
  const selector = await findLtlAccessorialCheckboxSelector(page, label);
  if (!selector) return false;
  return page.evaluate(function (sel) {
    const root = document.querySelector("#container-mh");
    if (!root) return false;
    const cb = root.querySelector(sel);
    return Boolean(cb && cb instanceof HTMLInputElement && cb.type === "checkbox" && cb.checked);
  }, selector);
}

/** LTL / Expedited 附加 — Feathery checkbox（P08 实机：#Checkbox15 等 + span 文案） */
export async function toggleLtlAccessorial(
  page: Page,
  label: string,
): Promise<boolean> {
  if (await isLtlAccessorialChecked(page, label)) return true;

  const selector = await findLtlAccessorialCheckboxSelector(page, label);
  if (selector) {
    await forceFeatheryCheckbox(page, selector);
    await page.waitForTimeout(300);
    if (await isLtlAccessorialChecked(page, label)) return true;
  }

  const root = page.locator("#container-mh");
  const pageText = ltlAccessorialDisplayText(label);
  const section = ltlAccessorialIsPickupSection(label)
    ? root.filter({ hasText: /Pickup Services/i }).first()
    : root.filter({ hasText: /Delivery Services/i }).first();
  const hit = section.getByText(pageText, { exact: true }).first();
  if ((await hit.count()) > 0) {
    await hit.scrollIntoViewIfNeeded().catch(() => undefined);
    await hit.click(CLICK);
    await page.waitForTimeout(400);
  }

  return isLtlAccessorialChecked(page, label);
}

/** Expedited / FTL 内嵌 Expedited — Large Straight 可选 Liftgate */
export async function selectExpeditedLiftgate(page: Page): Promise<boolean> {
  const root = page.locator("#container-mh");
  const section = root.filter({ hasText: /附加服务|Additional Services/i }).last();
  const lift = section.getByText(/^Liftgate$/i).first();
  if ((await lift.count()) === 0) {
    const fallback = root.getByText(/^Liftgate$/i).first();
    if ((await fallback.count()) === 0) return false;
    await fallback.click(CLICK);
    return true;
  }
  await lift.click(CLICK);
  await page.waitForTimeout(400);
  return true;
}

export async function readSelectVisibleText(locator: Locator): Promise<string> {
  return locator.evaluate(function (el) {
    const sel = el as HTMLSelectElement;
    if (sel.tagName === "SELECT") {
      return sel.options[sel.selectedIndex]?.text?.trim() ?? sel.value;
    }
    return (el as HTMLElement).innerText?.trim() ?? "";
  });
}

export function openDeckSelectionOk(observed: string, expected: string): boolean {
  if (!observed) return false;
  if (/No roof or sides includes/i.test(observed)) return false;
  return (
    optionMatchesSubtype(observed, expected) ||
    /Foot Flatbed|Foot Step Deck|Removable Gooseneck|Double Drop|Conestoga|Hot Shot/i.test(
      observed,
    )
  );
}
