import { setTimeout as sleep } from "node:timers/promises";

import {
  antiDetectPause,
  ensureSlideVerification,
  humanClick,
  randomBetween,
  waitForManualVerificationIfNeeded,
} from "./human.js";

const LOGIN_URL = "https://login.taobao.com/member/login.jhtml?style=tao_custom&from=1688web";
const SEARCH_HOME_URL = "https://www.1688.com/";
const LOGIN_SUCCESS_GRACE_AFTER_VERIFICATION_MS = 30000;
const VERIFICATION_TEXT_PATTERN = /验证码拦截|拖动下方滑块完成验证|请按住滑块|通过验证以确保正常访问|验证失败|二次验证|拖动到最右边|滑动验证|短信验证码|手机验证码|验证码已发送|请输入.{0,12}验证码|输入.{0,12}验证码|校验码|安全验证.{0,20}验证码|身份验证.{0,20}验证码|验证码.{0,12}(?:重新发送|已发送)|x5step=1|\/punish\b/i;
const HEADLESS_MANUAL_VERIFICATION_HANDOFF_CODE = "HEADLESS_MANUAL_VERIFICATION_HANDOFF";

function normalizeText(value) {
  return String(value ?? "").replace(/\s+/g, " ").trim();
}

function isPageClosed(page) {
  return !page || page.isClosed?.() === true;
}

export function is1688LoginUrl(url) {
  const value = String(url ?? "");
  return value.includes("login.taobao.com") || value.includes("login.1688.com");
}

function isTrustedPostLoginUrl(url) {
  const value = String(url ?? "");
  return value.includes("i.taobao.com/my_itaobao")
    || value.includes("work.1688.com")
    || value.includes("air.1688.com");
}

async function hasLoginSession(context) {
  const cookies = await context.cookies([SEARCH_HOME_URL, "https://login.taobao.com"]);
  return cookies.some((cookie) => cookie.name === "_m_h5_tk" || cookie.name === "cookie2" || cookie.name === "xman_us_t");
}

async function pageLooksLikeVerification(page) {
  if (isPageClosed(page)) {
    return false;
  }

  const pageUrl = String(page.url?.() ?? "");
  if (VERIFICATION_TEXT_PATTERN.test(pageUrl)) {
    return true;
  }

  const bodyText = await page.locator("body").innerText({ timeout: 1200 }).catch(async () => (
    page.evaluate(() => document.body?.innerText ?? "").catch(() => "")
  ));

  return VERIFICATION_TEXT_PATTERN.test(normalizeText(bodyText));
}

function isHeadlessManualVerificationHandoffError(error) {
  const message = error instanceof Error ? error.message : String(error);
  return message.includes(HEADLESS_MANUAL_VERIFICATION_HANDOFF_CODE);
}

function isLocatorTimeoutError(error) {
  const message = error instanceof Error ? error.message : String(error);
  return /locator\.[a-z]+:?\s*Timeout\s+\d+ms exceeded/i.test(message);
}

async function settleSlideVerification(page, logPrefix = "[login-preflight][验证码]", options = {}) {
  if (options.headless === true && await pageLooksLikeVerification(page).catch(() => false)) {
    return false;
  }

  let resolved = false;
  try {
    resolved = await ensureSlideVerification(page, { logPrefix });
  } catch (error) {
    if (isHeadlessManualVerificationHandoffError(error)) {
      throw error;
    }
    resolved = false;
  }
  if (resolved) {
    await page.waitForLoadState("domcontentloaded", { timeout: 5000 }).catch(() => {});
  }

  if (options.headless !== true) {
    await waitForManualVerificationIfNeeded(page).catch((error) => {
      if (isHeadlessManualVerificationHandoffError(error)) {
        throw error;
      }
      return false;
    });
  }
  return !(await pageLooksLikeVerification(page));
}

async function clickPasswordTab(page) {
  const tab = page.getByText("密码登录").first();
  if (await tab.isVisible().catch(() => false)) {
    await humanClick(page, tab).catch(() => tab.click({ delay: randomBetween(60, 180) }).catch(() => {}));
    await antiDetectPause(600, 1200);
  }
}

async function slowType(page, selector, value) {
  const normalized = normalizeText(value);
  if (!normalized) {
    return;
  }

  const field = page.locator(selector).first();
  await field.waitFor({ state: "visible", timeout: 15000 });
  await field.click();
  await page.keyboard.press("Meta+A").catch(() => {});
  await sleep(randomBetween(60, 140));
  await page.keyboard.press("Backspace").catch(() => {});
  await sleep(randomBetween(80, 180));

  for (const char of normalized) {
    await page.keyboard.type(char, { delay: randomBetween(90, 220) });
    await sleep(randomBetween(30, 120));
  }
}

