import type { Locator, Page } from "playwright";
import { isRpaAddressMockMode } from "@/lib/rpa/address-mode";
import {
  firstVisibleFromSpecs,
  getSelectorSpecs,
} from "@/lib/rpa/selector-specs";
import { RpaError } from "@/modules/rpa/errors";
import {
  isAddressSearchEditable,
  isAddressesLockedForSubmit,
  isCargoCommittedState,
} from "@/workers/rpa/cargo-lock";
import { dismissCookieConsent } from "@/workers/rpa/page-prep";
import type { QuoteSubmitOptions } from "@/workers/rpa/quote-capture/types";

const WIDGET_SELECTOR = "#react-quoter-landing-plugin";
const BUSINESS_READY_TIMEOUT_MS = 30_000;
const POLL_MS = 300;
const DOM_STABLE_MS = 600;

const COOKIE_OVERLAY_SELECTORS = [
  "#cookieyes",
  ".cky-consent-container",
  ".cky-modal",
  '[id*="cookie" i]',
  '[class*="cookie-banner" i]',
  '[class*="privacy-banner" i]',
] as const;

export type BusinessReadyProbe = {
  buttonEnabled: boolean;
  cookieClear: boolean;
  cargoLocked: boolean;
  hydrationReady: boolean;
  addressesLocked: boolean;
  submitHitTestOk: boolean;
  domStable: boolean;
  reasons: string[];
};

export type BusinessReadySnapshot = BusinessReadyProbe & {
  ready: boolean;
  url: string;
  submitLabel: string | null;
  widgetTextPreview: string;
};

export function evaluateBusinessReadyGates(
  probe: Omit<BusinessReadyProbe, "reasons">,
): BusinessReadyProbe {
  const reasons: string[] = [];
  if (!probe.hydrationReady) {
    reasons.push("hydration 未完成");
  }
  if (!probe.domStable) {
    reasons.push("DOM 未稳定");
  }
  if (!probe.addressesLocked) {
    reasons.push("地址未锁定");
  }
  if (!probe.cargoLocked) {
    reasons.push("货物状态未锁定");
  }
  if (!probe.buttonEnabled) {
    reasons.push("提交按钮 disabled");
  }
  if (!probe.cookieClear) {
    reasons.push("Cookie 横幅遮挡");
  }
  if (!probe.submitHitTestOk) {
    reasons.push("提交按钮 pointer 被拦截");
  }
  return { ...probe, reasons };
}

export function isBusinessReady(
  probe: BusinessReadyProbe,
  options?: QuoteSubmitOptions,
): boolean {
  const addressesOk =
    options?.skipAddressLock || probe.addressesLocked;
  return (
    probe.buttonEnabled &&
    probe.cookieClear &&
    probe.cargoLocked &&
    probe.hydrationReady &&
    addressesOk &&
    probe.submitHitTestOk &&
    probe.domStable
  );
}

type WidgetDomProbe = {
  hydrated: boolean;
  addressesLocked: boolean;
  cargoLocked: boolean;
  widgetTextPreview: string;
};

