/**
 * chat-init.js
 *
 * 首轮批量发送入口 — 从 Supabase 读取采购清单 → 搜索 1688 → 创建对话任务 → 发送 Round 1 话术。
 * 发送完成后由 chat-worker.js 接管后续巡检与多轮推进。
 *
 * 用法：
 *   node ./src/chat-init.js
 *
 * 环境变量 (.env)：
 *   SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY (必填，兼容 VITE_SUPABASE_SERVICE_ROLE_KEY)
 *   RESULT_LIMIT        搜索结果数量上限 (默认 10)
 *   HEADLESS            无头模式 (默认 0)
 *   KEEP_BROWSER_OPEN   运行完保持浏览器 (默认 0)
 */

import { existsSync } from "node:fs";
import { mkdir } from "node:fs/promises";
import path from "node:path";
import process from "node:process";
import { fileURLToPath } from "node:url";
import { chromium } from "playwright";

import { attachVerificationMonitor, humanScrollPage, humanType, randomPause, randomBetween, antiDetectPause } from "./human.js";
import { chatPause } from "./human.js";
import { normalizeOfferRecord } from "./offer-normalizer.js";
import { getMessageForRound } from "./chat-templates.js";
import { initChatTasks, markSent, logMessage, fetchPendingSends } from "./chat-scheduler.js";
import { contactMerchant, getMessageHash, isLoginUrl, promptForManualLogin } from "./wangwang-rpa.js";
import { createResolvedSupabaseClient } from "./supabase-client.js";
import { build1688OfferSearchUrl } from "../../automation/playwright/search-results-url-utils.js";

// ── 路径 & .env ──────────────────────────────────────

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const PLAYWRIGHT_DIR = path.resolve(__dirname, "..");

const envPaths = [
  path.resolve(process.cwd(), ".env"),
  path.resolve(PLAYWRIGHT_DIR, ".env"),
];
for (const p of envPaths) {
  if (existsSync(p)) {
    const dotenv = await import("dotenv");
    dotenv.config({ path: p });
    break;
  }
}

// ── 配置 ─────────────────────────────────────────────

const PROFILE_DIR = path.join(PLAYWRIGHT_DIR, ".browser", "1688-profile");
const RESULT_LIMIT = Number.parseInt(process.env.RESULT_LIMIT ?? "10", 10);
const HEADLESS = process.env.HEADLESS === "1";
const KEEP_BROWSER_OPEN = process.env.KEEP_BROWSER_OPEN === "1";

// ── Supabase ─────────────────────────────────────────

function initSupabase() {
  return createResolvedSupabaseClient(process.env);
}

async function fetchMaterials(supabase) {
  const { data, error } = await supabase
    .from("procurement_materials")
    .select("id, batch_id, material_name, specification, quantity, unit, brand, technical_standard, material_requirement")
    .order("created_at", { ascending: true });

  if (error) throw new Error(`读取 procurement_materials 失败: ${error.message}`);
  if (!data || data.length === 0) throw new Error("procurement_materials 表为空");

  return data;
}

// ── 搜索相关 (复用自 run-supabase.js) ────────────────

function normalizeUrl(url) {
  if (!url) return "";
  if (url.startsWith("//")) return `https:${url}`;
  return url;
}

function looksLikeTitle(text) {
  return (
    text.length >= 8 &&
    !/^¥/.test(text) &&
    !text.includes("回头率") &&
    !text.includes("退货包运费") &&
    !text.includes("包邮") &&
    !text.includes("榜第") &&
    !text.includes("先采后付") &&
    !text.includes("满") &&
    !text.includes("件")
  );
}

async function findVisibleSearchBox(page, timeoutMs = 12_000) {
  const startedAt = Date.now();
  while (Date.now() - startedAt < timeoutMs) {
    const locator = page.locator('input[type="text"], input:not([type])');
    const total = await locator.count();
    for (let i = 0; i < total; i++) {
      const candidate = locator.nth(i);
      if (await candidate.isVisible().catch(() => false)) return candidate;
    }
    await randomPause(200, 400);
  }
  return null;
}

