import type { Locator, Page } from "playwright";
import {
  clickFirstVisibleFromSpecs,
  getSelectorSpecs,
} from "@/lib/rpa/selector-specs";
import { pageLocator } from "@/lib/rpa/locator";
import { awaitHumanPacing } from "@/lib/rpa/human-pacing";
import { RpaError } from "@/modules/rpa/errors";
import { fillLocatorWithVerify } from "@/workers/rpa/cargo-field-fill";
import {
  buildWeightDisplayForUnit,
  inferWeightUnitFromFieldHint,
  type MotherShipWeightDisplay,
} from "@/workers/rpa/cargo-weight";
import { dismissCookieConsent } from "@/workers/rpa/page-prep";

const CARGO_EDITOR_OPEN_WAIT_MS = 12_000;
const POLL_MS = 200;

const DRAWER_LOCATORS = [
  '[role="dialog"]',
  '[aria-modal="true"]',
  '[data-state="open"][role="dialog"]',
] as const;

const CARGO_FIELD_PLACEHOLDER_RE =
  /pallet|weight|length|width|height|托盘|重量|长度|宽度|高度/i;

/** MotherShip popover 内常见控件（无 role=dialog） */
function popoverEditorSignals(page: Page): Locator[] {
  return [
    page.getByRole("button", { name: /ADD ITEM/i }),
    page.getByPlaceholder(/weight/i),
    page.getByPlaceholder(/how many pallets/i),
    page.locator('input[placeholder*="Weight" i]'),
    page.locator('input[placeholder*="Pallet" i]'),
    page.getByText(/^ADD ITEM$/i),
  ];
}

function cargoFieldLocators(widget: Locator): Locator[] {
  return [
    widget.getByPlaceholder(/how many pallets/i).first(),
    widget.getByPlaceholder(/需要几个托盘/i).first(),
    widget.getByPlaceholder(/weight/i).first(),
    widget.getByPlaceholder(/重量/i).first(),
    widget.getByPlaceholder(/length|width|height|长度|宽度|高度/i).first(),
    widget.locator('input[placeholder*="Pallet" i]').first(),
    widget.locator('input[placeholder*="Weight" i]').first(),
    widget.locator('input[placeholder*="托盘" i]').first(),
    widget.locator('input[placeholder*="重量" i]').first(),
  ];
}

/** widget 内是否已有可填货物字段（含中文向导内联步） */
export async function isCargoFieldsVisible(
  page: Page,
  widgetSelector: string,
): Promise<boolean> {
  const widget = pageLocator(page, widgetSelector);
  for (const loc of cargoFieldLocators(widget)) {
    if (await loc.isVisible().catch(() => false)) {
      const editable = await loc.isEditable().catch(() => false);
      if (editable) {
        return true;
      }
    }
  }
  return false;
}

/** popover/dialog 或 widget 内联货物字段可见 */
export async function isCargoEditable(
  page: Page,
  widgetSelector: string,
): Promise<boolean> {
  if (await isCargoEditorOpen(page)) {
    return true;
  }
  return isCargoFieldsVisible(page, widgetSelector);
}

/** drawer / popover 是否已打开（popover 以 editor 控件可见为准，非 dialog role） */
export async function isCargoEditorOpen(page: Page): Promise<boolean> {
  for (const selector of DRAWER_LOCATORS) {
    const loc = page.locator(selector).first();
    if (await loc.isVisible().catch(() => false)) {
      return true;
    }
  }
  for (const loc of popoverEditorSignals(page)) {
    if (await loc.first().isVisible().catch(() => false)) {
      return true;
    }
  }
  return false;
}

/** 等待 cargo 可填（popover 或中文内联步） */
export async function waitForCargoEditorOpen(
  page: Page,
  widgetSelector: string,
): Promise<void> {
  if (!(await isCargoUiPresent(page, widgetSelector))) {
    throw new RpaError("STRUCT_CHANGE", "cargo editor UI 不存在，无法等待打开", {
      retryable: false,
    });
  }
  const deadline = Date.now() + CARGO_EDITOR_OPEN_WAIT_MS;
  while (Date.now() < deadline) {
    if (await isCargoFieldsVisible(page, widgetSelector)) {
      return;
    }
    await page.waitForTimeout(POLL_MS);
  }
  if (await isCargoEditable(page, widgetSelector)) {
    return;
  }
  throw new RpaError("STRUCT_CHANGE", "cargo editor 未在时限内就绪", {
    retryable: true,
  });
}