async function probeWidgetDom(page: Page): Promise<WidgetDomProbe> {
  return page.evaluate((widgetSel) => {
    const widget = document.querySelector(widgetSel);
    if (!widget) {
      return {
        hydrated: false,
        addressesLocked: false,
        cargoLocked: false,
        widgetTextPreview: "",
      };
    }

    const text = (widget.textContent ?? "").replace(/\s+/g, " ").trim();
    const innerText = (
      (widget as HTMLElement).innerText ?? widget.textContent ?? ""
    )
      .replace(/\s+/g, " ")
      .trim();
    const probeText = innerText || text;
    const zipMatches = probeText.match(/\b\d{5}(?:-\d{4})?\b/g) ?? [];
    const hasTwoZips = zipMatches.length >= 2;
    const stateMatches = probeText.match(/,\s*[A-Z]{2}/g) ?? [];
    const hasTwoStatePairs = stateMatches.length >= 2;

    const searchInputs = widget.querySelectorAll<HTMLInputElement>(
      'input[placeholder*="Search" i], input[placeholder*="搜索"]',
    );
    let editableSearch = 0;
    for (const inp of searchInputs) {
      const visible = inp.offsetParent !== null;
      const editable =
        !inp.disabled &&
        inp.readOnly === false &&
        inp.getAttribute("aria-readonly") !== "true";
      if (visible && editable) {
        editableSearch += 1;
      }
    }

    const cargoInputs = widget.querySelectorAll<HTMLInputElement>(
      'input[placeholder*="Weight" i], input[placeholder*="Pallet" i], input[placeholder*="重量"], input[placeholder*="托盘"]',
    );
    let visibleCargoInputs = 0;
    for (const inp of cargoInputs) {
      if (inp.offsetParent !== null) {
        visibleCargoInputs += 1;
      }
    }

    const modalOpen = Boolean(
      document.querySelector(
        '[role="dialog"][aria-modal="true"], [aria-modal="true"][data-state="open"]',
      ),
    );

    const hasPalletSummary =
      /\d+\s*托盘s?/i.test(probeText) ||
      /\d+\s*pallets?/i.test(probeText) ||
      /\d+\s*lbs?\b/i.test(probeText) ||
      /\d+\s*公斤/.test(probeText) ||
      /\d+\s*x\s*\d+\s*x\s*\d+/i.test(probeText) ||
      /\d+W,\s*x\s*\d+L/i.test(probeText) ||
      Boolean(
        widget.querySelector<HTMLInputElement>(
          'input[placeholder*="托盘"], input[placeholder*="Pallet" i]',
        )?.value,
      );

    const addressesLocked =
      (hasTwoZips || hasTwoStatePairs) &&
      (editableSearch === 0 || hasPalletSummary);

    const cargoLocked = hasPalletSummary;

    const hydrated =
      widget.children.length > 0 && probeText.length > 24;

    return {
      hydrated,
      addressesLocked,
      cargoLocked,
      widgetTextPreview: probeText.slice(0, 220),
    };
  }, WIDGET_SELECTOR);
}

async function isCookieOverlayVisible(page: Page): Promise<boolean> {
  for (const sel of COOKIE_OVERLAY_SELECTORS) {
    const loc = page.locator(sel).first();
    if (!(await loc.isVisible().catch(() => false))) {
      continue;
    }
    const box = await loc.boundingBox().catch(() => null);
    if (box && box.width > 40 && box.height > 40) {
      return true;
    }
  }
  return false;
}

async function isSubmitButtonEnabled(submit: Locator): Promise<boolean> {
  if (!(await submit.isVisible().catch(() => false))) {
    return false;
  }
  const disabled = await submit.isDisabled().catch(() => true);
  if (disabled) {
    return false;
  }
  const ariaDisabled = await submit.getAttribute("aria-disabled");
  if (ariaDisabled === "true") {
    return false;
  }
  const nativeDisabled = await submit.getAttribute("disabled");
  return nativeDisabled === null;
}

async function isSubmitHitTestClear(
  page: Page,
  submit: Locator,
): Promise<boolean> {
  const box = await submit.boundingBox().catch(() => null);
  if (!box) {
    return false;
  }
  const x = box.x + box.width / 2;
  const y = box.y + box.height / 2;
  return submit.evaluate(
    (btn, point) => {
      const top = document.elementFromPoint(point.x, point.y);
      if (!top) {
        return false;
      }
      const cookieHit = Boolean(
        top.closest(
          '#cookieyes, .cky-consent-container, .cky-modal, [id*="cookie" i], [class*="cookie-banner" i]',
        ),
      );
      if (cookieHit) {
        return false;
      }
      return btn === top || btn.contains(top);
    },
    { x, y },
  );
}

