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

import type { LlmMatchedSearchCandidate } from "./fixed-search-llm.ts";

const mocks = vi.hoisted(() => ({
  scanOfferContactsByNavigation: vi.fn(),
  close: vi.fn(async () => {}),
}));

vi.mock("playwright", () => ({
  chromium: {
    launchPersistentContext: vi.fn(async () => ({
      close: mocks.close,
      pages: () => [],
    })),
  },
}));

vi.mock("../playwright/contact-page-navigation.js", () => ({
  scanOfferContactsByNavigation: mocks.scanOfferContactsByNavigation,
}));

vi.mock("../playwright/human.js", () => ({
  attachVerificationMonitor: () => ({ dispose: vi.fn() }),
}));

import { enrichSelectedOfferCandidates, normalizeLlmMatchJsonCandidates } from "./offer-contact-enrichment.ts";

function buildCandidate(index: number): LlmMatchedSearchCandidate {
  return {
    input: "地质罗盘 dql-8A",
    "title-text": `候选商品 ${index}`,
    url: `https://detail.1688.com/offer/${index}.html`,
    score: 100 - index,
    is_match: true,
    reason: "匹配",
    search_round: 1,
    round_label: "主搜索词",
    source_product: {
      title: `候选商品 ${index}`,
      offer_url: `https://detail.1688.com/offer/${index}.html`,
      merchant_name: `商家 ${index}`,
      merchant_url: `https://shop${index}.1688.com`,
      image_url: "",
      wangwang_url: "",
      contact_page_url: "",
      offer_screenshot_path: "",
    },
  };
}

