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[] = [];
  closed = false;
  hasOnlineChatButton = false;
  mouse = {
    move: async () => {},
    down: async () => {},
    up: async () => {
      await this.clickMatchedLocator("mouse");
    },
  };

  constructor(
    private readonly mode: "captcha" | "detail" | "contact_primary_same_page" | "contact_backup_popup" | "contact_no_chat",
    private readonly context?: FakeContext,
    initialUrl?: string,
  ) {
    if (mode === "captcha") {
      this.currentUrl = "https://detail.1688.com/punish";
      this.bodyText = "验证码拦截 亲，请拖动下方滑块完成验证 通过验证以确保正常访问";
    } else if (initialUrl?.includes("def_cbu_web_im")) {
      this.currentUrl = initialUrl;
      this.bodyText = "聊天设置 下载客户端 唤起客户端 请输入消息 发送";
    } 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")) {
        this.bodyText = "联系方式 测试商家 电话：暂无 手机：13800138000 传真：暂无 地址：测试地址 在线沟通";
        this.hasOnlineChatButton = this.mode !== "contact_no_chat";
      } else {
        this.bodyText = "岩棉板 TR10 容重100kg 商品详情 价格 起批 主图";
        this.hasOnlineChatButton = false;
      }
    }
    return { ok: () => true, status: () => 200 };
  }

  async waitForLoadState() {}

  async waitForFunction() {
    if (this.mode !== "captcha") {
      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.hasOnlineChatButton, "backup_xpath");
    }
    return new FakeLocator(this, false);
  }

  async waitForURL() {}

  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#/",
      );
    }
  }

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

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

  constructor(mode: "captcha" | "detail" | "contact_primary_same_page" | "contact_backup_popup" | "contact_no_chat") {
    this.page = new FakePage(mode, this);
  }

  async newPage() {
    return this.page;
  }

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

  openPopup(url: string) {
    const popup = new FakePage("detail", this, url);
    this.popupPages.push(popup);
    return popup;
  }
}

beforeEach(() => {
  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("does not screenshot or continue to contact page when offer captcha remains unresolved", async () => {
    mocks.waitForVerificationClear.mockResolvedValue(false);
    const context = new FakeContext("captcha");

    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: "offer_detail_captcha_unresolved",
      screenshotPath: "",
      offerScreenshotStatus: "captcha_unresolved",
      contactFetchStatus: "skipped",
    });
    expect(context.page.screenshotCalls).toEqual([]);
    expect(context.page.gotoCalls).toEqual(["https://detail.1688.com/offer/1.html"]);
  });

  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"]);
  });

  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("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("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");
  });
});
