import { mkdir } from "node:fs/promises";
import path from "node:path";
import process from "node:process";
import { setTimeout as sleep } from "node:timers/promises";
import { fileURLToPath } from "node:url";

import dotenv from "dotenv";

import {
  attachVerificationMonitor,
  waitForManualVerificationIfNeeded,
  waitForVerificationClear,
} from "./human.js";
import { buildSearchBrowserArgs } from "./low-cpu-runtime.js";
import { resolve1688ProfileDir } from "./profile-dir.js";
import { launch1688PersistentContext } from "./persistent-context.js";

const SCRIPT_FILE = fileURLToPath(import.meta.url);
const SCRIPT_DIR = path.dirname(SCRIPT_FILE);
const PROJECT_ROOT = path.resolve(SCRIPT_DIR, "../..");
const SEARCH_CHILD_EVENT_PREFIX = "__SEARCH_EVENT__:";
const VERIFICATION_URL_PATTERN = /x5step=1|\/punish\b/i;

dotenv.config({ path: path.join(PROJECT_ROOT, ".env") });

function parseArgs(argv) {
  const args = new Map();
  for (const item of argv) {
    const match = item.match(/^--([^=]+)=(.*)$/);
    if (match) {
      args.set(match[1], match[2]);
    }
  }
  return args;
}

function emitSearchChildEvent(event) {
  const payload = {
    taskId: process.env.SEARCH_TASK_ID ?? "",
    accountId: process.env.SEARCH_1688_ACCOUNT_ID ?? "",
    materialId: process.env.SEARCH_TASK_CURRENT_MATERIAL_ID ?? "",
    materialName: process.env.SEARCH_TASK_CURRENT_MATERIAL_NAME ?? "",
    ...event,
  };
  process.stdout.write(`${SEARCH_CHILD_EVENT_PREFIX}${JSON.stringify(payload)}\n`);
}

function isVerificationUrl(url) {
  return VERIFICATION_URL_PATTERN.test(String(url ?? ""));
}

async function waitForVisibleVerificationResolved(page) {
  let sawVerification = isVerificationUrl(page.url());

  while (!page.isClosed()) {
    const cleared = await waitForVerificationClear(page, {
      logPrefix: "[manual-verification][验证码]",
      manualOnAutoVerificationFailure: true,
    }).catch(() => false);
    if (cleared) {
      return true;
    }

    const manualResolved = await waitForManualVerificationIfNeeded(page).catch(() => false);
    if (manualResolved) {
      return true;
    }

    const currentUrl = page.url();
    if (isVerificationUrl(currentUrl)) {
      sawVerification = true;
    } else if (!sawVerification) {
      return true;
    } else {
      await sleep(1200);
      const settledUrl = page.url();
      if (!isVerificationUrl(settledUrl)) {
        return true;
      }
    }

    await sleep(1000);
  }

  throw new Error("验证码可视窗口已关闭，未确认验证完成");
}

async function main() {
  const args = parseArgs(process.argv.slice(2));
  const pageUrl = String(args.get("page-url") ?? "").trim();
  const materialId = String(args.get("material-id") ?? process.env.SEARCH_TASK_CURRENT_MATERIAL_ID ?? "").trim();
  const profileDir = resolve1688ProfileDir();

  if (!pageUrl) {
    throw new Error("缺少 --page-url，无法打开验证码页面");
  }

  await mkdir(profileDir, { recursive: true });

  const context = await launch1688PersistentContext(profileDir, {
    contextOptions: {
      headless: false,
      args: buildSearchBrowserArgs({
        ...process.env,
        SEARCH_RESOURCE_PROFILE: "debug",
      }),
    },
  });

  const monitor = attachVerificationMonitor(context, {
    logPrefix: "[manual-verification][验证码]",
    manualOnAutoVerificationFailure: true,
  });

  try {
    const page = context.pages()[0] ?? await context.newPage();
    emitSearchChildEvent({
      type: "manual_intervention_message",
      kind: "captcha",
      message: "已打开可视化验证码窗口，请在窗口中完成人工验证。",
      materialId,
      pageUrl,
      detectedAt: new Date().toISOString(),
    });

    await page.goto(pageUrl, { waitUntil: "domcontentloaded", timeout: 30_000 });
    await page.bringToFront().catch(() => {});

    await waitForVisibleVerificationResolved(page);

    emitSearchChildEvent({
      type: "manual_intervention_resolved",
      kind: "captcha",
      message: "验证码已解除，关闭可视化窗口并恢复无头搜索。",
      materialId,
      pageUrl: page.url(),
      resolvedAt: new Date().toISOString(),
    });
  } finally {
    monitor.dispose();
    await context.close().catch(() => {});
  }
}

main().catch((error) => {
  console.error(error instanceof Error ? error.stack ?? error.message : String(error));
  process.exitCode = 1;
});