async function waitForSearchResultsPage(context, fallbackPage) {
  for (let attempt = 0; attempt < 40; attempt++) {
    const target =
      context.pages().find((p) => p.url().includes("/offer_search.htm")) ??
      (fallbackPage.url().includes("/offer_search.htm") ? fallbackPage : null);
    if (target) {
      await target.bringToFront().catch(() => {});
      return target;
    }
    await randomPause(250, 450);
  }
  throw new Error("未检测到 1688 搜索结果页");
}

async function attemptHomeSearch(context, page, keyword) {
  const searchBox = await findVisibleSearchBox(page);
  if (!searchBox) return { resultsPage: null, loginPage: null };

  await humanType(page, searchBox, keyword);
  await randomPause(200, 500);
  await page.keyboard.press("Enter");
  await antiDetectPause();

  const loginPage = context.pages().find((p) => isLoginUrl(p.url())) ?? null;
  if (loginPage) return { resultsPage: null, loginPage };

  const resultsPage = await waitForSearchResultsPage(context, page).catch(() => null);
  return { resultsPage, loginPage: null };
}

async function openSearchResults(context, keyword, retryAfterLogin = true) {
  const page = context.pages()[0] ?? (await context.newPage());
  await page.goto("https://www.1688.com/", { waitUntil: "domcontentloaded" });
  await page.setViewportSize({ width: 1440, height: 960 });
  await antiDetectPause();

  const firstAttempt = await attemptHomeSearch(context, page, keyword);
  if (firstAttempt.resultsPage) {
    await firstAttempt.resultsPage.waitForLoadState("domcontentloaded");
    await antiDetectPause();
    return firstAttempt.resultsPage;
  }

  if (firstAttempt.loginPage && retryAfterLogin) {
    await promptForManualLogin(firstAttempt.loginPage, "https://www.1688.com/");
    return openSearchResults(context, keyword, false);
  }

  const fallbackPage = context.pages()[0] ?? page;
  const fallbackUrl = build1688OfferSearchUrl(keyword);
  await fallbackPage.goto(fallbackUrl, { waitUntil: "domcontentloaded" });

  if (isLoginUrl(fallbackPage.url()) && retryAfterLogin) {
    await promptForManualLogin(fallbackPage, fallbackUrl);
    await fallbackPage.goto(fallbackUrl, { waitUntil: "domcontentloaded" });
  }

  await fallbackPage.waitForLoadState("domcontentloaded");
  await antiDetectPause();
  return fallbackPage;
}

