import { beforeEach, describe, expect, it, vi } from "vitest";

const mocks = vi.hoisted(() => ({
  waitForVerificationClear: vi.fn(),
  stepPause: vi.fn(async () => {}),
  randomPause: vi.fn(async () => {}),
  humanClick: vi.fn(async () => {}),
  waitForManualVerificationIfNeeded: vi.fn(async () => false),
}));

const PRIMARY_ONLINE_CHAT_XPATH = "xpath=/html/body/div[2]/div/div/div[3]/div[2]/div/div/div[2]/div[1]/div[4]/div[2]";
const BACKUP_WANGWANG_ICON_XPATH = 'xpath=//*[@id="hd_0_container_0"]/div[1]/div[2]/div/div/div[1]/div[1]/div/div[2]/div/a/img';

vi.mock("./human.js", () => ({
  humanClick: mocks.humanClick,
  randomPause: mocks.randomPause,
  stepPause: mocks.stepPause,
  waitForManualVerificationIfNeeded: mocks.waitForManualVerificationIfNeeded,
  waitForVerificationClear: mocks.waitForVerificationClear,
}));

import { pickPreferredDetailTitle, scanOfferContactsByNavigation } from "./contact-page-navigation.js";

class FakeFrame {
  constructor(private readonly page: FakePage) {}

  url() {
    return this.page.url();
  }

  async evaluate() {
    return this.page.bodyText;
  }
}

class FakeLocator {
  constructor(
    private readonly page: FakePage,
    private readonly matched: boolean,
    private readonly source: "text" | "primary_xpath" | "backup_xpath" | "" = "",
  ) {}

  first() {
    return this;
  }

  filter(options?: { hasText?: RegExp }) {
    const pattern = options?.hasText;
    const matched = this.page.hasOnlineChatButton
      && Boolean(pattern && ["在线沟通", "旺旺在线", "立即沟通", "联系客服"].some((text) => pattern.test(text)));
    return new FakeLocator(this.page, matched, "text");
  }

  async count() {
    return this.matched ? 1 : 0;
  }

  async isVisible() {
    return this.matched;
  }

  async boundingBox() {
    if (!this.matched) {
      return null;
    }
    if (this.source === "backup_xpath") {
      return { x: 488, y: 126, width: 14, height: 14 };
    }
    return { x: 714, y: 506, width: 116, height: 30 };
  }

  async click() {
    await this.page.clickMatchedLocator(this.source);
  }
}

class FakePage {
  bodyText = "";
  currentUrl = "about:blank";
  gotoCalls: string[] = [];
  screenshotCalls: string[] = [];
  waitForURLTimeouts: number[] = [];
  closed = false;
  hasOnlineChatButton = false;
  mouse = {
    move: async () => {},
    down: async () => {},
    up: async () => {
      await this.clickMatchedLocator("mouse");
    },
  };

  constructor(
    private readonly mode: "captcha" | "detail" | "detail_not_ready_contact" | "title_only" | "contact_primary_same_page" | "contact_backup_popup" | "contact_no_chat" | "contact_token_captcha" | "contact_captcha_then_ready" | "contact_captcha_twice_then_ready" | "contact_captcha_twice_unresolved" | "contact_backup_popup_captcha_then_ready" | "contact_backup_popup_captcha_twice_then_ready",
    private readonly context?: FakeContext,
    initialUrl?: string,
  ) {
    if (mode === "captcha") {
      this.currentUrl = "https://detail.1688.com/punish";
      this.bodyText = "验证码拦截 亲，请拖动下方滑块完成验证 通过验证以确保正常访问";
    } else if ((mode === "contact_backup_popup_captcha_then_ready" || mode === "contact_backup_popup_captcha_twice_then_ready") && initialUrl?.includes("def_cbu_web_im")) {
      this.currentUrl = "https://air.1688.com/punish?x5step=1";
      this.bodyText = "安全验证 请拖动下方滑块完成验证 x5secdata=Ij9RnZgdg";
    } else if (initialUrl?.includes("def_cbu_web_im")) {
      this.currentUrl = initialUrl;
      this.bodyText = "聊天设置 下载客户端 唤起客户端 请输入消息 发送";
    } else if (mode === "detail_not_ready_contact") {
      this.currentUrl = initialUrl ?? "https://detail.1688.com/offer/1.html";
      this.bodyText = "页面加载中";
    } else if (mode === "title_only") {
      this.currentUrl = initialUrl ?? "https://detail.1688.com/offer/1.html";
      this.bodyText = "岩棉板 TR10 容重100kg";
    } else {
      this.currentUrl = initialUrl ?? "https://detail.1688.com/offer/1.html";
      this.bodyText = "岩棉板 TR10 容重100kg 商品详情 价格 起批 主图";
    }
  }