/** 填表作用域：dialog / popover / widget 内联 */
export async function resolveCargoEditorScope(
  page: Page,
  widgetSelector: string,
): Promise<Locator> {
  for (const selector of DRAWER_LOCATORS) {
    const loc = page.locator(selector).last();
    if (await loc.isVisible().catch(() => false)) {
      return loc;
    }
  }
  if (await isCargoFieldsVisible(page, widgetSelector)) {
    return pageLocator(page, widgetSelector);
  }
  if (await isCargoEditorOpen(page)) {
    return page.locator("body");
  }
  throw new RpaError("STRUCT_CHANGE", "cargo editor 未打开，禁止填表", {
    retryable: true,
  });
}

async function clickTrigger(loc: Locator): Promise<boolean> {
  if (!(await loc.isVisible().catch(() => false))) {
    return false;
  }
  await loc.scrollIntoViewIfNeeded().catch(() => undefined);
  await loc.click({ timeout: 8_000, force: true });
  await loc.page().waitForTimeout(500);
  return true;
}

/** widget 上是否存在可编辑的货物 UI（中/英）或可分步展开的货物步骤 */
export async function isCargoUiPresent(
  page: Page,
  widgetSelector: string,
): Promise<boolean> {
  if (await isCargoFieldsVisible(page, widgetSelector)) {
    return true;
  }
  const widget = pageLocator(page, widgetSelector);
  const signals: Locator[] = [
    widget.getByText(/what are you shipping/i).first(),
    widget.getByText(/\d+\s*pallet/i).first(),
    widget.getByText(/\d+\s*托盘/).first(),
    widget.getByText(/\d+W,\s*x\s*\d+L/i).first(),
    widget.getByText(/货运详情/).first(),
    widget.getByText(/Freight details/i).first(),
    widget.getByText(/Shipment details/i).first(),
    widget.getByPlaceholder(/cargo/i).first(),
    widget.getByRole("button", { name: /pallet/i }).first(),
    widget
      .getByRole("button", {
        name: /获取货运报价|GET QUOTE|Get freight quote|Get a freight quote/i,
      })
      .first(),
  ];
  for (const loc of signals) {
    if (await loc.isVisible().catch(() => false)) {
      return true;
    }
  }
  return isCargoWizardPastAddress(page, widgetSelector);
}

/** 双地址已锁定且不再处于地址搜索态 */
export async function isCargoWizardPastAddress(
  page: Page,
  widgetSelector: string,
): Promise<boolean> {
  return page.evaluate((sel) => {
    const widget = document.querySelector(sel);
    if (!widget) {
      return false;
    }
    const text = (
      (widget as HTMLElement).innerText ??
      widget.textContent ??
      ""
    ).replace(/\s+/g, " ");
    const stateMatches = text.match(/,\s*[A-Z]{2}/g) ?? [];
    if (stateMatches.length < 2) {
      return false;
    }
    const searchInputs = widget.querySelectorAll<HTMLInputElement>("input");
    for (const inp of searchInputs) {
      const ph = inp.placeholder ?? "";
      if (!/search|搜索/i.test(ph)) {
        continue;
      }
      const visible = inp.offsetParent !== null;
      const editable =
        !inp.disabled &&
        inp.readOnly === false &&
        inp.getAttribute("aria-readonly") !== "true";
      if (visible && editable) {
        return false;
      }
    }
    return true;
  }, widgetSelector);
}

/** 地址填完后等待货物步出现（MotherShip 渲染有延迟） */
export async function waitForCargoUiPresent(
  page: Page,
  widgetSelector: string,
  timeoutMs = 20_000,
): Promise<void> {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    if (await isCargoUiPresent(page, widgetSelector)) {
      return;
    }
    await dismissCookieConsent(page).catch(() => undefined);
    await page.waitForTimeout(POLL_MS);
  }
  throw new RpaError(
    "QUOTE_ENTRY_UNAVAILABLE",
    "货物编辑 UI 在时限内未出现",
    { retryable: true },
  );
}

async function waitCargoEditableAfterClick(
  page: Page,
  widgetSelector: string,
): Promise<boolean> {
  const deadline = Date.now() + 4_000;
  while (Date.now() < deadline) {
    if (await isCargoEditable(page, widgetSelector)) {
      return true;
    }
    await page.waitForTimeout(POLL_MS);
  }
  return false;
}