async function extractTopResults(page, limit) {
  await page.waitForSelector('a[href*="offerId="]', { timeout: 30_000 });
  await humanScrollPage(page, 3);

  const rawResults = await page.evaluate((take) => {
    const anchors = Array.from(document.querySelectorAll("a[href]"));
    const items = [];
    const seen = new Set();

    function isOfferLink(link) {
      return (
        link.href.includes("detail.m.1688.com/page/index.html") ||
        link.href.includes("detail.1688.com/offer/")
      );
    }

    function isMerchantLink(link) {
      return (
        link.href.includes(".1688.com/") &&
        !link.href.includes("offerId=") &&
        !link.href.includes("def_cbu_web_im") &&
        !link.href.includes("similar_search") &&
        !link.href.includes("fcaReport") &&
        !link.href.includes("auth/")
      );
    }

    function clean(text) {
      return (text ?? "").replace(/\s+/g, " ").trim();
    }

    function looksLikeDescendantTitle(value) {
      return (
        value.length >= 10 &&
        !value.startsWith("¥") &&
        !value.includes("采购咨询") &&
        !value.includes("退换体验") &&
        !value.includes("品质体验") &&
        !value.includes("纠纷解决") &&
        !value.includes("物流时效") &&
        !value.includes("回头率") &&
        !value.includes("退货包运费") &&
        !value.includes("先采后付") &&
        !value.includes("官方物流") &&
        !value.includes("验厂报告") &&
        value !== "找相似" &&
        value !== "旺旺在线"
      );
    }

    function pickTitle(link, merchantName) {
      const textBlocks = Array.from(link.querySelectorAll("*"))
        .map((node) => clean(node.textContent))
        .filter(Boolean);
      const nestedTitle = textBlocks.find(looksLikeDescendantTitle);
      if (nestedTitle) return nestedTitle;

      let text = clean(link.textContent).replace(/^找相似/, "").replace(/旺旺在线$/, "");
      if (merchantName) text = text.replace(merchantName, "");

      const metrics = [
        /综合服务验厂报告/g, /综合服务/g, /验厂报告/g,
        /采购咨询\d+(\.\d+)?/g, /退换体验\d+(\.\d+)?/g,
        /品质体验\d+(\.\d+)?/g, /纠纷解决\d+(\.\d+)?/g,
        /物流时效\d+(\.\d+)?/g, /回头率\d+%/g,
        /退货包运费/g, /先采后付/g, /官方物流/g, /旺旺在线/g,
        /¥\d+(\.\d+)?/g, /\d+\+?件/g, /满\d+[^\s]*/g,
        /首单减\d+[^\s]*/g, /最高返\d+[^\s]*/g, /商机组货：[^\s]+/g,
      ];
      for (const pattern of metrics) text = text.replace(pattern, " ");
      return clean(text);
    }

    for (let index = 0; index < anchors.length; index++) {
      const offerLink = anchors[index];
      if (!isOfferLink(offerLink)) continue;
      const offerUrl = offerLink.href;
      if (!offerUrl || seen.has(offerUrl)) continue;
      seen.add(offerUrl);

      let merchantLink = null;
      let contactLink = null;
      for (let cursor = index + 1; cursor < anchors.length; cursor++) {
        const candidate = anchors[cursor];
        if (isOfferLink(candidate)) break;
        if (!merchantLink && isMerchantLink(candidate)) { merchantLink = candidate; continue; }
        if (!contactLink && candidate.href.includes("def_cbu_web_im")) contactLink = candidate;
      }

      const image = offerLink.querySelector("img");
      const merchantName = clean(merchantLink?.textContent);

      items.push({
        title: pickTitle(offerLink, merchantName),
        offerUrl,
        imageUrl: image?.getAttribute("src") ?? image?.getAttribute("data-src") ?? image?.getAttribute("data-lazy-src") ?? "",
        merchantName,
        merchantUrl: merchantLink?.href ?? "",
        contactUrl: contactLink?.href ?? "",
      });
      if (items.length >= take) break;
    }
    return items;
  }, limit);

  return rawResults
    .map((item, index) =>
      normalizeOfferRecord({ ...item, imageUrl: normalizeUrl(item.imageUrl) }, index + 1),
    )
    .map((item) => ({
      ...item,
      title: looksLikeTitle(item.title) ? item.title : item.title || `1688结果-${item.index}`,
    }));
}

// ── 主流程 ───────────────────────────────────────────