  isClosed() {
    return this.closed;
  }

  frames() {
    return [new FakeFrame(this)];
  }

  url() {
    return this.currentUrl;
  }

  async goto(url: string) {
    this.gotoCalls.push(url);
    if (this.mode !== "captcha") {
      this.currentUrl = url;
      if (url.includes("contactinfo.htm")) {
        if (this.mode === "contact_token_captcha") {
          this.currentUrl = `${url}&x5step=1`;
          this.bodyText = "安全验证 请拖动下方滑块完成验证 微信：Ij9RnZgdg&x5secdata=xg9ef77b1fe0e";
        } else if (this.mode === "contact_captcha_then_ready" || this.mode === "contact_captcha_twice_then_ready" || this.mode === "contact_captcha_twice_unresolved") {
          this.currentUrl = `${url}&x5step=1`;
          this.bodyText = "安全验证 请拖动下方滑块完成验证 x5secdata=Ij9RnZgdg";
        } else {
          this.bodyText = "联系方式 测试商家 电话：暂无 手机：13800138000 传真：暂无 地址：测试地址 在线沟通";
        }
        this.hasOnlineChatButton = this.mode !== "contact_no_chat";
      } else if (this.mode === "title_only") {
        this.bodyText = "岩棉板 TR10 容重100kg";
        this.hasOnlineChatButton = false;
      } else {
        this.bodyText = "岩棉板 TR10 容重100kg 商品详情 价格 起批 主图";
        this.hasOnlineChatButton = false;
      }
    }
    return { ok: () => true, status: () => 200 };
  }

  async waitForLoadState() {}

  async waitForFunction(_fn?: unknown, arg?: unknown) {
    if (arg && typeof arg === "object" && "readyPatternSource" in arg) {
      const pattern = new RegExp(String((arg as { readyPatternSource?: unknown }).readyPatternSource ?? ""), "i");
      const looksLikeVerification = this.currentUrl.includes("/punish") || /验证码|滑块|验证|x5secdata/.test(this.bodyText);
      if (!looksLikeVerification && this.mode !== "detail_not_ready_contact" && pattern.test(this.bodyText)) {
        return true;
      }
      throw new Error("not ready");
    }

    if (arg && typeof arg === "object" && "contactReadyPatternSource" in arg) {
      const pattern = new RegExp(String((arg as { contactReadyPatternSource?: unknown }).contactReadyPatternSource ?? ""), "i");
      const looksLikeVerification = this.currentUrl.includes("/punish") || /验证码|滑块|验证|x5secdata/.test(this.bodyText);
      if (!looksLikeVerification && pattern.test(this.bodyText)) {
        return true;
      }
      throw new Error("contact not ready");
    }

    if (this.mode !== "captcha" && this.mode !== "detail_not_ready_contact") {
      return true;
    }
    throw new Error("not ready");
  }