async function clickFreightStepInWidget(widget: Locator): Promise<boolean> {
  const labels = ["货运详情", "Freight details", "Shipment details"];
  for (const label of labels) {
    const exact = widget.getByText(label, { exact: true });
    const count = await exact.count().catch(() => 0);
    for (let i = count - 1; i >= 0; i -= 1) {
      const target = exact.nth(i);
      if (await clickTrigger(target)) {
        return true;
      }
      const buttonAncestor = target.locator("xpath=ancestor::button[1]");
      if (await clickTrigger(buttonAncestor)) {
        return true;
      }
      const roleButtonAncestor = target.locator(
        "xpath=ancestor::*[@role='button'][1]",
      );
      if (await clickTrigger(roleButtonAncestor)) {
        return true;
      }
      const parent = target.locator("xpath=..");
      if (await clickTrigger(parent)) {
        return true;
      }
      const grandParent = target.locator("xpath=../..");
      if (await clickTrigger(grandParent)) {
        return true;
      }
    }
  }
  return false;
}

/** 重新展开中文内联货物步（不抛错） */
export async function reopenInlineCargoStep(
  page: Page,
  widgetSelector: string,
): Promise<void> {
  if (await isCargoFieldsVisible(page, widgetSelector)) {
    return;
  }
  const widget = pageLocator(page, widgetSelector);
  await clickFreightStepInWidget(widget);
  await waitCargoEditableAfterClick(page, widgetSelector);
}

/**
 * 点击 summary chip / 步骤标签，展开 cargo editor（popover 或中文内联步）。
 */
export async function expandCargoEditor(
  page: Page,
  widgetSelector: string,
): Promise<void> {
  if (!(await isCargoUiPresent(page, widgetSelector))) {
    throw new RpaError(
      "STRUCT_CHANGE",
      "无法展开 cargo editor：widget 无货物编辑 UI",
      { retryable: false },
    );
  }

  if (await isCargoEditable(page, widgetSelector)) {
    return;
  }

  await dismissCookieConsent(page);

  const widget = pageLocator(page, widgetSelector);

  if (await clickFreightStepInWidget(widget)) {
    if (await waitCargoEditableAfterClick(page, widgetSelector)) {
      console.log("[rpa] cargo-editor: 内联货物步已展开（货运详情）");
      return;
    }
  }

  const triggers: Locator[] = [
    widget.getByRole("button", { name: /\d+\s*Pallet/i }).first(),
    widget.locator("button, [role='button']").filter({ hasText: /Pallet/i }).first(),
    widget.getByText(/^\d+\s*Pallet$/i).first(),
    widget.getByText(/\d+\s*x\s*\d+\s*x\s*\d+/i).first(),
    widget.getByText(/what are you shipping/i).first(),
    widget.getByText(/你在运送什么/i).first(),
    widget.locator("text=/\\d+\\s*Pallet/i").first(),
    widget.getByText(/\d+\s*Pallet.*\d+\s*lbs?/i).first(),
    widget.locator('[class*="summary"], [data-testid*="cargo"], [data-testid*="shipment"]').first(),
  ];

  for (const loc of triggers) {
    if (await clickTrigger(loc)) {
      if (await waitCargoEditableAfterClick(page, widgetSelector)) {
        console.log("[rpa] cargo-editor: popover opened via summary chip");
        return;
      }
    }
  }

  const clicked = await clickFirstVisibleFromSpecs(
    page,
    getSelectorSpecs("RPA_SELECTOR_CARGO_SECTION"),
  );
  if (clicked) {
    await page.waitForTimeout(500);
    if (await waitCargoEditableAfterClick(page, widgetSelector)) {
      console.log("[rpa] cargo-editor: 内联步已展开（CARGO_SECTION spec）");
      return;
    }
  }

  throw new RpaError(
    "STRUCT_CHANGE",
    "无法展开 cargo editor（步骤标签/chip 未响应）",
    { retryable: true },
  );
}