async function fillLoginCredentials(page, accountName, password) {
  try {
    await slowType(page, "#fm-login-id", accountName);
    await antiDetectPause(800, 1500);
    await slowType(page, "#fm-login-password", password);
    return { ok: true, reason: "" };
  } catch (error) {
    if (isLocatorTimeoutError(error)) {
      const reason = await detectFailureReason(page);
      if (isManualVerificationFailureReason(reason)) {
        return { ok: false, reason };
      }
      return {
        ok: false,
        reason: "搜索前登录态预热暂停：登录表单未显示，请在可视化窗口完成登录或验证",
      };
    }
    throw error;
  }
}

async function clickLoginButton(page) {
  const candidates = [
    page.getByRole("button", { name: /登录|登 录|立即登录/ }).first(),
    page.locator("button[type='submit']").first(),
    page.locator("input[type='submit']").first(),
    page.locator(".fm-button.fm-submit").first(),
    page.locator(".password-login").first(),
  ];

  for (const candidate of candidates) {
    if ((await candidate.count().catch(() => 0)) === 0) {
      continue;
    }
    if (!(await candidate.isVisible().catch(() => false))) {
      continue;
    }

    await humanClick(page, candidate).catch(async () => {
      await candidate.click({ delay: randomBetween(60, 180) }).catch(async () => {
        await page.keyboard.press("Enter").catch(() => {});
      });
    });
    return;
  }

  await page.keyboard.press("Enter").catch(() => {});
}

async function confirmTrustedPostLoginHome(page, timeoutMs, options = {}) {
  if (isPageClosed(page)) {
    return false;
  }

  await page.goto(SEARCH_HOME_URL, { waitUntil: "domcontentloaded", timeout: timeoutMs }).catch(() => {});
  const pageClear = await settleSlideVerification(page, "[login-preflight][验证码]", options).catch(() => false);
  if (!pageClear || isPageClosed(page)) {
    return false;
  }

  return !is1688LoginUrl(page.url()) && !(await pageLooksLikeVerification(page).catch(() => false));
}

async function waitForLoginSuccess(page, context, timeoutMs, options = {}) {
  let deadlineAt = Date.now() + timeoutMs;

  while (Date.now() < deadlineAt) {
    const verificationBeforeSettle = await pageLooksLikeVerification(page).catch(() => false);
    if (options.headless === true && verificationBeforeSettle) {
      return null;
    }
    const settleStartedAt = Date.now();
    const currentPageClear = await settleSlideVerification(page, "[login-preflight][验证码]", options);
    const waitedForVerificationMs = Date.now() - settleStartedAt;
    if (currentPageClear && (verificationBeforeSettle || waitedForVerificationMs > 5000)) {
      deadlineAt = Math.max(deadlineAt, Date.now() + LOGIN_SUCCESS_GRACE_AFTER_VERIFICATION_MS);
    }

    if (currentPageClear && !is1688LoginUrl(page.url()) && await hasLoginSession(context)) {
      return page;
    }
    if (currentPageClear && isTrustedPostLoginUrl(page.url()) && await confirmTrustedPostLoginHome(page, timeoutMs, options)) {
      return page;
    }

    for (const candidate of context.pages()) {
      if (!is1688LoginUrl(candidate.url()) && (candidate.url().includes("1688.com") || candidate.url().includes("taobao.com"))) {
        const candidateClear = candidate === page ? currentPageClear : await settleSlideVerification(candidate, "[login-preflight][验证码]", options);
        if (candidateClear && await hasLoginSession(context)) {
          if (candidate !== page) {
            await candidate.bringToFront().catch(() => {});
          }
          return candidate;
        }
      }
    }

    await sleep(1500);
  }

  return null;
}

async function detectFailureReason(page) {
  if (isPageClosed(page)) {
    return "有头模式人工验证未完成：浏览器页面已关闭";
  }

  const bodyText = await page.locator("body").innerText().catch(() => "");

  if (/短信验证码|手机验证码|验证码已发送|请输入.{0,12}验证码|输入.{0,12}验证码|校验码|验证码.{0,12}(?:重新发送|已发送)/.test(bodyText)) {
    return "搜索前登录态预热暂停：当前页面需要手机短信验证码";
  }

  if (/验证码|滑动|二次验证|验证失败/.test(bodyText)) {
    return "搜索前登录态预热失败：当前页面需要验证码或二次验证";
  }

  if (/账号|密码.*错误|登录失败|账户异常/.test(bodyText)) {
    return "搜索前登录态预热失败：请检查 1688 账户名/邮箱/手机号或密码是否正确";
  }

  return "搜索前登录态预热失败：未获得有效的 1688 登录态";
}

function isManualVerificationFailureReason(reason) {
  return /短信验证码|手机验证码|验证码已发送|验证码或二次验证|需要验证码|二次验证|滑动/.test(reason);
}