  async evaluate(_fn: unknown, arg?: unknown) {
    if (Array.isArray(arg)) {
      return this.mode !== "captcha" ? ["岩棉板 TR10 容重100kg"] : [];
    }
    if (arg && typeof arg === "object" && Array.isArray((arg as { signals?: unknown }).signals)) {
      return ((arg as { signals: string[] }).signals).filter((signal) => this.bodyText.includes(signal));
    }
    return this.bodyText;
  }

  async screenshot(options: { path: string }) {
    this.screenshotCalls.push(options.path);
  }

  locator(selector?: string) {
    if (selector === PRIMARY_ONLINE_CHAT_XPATH) {
      return new FakeLocator(this, this.mode === "contact_primary_same_page" && this.hasOnlineChatButton, "primary_xpath");
    }
    if (selector === BACKUP_WANGWANG_ICON_XPATH) {
      return new FakeLocator(this, (this.mode === "contact_backup_popup" || this.mode === "contact_backup_popup_captcha_then_ready" || this.mode === "contact_backup_popup_captcha_twice_then_ready") && this.hasOnlineChatButton, "backup_xpath");
    }
    return new FakeLocator(this, false);
  }

  async waitForURL(_predicate?: unknown, options?: { timeout?: number }) {
    if (typeof options?.timeout === "number") {
      this.waitForURLTimeouts.push(options.timeout);
    }
  }

  async clickMatchedLocator(_source: string) {
    if (this.mode === "contact_primary_same_page") {
      this.currentUrl = "https://air.1688.com/app/ocms-fusion-components-1688/def_cbu_web_im/index.html?touid=cnalichn%E6%BA%90%E4%B8%B0%E5%A1%91%E8%83%B6sj&siteid=cnalichn&status=1#/";
      this.bodyText = "聊天设置 下载客户端 唤起客户端 请输入消息 发送";
      return;
    }

    if (this.mode === "contact_backup_popup") {
      this.context?.openPopup(
        "https://air.1688.com/app/ocms-fusion-components-1688/def_cbu_web_im/index.html?touid=cnalichn%E6%BA%90%E4%B8%B0%E5%A1%91%E8%83%B6sj&siteid=cnalichn&status=1#/",
      );
    }

    if (this.mode === "contact_backup_popup_captcha_then_ready" || this.mode === "contact_backup_popup_captcha_twice_then_ready") {
      this.context?.openPopup(
        "https://air.1688.com/app/ocms-fusion-components-1688/def_cbu_web_im/index.html?touid=cnalichn%E6%BA%90%E4%B8%B0%E5%A1%91%E8%83%B6sj&siteid=cnalichn&status=1#/",
      );
    }
  }

  async close() {
    this.closed = true;
  }
}

class FakeContext {
  page: FakePage;
  popupPages: FakePage[] = [];
  newPageCalls = 0;

  constructor(mode: "captcha" | "detail" | "detail_not_ready_contact" | "title_only" | "contact_primary_same_page" | "contact_backup_popup" | "contact_no_chat" | "contact_token_captcha" | "contact_captcha_then_ready" | "contact_captcha_twice_then_ready" | "contact_captcha_twice_unresolved" | "contact_backup_popup_captcha_then_ready" | "contact_backup_popup_captcha_twice_then_ready") {
    this.page = new FakePage(mode, this);
  }

  async newPage() {
    this.newPageCalls += 1;
    return this.page;
  }

  pages() {
    return [this.page, ...this.popupPages];
  }

  openPopup(url: string) {
    const popup = new FakePage(
      this.page["mode"] === "contact_backup_popup_captcha_then_ready"
        ? "contact_backup_popup_captcha_then_ready"
        : this.page["mode"] === "contact_backup_popup_captcha_twice_then_ready"
          ? "contact_backup_popup_captcha_twice_then_ready"
          : "detail",
      this,
      url,
    );
    this.popupPages.push(popup);
    return popup;
  }
}