describe("enrichSelectedOfferCandidates", () => {
  it("keeps scanning LLM matches until the requested number of phone-qualified merchants is collected", async () => {
    mocks.scanOfferContactsByNavigation.mockReset();
    mocks.close.mockReset();
    mocks.scanOfferContactsByNavigation.mockImplementation(async (_context, params) => {
      const offerId = Number(String(params.offerUrl).match(/offer\/(\d+)\.html/)?.[1] ?? 0);
      return {
        preferredTitle: "hO7TX2rRhkJDVqeFGucSXQ|aetU-Q|BUpWdg_0|By-uiQ|0",
        screenshotPath: `/tmp/offer-${offerId}.jpg`,
        contactPageUrl: `https://shop${offerId}.1688.com/contact`,
        offerScreenshotStatus: "ok",
        contactFetchStatus: "ok",
        contactFetchMethod: "direct_contact_url",
        reason: offerId <= 2 ? "未检测到完整电话" : "检测到完整电话",
        detectedContacts: offerId === 1
          ? [{ type: "mobile", value: "86" }]
          : offerId === 2
            ? []
            : [{ type: "mobile", value: `1380000000${offerId}`, source: "contact_page" }],
        contactPageContacts: offerId <= 2
          ? []
          : [{ type: "mobile", value: `1380000000${offerId}`, source: "contact_page" }],
      };
    });

    const result = await enrichSelectedOfferCandidates({
      candidates: Array.from({ length: 8 }, (_, index) => buildCandidate(index + 1)),
      screenshotDir: "/tmp",
      headless: true,
      targetQualifiedCount: 5,
    });
    const enriched = result.enriched;

    expect(enriched).toHaveLength(5);
    expect(enriched.map((item) => item.url)).toEqual([
      "https://detail.1688.com/offer/3.html",
      "https://detail.1688.com/offer/4.html",
      "https://detail.1688.com/offer/5.html",
      "https://detail.1688.com/offer/6.html",
      "https://detail.1688.com/offer/7.html",
    ]);
    expect(enriched.map((item) => item.title)).toEqual([
      "候选商品 3",
      "候选商品 4",
      "候选商品 5",
      "候选商品 6",
      "候选商品 7",
    ]);
    expect(enriched.map((item) => item.merchant_name)).toEqual([
      "商家 3",
      "商家 4",
      "商家 5",
      "商家 6",
      "商家 7",
    ]);
    expect(enriched.every((item) => item.has_phone_contact)).toBe(true);
    expect(enriched.map((item) => item.index)).toEqual([1, 2, 3, 4, 5]);
    expect(result.attempts.map((item) => item.qualification_status)).toEqual([
      "rejected_no_phone",
      "rejected_no_phone",
      "qualified",
      "qualified",
      "qualified",
      "qualified",
      "qualified",
    ]);
    expect(result.initialWindowSize).toBe(5);
    expect(result.attempts.map((item) => item.phase)).toEqual([
      "initial_window",
      "initial_window",
      "initial_window",
      "initial_window",
      "initial_window",
      "top_up",
      "top_up",
    ]);
    expect(mocks.scanOfferContactsByNavigation).toHaveBeenCalledTimes(7);
    expect(mocks.close).toHaveBeenCalledTimes(1);
  });

  it("stops immediately after enough phone-qualified merchants are collected", async () => {
    mocks.scanOfferContactsByNavigation.mockReset();
    mocks.close.mockReset();
    mocks.scanOfferContactsByNavigation.mockImplementation(async (_context, params) => {
      const offerId = Number(String(params.offerUrl).match(/offer\/(\d+)\.html/)?.[1] ?? 0);
      return {
        preferredTitle: `详情标题 ${offerId}`,
        screenshotPath: `/tmp/offer-${offerId}.jpg`,
        contactPageUrl: `https://shop${offerId}.1688.com/contact`,
        offerScreenshotStatus: "ok",
        contactFetchStatus: "ok",
        contactFetchMethod: "direct_contact_url",
        reason: "检测到完整电话",
        detectedContacts: [{ type: "phone", value: `0571-8888666${offerId}`, source: "contact_page" }],
        contactPageContacts: [{ type: "phone", value: `0571-8888666${offerId}`, source: "contact_page" }],
      };
    });

    const result = await enrichSelectedOfferCandidates({
      candidates: Array.from({ length: 8 }, (_, index) => buildCandidate(index + 1)),
      screenshotDir: "/tmp",
      headless: true,
      targetQualifiedCount: 5,
    });
    const enriched = result.enriched;

    expect(enriched).toHaveLength(5);
    expect(enriched.map((item) => item.url)).toEqual([
      "https://detail.1688.com/offer/1.html",
      "https://detail.1688.com/offer/2.html",
      "https://detail.1688.com/offer/3.html",
      "https://detail.1688.com/offer/4.html",
      "https://detail.1688.com/offer/5.html",
    ]);
    expect(mocks.scanOfferContactsByNavigation).toHaveBeenCalledTimes(5);
    expect(mocks.close).toHaveBeenCalledTimes(1);
  });

  it("does not fail the whole enrichment when one candidate throws", async () => {
    mocks.scanOfferContactsByNavigation.mockReset();
    mocks.close.mockReset();
    mocks.scanOfferContactsByNavigation.mockImplementation(async (_context, params) => {
      const offerId = Number(String(params.offerUrl).match(/offer\/(\d+)\.html/)?.[1] ?? 0);
      if (offerId === 1) {
        throw new Error("detail_timeout");
      }
      return {
        preferredTitle: `详情标题 ${offerId}`,
        screenshotPath: `/tmp/offer-${offerId}.jpg`,
        contactPageUrl: `https://shop${offerId}.1688.com/contact`,
        offerScreenshotStatus: "ok",
        contactFetchStatus: "ok",
        contactFetchMethod: "direct_contact_url",
        reason: "检测到完整电话",
        detectedContacts: [{ type: "mobile", value: `1380000000${offerId}`, source: "contact_page" }],
        contactPageContacts: [{ type: "mobile", value: `1380000000${offerId}`, source: "contact_page" }],
      };
    });

    const result = await enrichSelectedOfferCandidates({
      candidates: Array.from({ length: 4 }, (_, index) => buildCandidate(index + 1)),
      screenshotDir: "/tmp",
      headless: true,
      targetQualifiedCount: 2,
    });

    expect(result.enriched.map((item) => item.url)).toEqual([
      "https://detail.1688.com/offer/2.html",
      "https://detail.1688.com/offer/3.html",
    ]);
    expect(result.attempts[0]).toMatchObject({
      qualification_status: "rejected_contact_error",
      error_message: "detail_timeout",
    });
    expect(mocks.scanOfferContactsByNavigation).toHaveBeenCalledTimes(3);
  });

  it("passes merchant url for direct contact-page navigation and does not qualify product-detail-only contacts", async () => {
    mocks.scanOfferContactsByNavigation.mockReset();
    mocks.close.mockReset();
    mocks.scanOfferContactsByNavigation.mockImplementation(async (_context, params) => ({
      preferredTitle: "详情标题",
      screenshotPath: params.screenshotPath,
      contactPageUrl: "",
      offerScreenshotStatus: "ok",
      contactFetchStatus: "no_contacts",
      contactFetchMethod: "direct_contact_url",
      reason: "contact_page_no_contacts",
      detectedContacts: [{ type: "mobile", value: "13800138000", source: "product_detail" }],
      detailContacts: [{ type: "mobile", value: "13800138000", source: "product_detail" }],
      contactPageContacts: [],
    }));

    const result = await enrichSelectedOfferCandidates({
      candidates: [buildCandidate(1), buildCandidate(2)],
      screenshotDir: "/tmp",
      headless: true,
      targetQualifiedCount: 1,
    });

    expect(result.enriched).toHaveLength(0);
    expect(result.attempts).toHaveLength(2);
    expect(result.attempts.every((item) => item.qualification_status === "rejected_no_phone")).toBe(true);
    expect(mocks.scanOfferContactsByNavigation).toHaveBeenNthCalledWith(
      1,
      expect.anything(),
      expect.objectContaining({
        offerUrl: "https://detail.1688.com/offer/1.html",
        merchantUrl: "https://shop1.1688.com",
      }),
    );
  });

  it("prefers the real wangwang url captured from the offer detail customer-service entry", async () => {
    mocks.scanOfferContactsByNavigation.mockReset();
    mocks.close.mockReset();
    const candidate = buildCandidate(1);
    candidate.source_product.wangwang_url = "https://air.1688.com/app/ocms-fusion-components-1688/def_cbu_web_im/index.html?uid=old&site=cnalichn";
    mocks.scanOfferContactsByNavigation.mockResolvedValue({
      preferredTitle: "详情标题",
      screenshotPath: "/tmp/offer-1.jpg",
      contactPageUrl: "https://shop1.1688.com/contact",
      offerScreenshotStatus: "ok",
      contactFetchStatus: "ok",
      contactFetchMethod: "direct_contact_url",
      wangwangUrl: "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#/",
      wangwangFetchStatus: "ok",
      wangwangFetchMethod: "new_page",
      wangwangFetchReason: "",
      reason: "检测到完整电话",
      detectedContacts: [{ type: "mobile", value: "13800000001", source: "contact_page" }],
      contactPageContacts: [{ type: "mobile", value: "13800000001", source: "contact_page" }],
    });

    const result = await enrichSelectedOfferCandidates({
      candidates: [candidate],
      screenshotDir: "/tmp",
      headless: true,
      targetQualifiedCount: 1,
    });

    expect(result.enriched[0]?.wangwang_url).toContain("touid=cnalichn%E6%BA%90%E4%B8%B0%E5%A1%91%E8%83%B6sj");
    expect(result.attempts[0]).toMatchObject({
      wangwang_url: expect.stringContaining("touid=cnalichn%E6%BA%90%E4%B8%B0%E5%A1%91%E8%83%B6sj"),
      wangwang_original_url: "https://air.1688.com/app/ocms-fusion-components-1688/def_cbu_web_im/index.html?uid=old&site=cnalichn",
      wangwangFetchStatus: "ok",
      wangwangFetchMethod: "new_page",
    });
  });

  it("keeps the fallback wangwang url when the detail-page click does not produce a real url", async () => {
    mocks.scanOfferContactsByNavigation.mockReset();
    mocks.close.mockReset();
    const candidate = buildCandidate(1);
    candidate.source_product.wangwang_url = "https://air.1688.com/app/ocms-fusion-components-1688/def_cbu_web_im/index.html?uid=old&site=cnalichn";
    mocks.scanOfferContactsByNavigation.mockResolvedValue({
      preferredTitle: "详情标题",
      screenshotPath: "/tmp/offer-1.jpg",
      contactPageUrl: "https://shop1.1688.com/contact",
      offerScreenshotStatus: "ok",
      contactFetchStatus: "ok",
      contactFetchMethod: "direct_contact_url",
      wangwangUrl: "",
      wangwangFetchStatus: "not_found",
      wangwangFetchMethod: "",
      wangwangFetchReason: "wangwang_url_not_found_after_click",
      reason: "检测到完整电话",
      detectedContacts: [{ type: "mobile", value: "13800000001", source: "contact_page" }],
      contactPageContacts: [{ type: "mobile", value: "13800000001", source: "contact_page" }],
    });

    const result = await enrichSelectedOfferCandidates({
      candidates: [candidate],
      screenshotDir: "/tmp",
      headless: true,
      targetQualifiedCount: 1,
    });

    expect(result.enriched[0]?.wangwang_url).toBe("https://air.1688.com/app/ocms-fusion-components-1688/def_cbu_web_im/index.html?uid=old&site=cnalichn");
    expect(result.attempts[0]).toMatchObject({
      wangwang_url: "https://air.1688.com/app/ocms-fusion-components-1688/def_cbu_web_im/index.html?uid=old&site=cnalichn",
      wangwang_original_url: "https://air.1688.com/app/ocms-fusion-components-1688/def_cbu_web_im/index.html?uid=old&site=cnalichn",
      wangwangFetchStatus: "not_found",
      wangwangFetchReason: "wangwang_url_not_found_after_click",
    });
  });

  it("does not retain requested screenshot path when offer captcha prevents screenshot capture", async () => {
    mocks.scanOfferContactsByNavigation.mockReset();
    mocks.close.mockReset();
    mocks.scanOfferContactsByNavigation.mockResolvedValue({
      preferredTitle: "",
      screenshotPath: "",
      contactPageUrl: "",
      offerScreenshotStatus: "captcha_unresolved",
      contactFetchStatus: "skipped",
      contactFetchMethod: "",
      reason: "offer_detail_captcha_unresolved",
      detectedContacts: [],
      detailContacts: [],
      contactPageContacts: [],
    });

    const result = await enrichSelectedOfferCandidates({
      candidates: [buildCandidate(1)],
      screenshotDir: "/tmp",
      headless: true,
      targetQualifiedCount: 1,
    });

    expect(result.enriched).toHaveLength(0);
    expect(result.attempts[0]).toMatchObject({
      screenshot_path: "",
      offerScreenshotStatus: "captcha_unresolved",
      contactFetchStatus: "skipped",
      qualification_status: "rejected_no_phone",
      contact_collection_reason: "offer_detail_captcha_unresolved",
    });
  });

  it("normalizes LLM match JSON into recoverable candidates", () => {
    const candidates = normalizeLlmMatchJsonCandidates([
      {
        input: "电磁阀",
        "title-text": "4V210 电磁阀",
        url: "https://detail.1688.com/offer/1.html",
        score: 88,
        is_match: true,
        reason: "型号匹配",
        search_round: 2,
        round_label: "标准化名称+规格",
        source_product: {
          merchant_name: "商家 A",
          merchant_url: "https://shop1.1688.com",
          image_url: "https://img.example.com/a.jpg",
          wangwang_url: "https://amos.example.com/a",
        },
      },
    ]);

    expect(candidates).toHaveLength(1);
    expect(candidates[0]).toMatchObject({
      input: "电磁阀",
      url: "https://detail.1688.com/offer/1.html",
      is_match: true,
      source_product: {
        title: "4V210 电磁阀",
        offer_url: "https://detail.1688.com/offer/1.html",
        merchant_name: "商家 A",
      },
    });
  });
});