async function main() {
  console.log("\n╔══════════════════════════════════════════════════════╗");
  console.log("║   旺旺多轮询价 — 首轮发送                            ║");
  console.log("╚══════════════════════════════════════════════════════╝\n");

  const supabase = initSupabase();
  console.log("✅ Supabase 已连接");

  const materials = await fetchMaterials(supabase);
  console.log(`📦 材料清单: ${materials.length} 个关键词`);
  for (const m of materials) {
    console.log(`   - ${m.material_name}${m.specification ? ` (${m.specification})` : ""}`);
  }

  await mkdir(PROFILE_DIR, { recursive: true });
  const context = await chromium.launchPersistentContext(PROFILE_DIR, {
    channel: "chrome",
    headless: HEADLESS,
    locale: "zh-CN",
    viewport: { width: 1440, height: 960 },
    args: ["--disable-blink-features=AutomationControlled"],
  });
  const verificationMonitor = attachVerificationMonitor(context, { logPrefix: "[chat-init][验证码]" });

  while (context.pages().length > 1) {
    await context.pages().at(-1)?.close().catch(() => {});
  }

  try {
    for (let i = 0; i < materials.length; i++) {
      const material = materials[i];
      const keyword = material.material_name;
      const batchId = String(material.batch_id ?? "").trim();

      if (!batchId) {
        console.error(`\n❌ 物料 ${material.id} 缺少 batch_id，跳过`);
        continue;
      }

      console.log(`\n${"═".repeat(56)}`);
      console.log(`📌 [${i + 1}/${materials.length}] 关键词: "${keyword}"`);
      console.log(`🏷️  对应批次: ${batchId}`);
      console.log(`${"─".repeat(56)}`);

      // 1. 搜索
      let resultsPage;
      try {
        resultsPage = await openSearchResults(context, keyword);
      } catch (err) {
        console.error(`  ❌ 搜索失败: ${err.message}`);
        continue;
      }

      let topResults;
      try {
        topResults = await extractTopResults(resultsPage, RESULT_LIMIT);
      } catch (err) {
        console.error(`  ❌ 结果提取失败: ${err.message}`);
        continue;
      }
      console.log(`  📋 提取到 ${topResults.length} 个搜索结果`);

      if (topResults.length === 0) {
        console.log("  ⚠️ 无搜索结果，跳过");
        continue;
      }

      // 2. 创建 customer_service_chats 记录
      let chatTasks;
      try {
        chatTasks = await initChatTasks(supabase, {
          batchId,
          material,
          searchResults: topResults,
        });
        console.log(`  💾 创建了 ${chatTasks.length} 个对话任务`);
      } catch (err) {
        console.error(`  ❌ 创建对话任务失败: ${err.message}`);
        continue;
      }

      // 3. 发送 Round 1 话术
      const contacted = new Set();
      for (const task of chatTasks) {
        const dedupeKey = task.contact_url || task.merchant_url || task.offer_url;
        if (contacted.has(dedupeKey)) {
          console.log(`  ⏭️  跳过重复商家: ${task.merchant_name}`);
          continue;
        }
        contacted.add(dedupeKey);

        const round1Message = getMessageForRound(1, task.ask_context || {});
        console.log(`\n  🗨️  联系: ${task.merchant_name || task.contact_handle || "未知商家"}`);
        console.log(`     话术: "${round1Message.slice(0, 50)}..."`);

        // 构造 contactMerchant 所需的 result 格式
        const fakeResult = {
          contact: {
            url: task.contact_url,
            handle: task.contact_handle,
            site: task.contact_site,
          },
          merchantName: task.merchant_name,
          merchantUrl: task.merchant_url,
          offerUrl: task.offer_url,
        };

        const contactStatus = await contactMerchant(context, fakeResult, round1Message);
        console.log(`     结果: [${contactStatus.status}] ${contactStatus.reason || "OK"}`);

        if (contactStatus.status === "sent" || contactStatus.status === "unknown") {
          const msgHash = getMessageHash(round1Message);
          await markSent(supabase, task.id, msgHash);
          await logMessage(supabase, {
            chatId: task.id,
            round: 1,
            direction: "SENT",
            content: round1Message,
            messageHash: msgHash,
          });
        }

        await chatPause();
      }

      // 关闭多余标签页
      for (const p of context.pages().slice(1)) {
        await p.close().catch(() => {});
      }

      // 关键词间等待
      if (i < materials.length - 1) {
        const wait = randomBetween(8_000, 15_000);
        console.log(`\n⏳ 关键词间隔等待 ${(wait / 1000).toFixed(1)}s...`);
        await new Promise((r) => setTimeout(r, wait));
      }
    }

    console.log(`\n${"═".repeat(56)}`);
    console.log(`✅ 首轮发送完成！批次 ID: ${batchId}`);
    console.log(`   共处理 ${materials.length} 个关键词`);
    console.log(`   接下来请启动 Worker: pnpm --dir chatbot run chat:worker`);
    console.log(`${"═".repeat(56)}\n`);
  } finally {
    verificationMonitor.dispose();
    if (!KEEP_BROWSER_OPEN) {
      await context.close();
    }
  }
}

main().catch((error) => {
  console.error(`\n💥 程序崩溃: ${error instanceof Error ? error.message : String(error)}`);
  if (error instanceof Error && error.stack) console.error(error.stack);
  process.exitCode = 1;
});