beforeEach(() => {
  delete process.env.SEARCH_HEADLESS;
  process.env.SEARCH_WANGWANG_TIMEOUT_MS = "1000";
  mocks.waitForVerificationClear.mockReset();
  mocks.stepPause.mockClear();
  mocks.randomPause.mockClear();
  mocks.humanClick.mockReset();
  mocks.humanClick.mockImplementation(async (_page, locator) => {
    await locator.click?.();
  });
  mocks.waitForManualVerificationIfNeeded.mockClear();
});

describe("pickPreferredDetailTitle", () => {
  it("prefers the detail page title when the search card title is interaction prompt copy", () => {
    expect(
      pickPreferredDetailTitle(
        "点击可以直接和卖家交流选好的宝贝，或者互动购网站体验，还支持语音视频哦。",
        "国标BV1.5 2.5 4 6平方单芯铜芯电线阻燃防水电源线电缆线批发",
        "鸿海线缆有限公司",
      ),
    ).toBe("国标BV1.5 2.5 4 6平方单芯铜芯电线阻燃防水电源线电缆线批发");
  });

  it("keeps the current title when the detail title is empty or lower quality", () => {
    expect(
      pickPreferredDetailTitle(
        "BVR 2.5平方单芯软铜线蓝 珠江工",
        "",
        "鸿海线缆有限公司",
      ),
    ).toBe("BVR 2.5平方单芯软铜线蓝 珠江工");
  });

  it("rejects generated resource identifiers from detail title diagnostics", () => {
    expect(
      pickPreferredDetailTitle(
        "1kV冷缩电缆终端头 120mm2 铝芯",
        "hO7TX2rRhkJDVqeFGucSXQ|aetU-Q|BUpWdg_0|By-uiQ|0",
        "",
      ),
    ).toBe("1kV冷缩电缆终端头 120mm2 铝芯");
  });
});