function resolveSpecOnScope(scope: Locator, spec: string): Locator {
  const trimmed = spec.trim();
  if (trimmed.startsWith("placeholder=")) {
    const ph = trimmed.slice(12).replace(/^"|"$/g, "");
    return scope.getByPlaceholder(ph);
  }
  const roleMatch = trimmed.match(/^role=(\w+)(?:\[name="([^"]*)"\])?$/);
  if (roleMatch) {
    const [, role, name] = roleMatch;
    return name
      ? scope.getByRole(role as Parameters<Locator["getByRole"]>[0], { name })
      : scope.getByRole(role as Parameters<Locator["getByRole"]>[0]);
  }
  if (trimmed.startsWith("text=")) {
    const text = trimmed.slice(5);
    if (text.startsWith("/") && text.endsWith("/")) {
      return scope.getByText(new RegExp(text.slice(1, -1)));
    }
    return scope.getByText(text);
  }
  return scope.locator(trimmed);
}

const CARGO_FIELD_WAIT_MS = 20_000;

async function waitForFieldLocator(loc: Locator): Promise<boolean> {
  const deadline = Date.now() + CARGO_FIELD_WAIT_MS;
  while (Date.now() < deadline) {
    const visible = await loc.isVisible().catch(() => false);
    const editable = visible && (await loc.isEditable().catch(() => false));
    if (editable) {
      return true;
    }
    await loc.page().waitForTimeout(POLL_MS);
  }
  return false;
}

/** 在已打开的 editor / 内联步内按 spec 填值 */
export async function fillInCargoEditor(
  page: Page,
  envKey: string,
  value: string,
  widgetSelector: string,
  options?: { commit?: boolean; forceClear?: boolean },
): Promise<boolean> {
  if (!(await isCargoFieldsVisible(page, widgetSelector))) {
    await reopenInlineCargoStep(page, widgetSelector);
  }

  const specs = getSelectorSpecs(envKey);
  const scopes: Locator[] = [];

  if (await isCargoFieldsVisible(page, widgetSelector)) {
    scopes.push(pageLocator(page, widgetSelector));
  } else {
    if (await isCargoUiPresent(page, widgetSelector)) {
      await reopenInlineCargoStep(page, widgetSelector);
    }
    if (await isCargoFieldsVisible(page, widgetSelector)) {
      scopes.push(pageLocator(page, widgetSelector));
    } else {
      const scope = await resolveCargoEditorScope(page, widgetSelector);
      scopes.push(scope);
    }
  }

  for (const fillScope of scopes) {
    for (const spec of specs) {
      const loc = resolveSpecOnScope(fillScope, spec).first();
      if (!(await waitForFieldLocator(loc))) {
        continue;
      }
      try {
        await fillLocatorWithVerify(page, loc, value, {
          forceClear: options?.forceClear,
          commit: options?.commit ?? false,
        });
        await awaitHumanPacing(page);
        return true;
      } catch {
        /* 尝试下一 spec */
      }
    }
  }

  return false;
}

/** 按实际可见 weight 输入框 placeholder 决定填入值并填写 */
export async function fillWeightInCargoEditor(
  page: Page,
  widgetSelector: string,
  weightLbPerPallet: number,
  options?: { commit?: boolean; forceClear?: boolean },
): Promise<MotherShipWeightDisplay> {
  if (!(await isCargoFieldsVisible(page, widgetSelector))) {
    await reopenInlineCargoStep(page, widgetSelector);
  }

  const specs = getSelectorSpecs("RPA_SELECTOR_WEIGHT");
  const scopes: Locator[] = [];

  if (await isCargoFieldsVisible(page, widgetSelector)) {
    scopes.push(pageLocator(page, widgetSelector));
  } else {
    if (await isCargoUiPresent(page, widgetSelector)) {
      await reopenInlineCargoStep(page, widgetSelector);
    }
    if (await isCargoFieldsVisible(page, widgetSelector)) {
      scopes.push(pageLocator(page, widgetSelector));
    } else {
      scopes.push(await resolveCargoEditorScope(page, widgetSelector));
    }
  }
  scopes.push(page.locator("body"));

  let lastError: unknown;
  for (const fillScope of scopes) {
    for (const spec of specs) {
      const loc = resolveSpecOnScope(fillScope, spec).first();
      if (!(await waitForFieldLocator(loc))) {
        continue;
      }
      const hint = `${(await loc.getAttribute("placeholder")) ?? ""} ${(await loc.getAttribute("aria-label")) ?? ""}`;
      const display = buildWeightDisplayForUnit(
        weightLbPerPallet,
        inferWeightUnitFromFieldHint(hint),
      );
      try {
        await fillLocatorWithVerify(page, loc, display.displayValue, {
          forceClear: options?.forceClear,
          commit: options?.commit ?? true,
        });
        await awaitHumanPacing(page);
        return display;
      } catch (error) {
        lastError = error;
      }
    }
  }

  if (lastError instanceof RpaError) {
    throw lastError;
  }

  throw new RpaError(
    "STRUCT_CHANGE",
    "货物重量字段填写失败（drawer 内）：RPA_SELECTOR_WEIGHT",
    { retryable: true },
  );
}

/** @internal 单测 */
export function cargoPlaceholderMatches(placeholder: string): boolean {
  return CARGO_FIELD_PLACEHOLDER_RE.test(placeholder);
}