async function waitDomStable(page: Page): Promise<boolean> {
  const first = await probeWidgetDom(page);
  await page.waitForTimeout(DOM_STABLE_MS);
  const second = await probeWidgetDom(page);
  return (
    first.widgetTextPreview === second.widgetTextPreview &&
    first.hydrated === second.hydrated
  );
}

async function resolveSubmitButton(page: Page): Promise<Locator | null> {
  return firstVisibleFromSpecs(page, getSelectorSpecs("RPA_SELECTOR_SUBMIT"));
}

/** 采集 business-ready 各闸门快照（不等待） */
export async function debugBusinessStateSnapshot(
  page: Page,
  options?: QuoteSubmitOptions,
): Promise<BusinessReadySnapshot> {
  await dismissCookieConsent(page).catch(() => undefined);

  const submit = await resolveSubmitButton(page);
  const widgetDom = await probeWidgetDom(page);
  const domStable = await waitDomStable(page);

  const buttonEnabled = submit ? await isSubmitButtonEnabled(submit) : false;
  const cookieOverlay = await isCookieOverlayVisible(page);
  const cookieClear = !cookieOverlay;
  const submitHitTestOk =
    submit && cookieClear
      ? await isSubmitHitTestClear(page, submit)
      : false;

  const submitLabel = submit
    ? await submit.innerText().catch(() => null)
    : null;

  const cargoLocked = await isCargoCommittedState(page, WIDGET_SELECTOR);
  const addressSearchOpen = await isAddressSearchEditable(page, WIDGET_SELECTOR);
  const addressesVisible = await isAddressesLockedForSubmit(page, WIDGET_SELECTOR);
  const addressesLocked = isRpaAddressMockMode() || options?.skipAddressLock
    ? cargoLocked
    : !addressSearchOpen && addressesVisible;

  const probe = evaluateBusinessReadyGates({
    buttonEnabled,
    cookieClear,
    cargoLocked,
    hydrationReady: widgetDom.hydrated,
    addressesLocked,
    submitHitTestOk,
    domStable,
  });

  const ready = isBusinessReady(probe, options);

  return {
    ...probe,
    ready,
    url: page.url(),
    submitLabel,
    widgetTextPreview: widgetDom.widgetTextPreview,
  };
}

/**
 * submit 前闸门：四项就绪 + DOM 稳定；未就绪则轮询 dismiss cookie 直至超时。
 */
export async function waitForBusinessReadyState(
  page: Page,
  options?: QuoteSubmitOptions,
): Promise<BusinessReadySnapshot> {
  const deadline = Date.now() + BUSINESS_READY_TIMEOUT_MS;
  let last: BusinessReadySnapshot | null = null;

  while (Date.now() < deadline) {
    await dismissCookieConsent(page).catch(() => undefined);
    const snapshot = await debugBusinessStateSnapshot(page, options);
    last = snapshot;

    if (snapshot.ready) {
      console.log(
        `[rpa] business-ready: ok url=${snapshot.url} submit="${snapshot.submitLabel ?? ""}"`,
      );
      return snapshot;
    }

    await page.waitForTimeout(POLL_MS);
  }

  const failed = last ?? (await debugBusinessStateSnapshot(page));
  console.log(
    `[rpa] business-ready: FAIL ${JSON.stringify({
      url: failed.url,
      reasons: failed.reasons,
      buttonEnabled: failed.buttonEnabled,
      cookieClear: failed.cookieClear,
      cargoLocked: failed.cargoLocked,
      hydrationReady: failed.hydrationReady,
      addressesLocked: failed.addressesLocked,
      submitHitTestOk: failed.submitHitTestOk,
      domStable: failed.domStable,
      widgetTextPreview: failed.widgetTextPreview,
    })}`,
  );

  throw new RpaError(
    "QUOTE_ENTRY_UNAVAILABLE",
    `报价表单未达 business-ready（${failed.reasons.join("；")}）`,
    { retryable: true },
  );
}