describe("scanOfferContactsByNavigation screenshots", () => {
  it("uses the provided page for the first LLM-selected offer instead of opening a fresh tab", async () => {
    mocks.waitForVerificationClear.mockResolvedValue(true);
    const context = new FakeContext("detail");

    const result = await scanOfferContactsByNavigation(context as never, {
      page: context.page,
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    } as never);

    expect(result.offerScreenshotStatus).toBe("ok");
    expect(context.newPageCalls).toBe(0);
    expect(context.page.closed).toBe(false);
  });

  it("keeps the offer detail scan alive until the captcha clears", async () => {
    const context = new FakeContext("captcha");
    mocks.waitForVerificationClear.mockImplementation(async (page) => {
      page.currentUrl = "https://detail.1688.com/offer/1.html";
      page.bodyText = "岩棉板 TR10 容重100kg 商品详情 价格 起批 主图";
      return true;
    });

    const result = await scanOfferContactsByNavigation(context as never, {
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "https://shop1.1688.com",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    });

    expect(result).toMatchObject({
      screenshotPath: "/tmp/offer-1.jpg",
      offerScreenshotStatus: "ok",
    });
    expect(context.page.screenshotCalls).toEqual(["/tmp/offer-1.jpg"]);
    expect(context.page.gotoCalls[0]).toBe("https://detail.1688.com/offer/1.html");
    expect(mocks.waitForVerificationClear).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
      manualOnAutoVerificationFailure: true,
    }));
  });

  it("routes headless offer captcha into manual handling instead of marking it unresolved", async () => {
    process.env.SEARCH_HEADLESS = "1";
    const context = new FakeContext("captcha");
    mocks.waitForVerificationClear.mockImplementation(async (page) => {
      page.currentUrl = "https://detail.1688.com/offer/1.html";
      page.bodyText = "岩棉板 TR10 容重100kg 商品详情 价格 起批 主图";
      return true;
    });

    const result = await scanOfferContactsByNavigation(context as never, {
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "https://shop1.1688.com",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    });

    expect(result).toMatchObject({
      screenshotPath: "/tmp/offer-1.jpg",
      offerScreenshotStatus: "ok",
    });
    expect(context.page.screenshotCalls).toEqual(["/tmp/offer-1.jpg"]);
    expect(mocks.waitForVerificationClear).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
      manualOnAutoVerificationFailure: true,
    }));
  });

  it("waits for manual captcha resolution before screenshotting offer detail", async () => {
    const context = new FakeContext("captcha");
    mocks.waitForVerificationClear.mockImplementation(async () => {
      context.page.currentUrl = "https://detail.1688.com/offer/1.html";
      context.page.bodyText = "岩棉板 TR10 容重100kg 商品详情 价格 起批 主图";
      return true;
    });

    const result = await scanOfferContactsByNavigation(context as never, {
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    });

    expect(result.offerScreenshotStatus).toBe("ok");
    expect(result.screenshotPath).toBe("/tmp/offer-1.jpg");
    expect(context.page.screenshotCalls).toEqual(["/tmp/offer-1.jpg"]);
    expect(mocks.waitForVerificationClear).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
      manualOnAutoVerificationFailure: true,
    }));
  });

  it("screenshots only after captcha is clear and offer detail content is ready", async () => {
    mocks.waitForVerificationClear.mockResolvedValue(true);
    const context = new FakeContext("detail");

    const result = await scanOfferContactsByNavigation(context as never, {
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    });

    expect(result.offerScreenshotStatus).toBe("ok");
    expect(result.screenshotPath).toBe("/tmp/offer-1.jpg");
    expect(result.wangwangUrl).toBe("");
    expect(result.wangwangFetchStatus).toBe("skipped");
    expect(context.page.screenshotCalls).toEqual(["/tmp/offer-1.jpg"]);
    expect(mocks.waitForVerificationClear).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
      manualOnAutoVerificationFailure: true,
    }));
  });

  it("does not treat title-only residual content as offer detail ready", async () => {
    mocks.waitForVerificationClear.mockResolvedValue(false);
    const context = new FakeContext("title_only");

    const result = await scanOfferContactsByNavigation(context as never, {
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    });

    expect(result.offerScreenshotStatus).toBe("detail_not_ready");
    expect(result.screenshotPath).toBe("");
    expect(context.page.screenshotCalls).toEqual([]);
  });

  it("continues to the direct contact page when offer detail screenshot is not ready", async () => {
    mocks.waitForVerificationClear.mockResolvedValue(true);
    const context = new FakeContext("detail_not_ready_contact");

    const result = await scanOfferContactsByNavigation(context as never, {
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "https://shop1.1688.com",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    });

    expect(result).toMatchObject({
      status: "ok",
      reason: "offer_detail_not_ready",
      screenshotPath: "",
      offerScreenshotStatus: "detail_not_ready",
      contactFetchStatus: "ok",
      contactFetchMethod: "direct_contact_url",
    });
    expect(result.contactPageUrl).toContain("contactinfo.htm");
    expect(result.contactPageContacts).toEqual(expect.arrayContaining([
      expect.objectContaining({ type: "mobile", value: "13800138000", source: "contact_page" }),
    ]));
    expect(context.page.screenshotCalls).toEqual([]);
    expect(context.page.gotoCalls[0]).toBe("https://detail.1688.com/offer/1.html");
    expect(context.page.gotoCalls[1]).toContain("https://shop1.1688.com/page/contactinfo.htm");
  });

  it("does not extract captcha token fragments as contact page contacts", async () => {
    const context = new FakeContext("contact_token_captcha");
    mocks.waitForVerificationClear.mockImplementation(async (page) => {
      if (String(page.url()).includes("contactinfo.htm")) {
        page.currentUrl = "https://shop1.1688.com/page/contactinfo.htm";
        page.bodyText = "联系方式 测试商家 电话：暂无 手机： 传真：暂无 地址：测试地址 在线沟通";
      }
      return true;
    });

    const result = await scanOfferContactsByNavigation(context as never, {
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "https://shop1.1688.com",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    });

    expect(result).toMatchObject({
      status: "error",
      reason: "contact_page_no_contacts",
      contactFetchStatus: "no_contacts",
      contactFetchMethod: "direct_contact_url",
    });
    expect(result.contactPageContacts).toEqual([]);
    expect(result.detectedContacts).not.toEqual(expect.arrayContaining([
      expect.objectContaining({ type: "wechat", value: "Ij9RnZgdg" }),
    ]));
  });

  it("waits for contact page captcha resolution before extracting phone contacts", async () => {
    const context = new FakeContext("contact_captcha_then_ready");
    mocks.waitForVerificationClear.mockImplementation(async (page) => {
      if (String(page.url()).includes("contactinfo.htm")) {
        page.currentUrl = "https://shop1.1688.com/page/contactinfo.htm";
        page.bodyText = "联系方式 测试商家 电话：86 020 62253872-604 手机：18520735369 传真：86 020 62253872-607 地址：测试地址 在线沟通";
      }
      return true;
    });

    const result = await scanOfferContactsByNavigation(context as never, {
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "https://shop1.1688.com",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    });

    expect(result).toMatchObject({
      status: "ok",
      contactFetchStatus: "ok",
      contactFetchMethod: "direct_contact_url",
    });
    expect(result.contactPageContacts).toEqual(expect.arrayContaining([
      expect.objectContaining({ type: "mobile", value: "18520735369", source: "contact_page" }),
      expect.objectContaining({ type: "phone", value: "020-62253872", source: "contact_page" }),
    ]));
  });

  it("keeps waiting through consecutive contact page captchas before extracting phone contacts", async () => {
    const context = new FakeContext("contact_captcha_twice_then_ready");
    const contactVerificationCounts = new WeakMap<object, number>();
    mocks.waitForVerificationClear.mockImplementation(async (page) => {
      if (!String(page.url()).includes("contactinfo.htm")) {
        return true;
      }

      const count = contactVerificationCounts.get(page) ?? 0;
      contactVerificationCounts.set(page, count + 1);
      if (count === 0) {
        page.currentUrl = "https://shop1.1688.com/page/contactinfo.htm?x5step=2";
        page.bodyText = "安全验证 第二层验证码 请按住滑块 x5secdata=second";
        return true;
      }

      page.currentUrl = "https://shop1.1688.com/page/contactinfo.htm";
      page.bodyText = "联系方式 测试商家 电话：86 020 62253872-604 手机：18520735369 传真：86 020 62253872-607 地址：测试地址 在线沟通";
      return true;
    });

    const result = await scanOfferContactsByNavigation(context as never, {
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "https://shop1.1688.com",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    });

    expect(result).toMatchObject({
      status: "ok",
      contactFetchStatus: "ok",
      contactFetchMethod: "direct_contact_url",
    });
    expect(result.contactPageContacts).toEqual(expect.arrayContaining([
      expect.objectContaining({ type: "mobile", value: "18520735369", source: "contact_page" }),
      expect.objectContaining({ type: "phone", value: "020-62253872", source: "contact_page" }),
    ]));
    expect(contactVerificationCounts.get(context.page)).toBe(2);
  });

  it("keeps waiting beyond five consecutive contact page captchas before extracting phone contacts", async () => {
    const context = new FakeContext("contact_captcha_twice_unresolved");
    const contactVerificationCounts = new WeakMap<object, number>();
    mocks.waitForVerificationClear.mockImplementation(async (page) => {
      if (!String(page.url()).includes("contactinfo.htm")) {
        return true;
      }

      const count = contactVerificationCounts.get(page) ?? 0;
      contactVerificationCounts.set(page, count + 1);
      if (count < 6) {
        page.currentUrl = `https://shop1.1688.com/page/contactinfo.htm?x5step=${count + 2}`;
        page.bodyText = `安全验证 第${count + 2}层验证码 请按住滑块 x5secdata=still-blocked`;
        return true;
      }
      page.currentUrl = "https://shop1.1688.com/page/contactinfo.htm";
      page.bodyText = "联系方式 测试商家 电话：86 020 62253872-604 手机：18520735369 传真：86 020 62253872-607 地址：测试地址 在线沟通";
      return true;
    });

    const result = await scanOfferContactsByNavigation(context as never, {
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "https://shop1.1688.com",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    });

    expect(result).toMatchObject({
      status: "ok",
      contactFetchStatus: "ok",
      contactFetchMethod: "direct_contact_url",
    });
    expect(result.contactPageContacts).toEqual(expect.arrayContaining([
      expect.objectContaining({ type: "mobile", value: "18520735369", source: "contact_page" }),
      expect.objectContaining({ type: "phone", value: "020-62253872", source: "contact_page" }),
    ]));
    expect(contactVerificationCounts.get(context.page)).toBeGreaterThan(5);
  });

  it("captures the real wangwang url from the contact page when online chat opens in the same page", async () => {
    mocks.waitForVerificationClear.mockResolvedValue(true);
    const context = new FakeContext("contact_primary_same_page");

    const result = await scanOfferContactsByNavigation(context as never, {
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "https://shop1.1688.com",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    });

    expect(result.offerScreenshotStatus).toBe("ok");
    expect(result.contactPageUrl).toContain("contactinfo.htm");
    expect(result.contactPageContacts).toEqual(expect.arrayContaining([
      expect.objectContaining({ type: "mobile", value: "13800138000", source: "contact_page" }),
    ]));
    expect(result.wangwangUrl).toContain("touid=cnalichn%E6%BA%90%E4%B8%B0%E5%A1%91%E8%83%B6sj");
    expect(result.wangwangFetchStatus).toBe("ok");
    expect(result.wangwangFetchMethod).toBe("same_page");
    expect(result.wangwangChatPageLoaded).toBe(true);
    expect(result.wangwangChatPageSignals).toEqual(expect.arrayContaining(["聊天设置", "下载客户端"]));
    expect(result.wangwangUrl).toContain("siteid=cnalichn");
    expect(result.wangwangUrl).toContain("#/");
  });

  it("uses the dedicated longer wangwang wait window even when contact scanning timeout is short", async () => {
    process.env.SEARCH_WANGWANG_TIMEOUT_MS = "25000";
    mocks.waitForVerificationClear.mockResolvedValue(true);
    const context = new FakeContext("contact_primary_same_page");

    const result = await scanOfferContactsByNavigation(context as never, {
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "https://shop1.1688.com",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    });

    expect(result.wangwangFetchStatus).toBe("ok");
    expect(context.page.waitForURLTimeouts).toContain(5000);
  });

  it("captures the real wangwang url from the backup wangwang icon when it opens a new page", async () => {
    mocks.waitForVerificationClear.mockResolvedValue(true);
    const context = new FakeContext("contact_backup_popup");

    const result = await scanOfferContactsByNavigation(context as never, {
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "https://shop1.1688.com",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    });

    expect(result.offerScreenshotStatus).toBe("ok");
    expect(result.wangwangUrl).toContain("touid=cnalichn%E6%BA%90%E4%B8%B0%E5%A1%91%E8%83%B6sj");
    expect(result.wangwangFetchStatus).toBe("ok");
    expect(result.wangwangFetchMethod).toBe("new_page");
    expect(result.wangwangChatPageLoaded).toBe(true);
    expect(result.wangwangChatPageSignals).toEqual(expect.arrayContaining(["聊天设置", "下载客户端"]));
    expect(result.wangwangUrl).toContain("siteid=cnalichn");
    expect(result.wangwangUrl).toContain("#/");
    expect(context.popupPages[0]?.closed).toBe(true);
  });

  it("waits for wangwang popup captcha resolution before accepting the chat url", async () => {
    const context = new FakeContext("contact_backup_popup_captcha_then_ready");
    mocks.waitForVerificationClear.mockImplementation(async (page) => {
      if (String(page.url()).includes("air.1688.com")) {
        page.currentUrl = "https://air.1688.com/app/ocms-fusion-components-1688/def_cbu_web_im/index.html?touid=cnalichn%E6%BA%90%E4%B8%B0%E5%A1%91%E8%83%B6sj&siteid=cnalichn&status=1#/";
        page.bodyText = "聊天设置 下载客户端 唤起客户端 请输入消息 发送";
      }
      return true;
    });

    const result = await scanOfferContactsByNavigation(context as never, {
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "https://shop1.1688.com",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    });

    expect(result.wangwangUrl).toContain("touid=cnalichn%E6%BA%90%E4%B8%B0%E5%A1%91%E8%83%B6sj");
    expect(result.wangwangFetchStatus).toBe("ok");
    expect(result.wangwangFetchMethod).toBe("new_page");
    expect(result.wangwangChatPageLoaded).toBe(true);
    expect(result.wangwangChatPageSignals).toEqual(expect.arrayContaining(["聊天设置", "下载客户端"]));
  });

  it("keeps waiting through consecutive wangwang popup captchas before accepting the chat url", async () => {
    const context = new FakeContext("contact_backup_popup_captcha_twice_then_ready");
    const wangwangVerificationCounts = new WeakMap<object, number>();
    mocks.waitForVerificationClear.mockImplementation(async (page) => {
      if (!String(page.url()).includes("air.1688.com")) {
        return true;
      }

      const count = wangwangVerificationCounts.get(page) ?? 0;
      wangwangVerificationCounts.set(page, count + 1);
      if (count === 0) {
        page.currentUrl = "https://air.1688.com/punish?x5step=2";
        page.bodyText = "安全验证 第二层旺旺验证码 请按住滑块 x5secdata=second";
        return true;
      }

      page.currentUrl = "https://air.1688.com/app/ocms-fusion-components-1688/def_cbu_web_im/index.html?touid=cnalichn%E6%BA%90%E4%B8%B0%E5%A1%91%E8%83%B6sj&siteid=cnalichn&status=1#/";
      page.bodyText = "聊天设置 下载客户端 唤起客户端 请输入消息 发送";
      return true;
    });

    const result = await scanOfferContactsByNavigation(context as never, {
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "https://shop1.1688.com",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    });

    expect(result.wangwangUrl).toContain("touid=cnalichn%E6%BA%90%E4%B8%B0%E5%A1%91%E8%83%B6sj");
    expect(result.wangwangFetchStatus).toBe("ok");
    expect(result.wangwangFetchMethod).toBe("new_page");
    expect(result.wangwangChatPageLoaded).toBe(true);
    expect(result.wangwangChatPageSignals).toEqual(expect.arrayContaining(["聊天设置", "下载客户端"]));
    expect(wangwangVerificationCounts.get(context.popupPages[0]!)).toBeGreaterThanOrEqual(2);
  });

  it("keeps contact data and reports skipped wangwang capture when the contact page has no online chat button", async () => {
    mocks.waitForVerificationClear.mockResolvedValue(true);
    const context = new FakeContext("contact_no_chat");

    const result = await scanOfferContactsByNavigation(context as never, {
      offerUrl: "https://detail.1688.com/offer/1.html",
      merchantUrl: "https://shop1.1688.com",
      timeoutMs: 1000,
      screenshotPath: "/tmp/offer-1.jpg",
      merchantName: "测试商家",
      currentTitle: "岩棉板 TR10 容重100kg",
    });

    expect(result.contactPageContacts).toEqual(expect.arrayContaining([
      expect.objectContaining({ type: "mobile", value: "13800138000", source: "contact_page" }),
    ]));
    expect(result.wangwangUrl).toBe("");
    expect(result.wangwangFetchStatus).toBe("skipped");
    expect(result.wangwangChatPageLoaded).toBe(false);
    expect(result.wangwangFetchReason).toBe("wangwang_entry_missing");
  });
});