function buildHeadedManualVerificationFailureReason(page, reason) {
  if (isPageClosed(page)) {
    return "有头模式人工验证未完成：浏览器页面已关闭";
  }

  if (/短信验证码|手机验证码|验证码已发送/.test(reason)) {
    return "有头模式人工验证未完成：当前页面仍需要手机短信验证码";
  }

  if (isManualVerificationFailureReason(reason)) {
    return "有头模式人工验证未完成：当前页面仍需要验证码或二次验证";
  }

  return reason;
}

function buildManualVerificationResult(page, reason) {
  return {
    ok: false,
    page,
    status: "manual_verification_required",
    reason,
  };
}

function pickReusablePage(context) {
  return context.pages().find((page) => !page.isClosed() && page.url().includes("1688.com"))
    ?? context.pages().find((page) => !page.isClosed())
    ?? null;
}

export async function ensure1688LoggedIn(context, options = {}) {
  const timeoutMs = Number.isInteger(options.timeoutMs) && options.timeoutMs > 0 ? options.timeoutMs : 90000;
  const accountName = normalizeText(options.accountName ?? process.env.LOGIN_1688_USER);
  const password = normalizeText(options.password ?? process.env.LOGIN_1688_PASS);
  const page = pickReusablePage(context) ?? await context.newPage();

  await page.goto(SEARCH_HOME_URL, { waitUntil: "domcontentloaded", timeout: timeoutMs }).catch(() => {});
  const homePageClear = await settleSlideVerification(page, "[login-preflight][验证码]", options);
  await antiDetectPause(800, 1500);

  if (isPageClosed(page)) {
    const reason = await detectFailureReason(page);
    return {
      ok: false,
      page,
      status: "login_failed",
      reason,
    };
  }

  if (homePageClear && !is1688LoginUrl(page.url()) && await hasLoginSession(context)) {
    return { ok: true, page, status: "existing_session", reason: "" };
  }

  if (options.headless === true && await pageLooksLikeVerification(page).catch(() => false)) {
    const reason = await detectFailureReason(page);
    return buildManualVerificationResult(page, reason);
  }

  if (!accountName || !password) {
    return {
      ok: false,
      page,
      status: "missing_credentials",
      reason: "搜索前登录态预热失败：未提供 1688 账号或密码",
    };
  }

  await page.goto(LOGIN_URL, { waitUntil: "domcontentloaded", timeout: timeoutMs });
  await settleSlideVerification(page, "[login-preflight][验证码]", options);
  if (isPageClosed(page)) {
    const reason = await detectFailureReason(page);
    return {
      ok: false,
      page,
      status: "login_failed",
      reason,
    };
  }
  if (isTrustedPostLoginUrl(page.url()) && await confirmTrustedPostLoginHome(page, timeoutMs, options)) {
    return { ok: true, page, status: "trusted_post_login", reason: "" };
  }
  if (options.headless === true && await pageLooksLikeVerification(page).catch(() => false)) {
    const reason = await detectFailureReason(page);
    return buildManualVerificationResult(page, reason);
  }
  await antiDetectPause();
  await clickPasswordTab(page);
  const credentialFill = await fillLoginCredentials(page, accountName, password);
  if (!credentialFill.ok) {
    if (options.headless === true
      && (isManualVerificationFailureReason(credentialFill.reason) || credentialFill.reason.includes("登录表单未显示"))) {
      return buildManualVerificationResult(page, credentialFill.reason);
    }
    return {
      ok: false,
      page,
      status: "login_failed",
      reason: credentialFill.reason,
    };
  }
  await antiDetectPause(800, 1500);
  await clickLoginButton(page);
  await antiDetectPause(1200, 2200);

  if (options.headless === true && await pageLooksLikeVerification(page).catch(() => false)) {
    const reason = await detectFailureReason(page);
    if (isManualVerificationFailureReason(reason)) {
      return buildManualVerificationResult(page, reason);
    }
  }

  const loggedInPage = await waitForLoginSuccess(page, context, timeoutMs, options);
  if (loggedInPage) {
    await settleSlideVerification(loggedInPage, "[login-preflight][验证码]", options);
    return { ok: true, page: loggedInPage, status: "logged_in", reason: "" };
  }

  const reason = await detectFailureReason(page);
  if (options.headless === true && isManualVerificationFailureReason(reason)) {
    return buildManualVerificationResult(page, reason);
  }
  if (options.headless !== true && isManualVerificationFailureReason(reason)) {
    return {
      ok: false,
      page,
      status: "login_failed",
      reason: buildHeadedManualVerificationFailureReason(page, reason),
    };
  }

  return {
    ok: false,
    page,
    status: "login_failed",
    reason,
  };
}
