import { mkdir } from "node:fs/promises";
import { existsSync } from "node:fs";
import path from "node:path";
import process from "node:process";
import { setTimeout as sleep } from "node:timers/promises";
import { fileURLToPath } from "node:url";
import { chromium } from "playwright";
import {
  antiDetectPause,
  attachVerificationMonitor,
  ensureSlideVerification,
  randomBetween,
  waitForManualVerificationIfNeeded,
} from "../playwright/human.js";
import { createResolvedSupabaseClient } from "./supabase-node-client.js";

const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
const AUTOMATION_DIR = path.join(SCRIPT_DIR, "..");
const DEFAULT_PROFILE_ROOT = path.join(AUTOMATION_DIR, ".browser", "1688-profiles");
const LEGACY_PROFILE_DIR = path.join(AUTOMATION_DIR, ".browser", "1688-profile");
const LOGIN_URL = "https://login.taobao.com/member/login.jhtml?style=tao_custom&from=1688web";
const SEARCH_HOME_URL = "https://www.1688.com/";
const DEFAULT_TIMEOUT_MS = Number.parseInt(process.env.LOGIN_1688_TIMEOUT_MS ?? "90000", 10);
const LOGIN_CONTEXT_RETRY_LIMIT = 2;
const VERIFICATION_TEXT_PATTERN = /验证码拦截|拖动下方滑块完成验证|请按住滑块|通过验证以确保正常访问|验证失败|二次验证|拖动到最右边|滑动验证|x5step=1|\/punish\b/i;

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

function parseArgs(argv) {
  const options = {
    accountId: process.env.SEARCH_1688_ACCOUNT_ID ?? "",
    profileDir: process.env.SEARCH_1688_PROFILE_DIR ?? "",
    headless: false,
    timeoutMs: DEFAULT_TIMEOUT_MS,
    keepOpen: false,
  };

  for (const arg of argv) {
    if (!arg.startsWith("--")) {
      continue;
    }

    const [flag, rawValue] = arg.slice(2).split("=", 2);
    const value = rawValue ?? "";

    if (flag === "account-id" && value) {
      options.accountId = value;
    } else if (flag === "profile-dir" && value) {
      options.profileDir = value;
    } else if (flag === "headless" && value) {
      options.headless = value === "1" || value === "true";
    } else if (flag === "timeout-ms" && value) {
      options.timeoutMs = Number.parseInt(value, 10);
    } else if (flag === "keep-open") {
      options.keepOpen = true;
    }
  }

  if (!Number.isInteger(options.timeoutMs) || options.timeoutMs <= 0) {
    throw new Error("参数 --timeout-ms 必须是大于 0 的整数");
  }

  return options;
}

function getSupabase() {
  try {
    return createResolvedSupabaseClient(process.env);
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    if (message.includes("缺少 SUPABASE_URL") || message.includes("可用的 Supabase key")) {
      throw new Error("缺少 Supabase 环境变量，无法读取 1688 账户信息");
    }

    throw error;
  }
}

async function fetchAccountById(id) {
  const supabase = getSupabase();
  const { data, error } = await supabase
    .from("procurement_accounts_1688")
    .select("id, account_name, password, remark")
    .eq("id", id)
    .maybeSingle();

  if (error) {
    throw new Error(`读取 1688 账户失败: ${error.message}`);
  }

  return data ?? null;
}

async function reportLoginStatus(account, status, error = null) {
  if (!account?.id && !account?.account_name) {
    return;
  }

  try {
    const supabase = getSupabase();
    let query = supabase
      .from("procurement_accounts_1688")
      .update({
        login_status: status,
        login_error: error,
        login_checked_at: new Date().toISOString(),
      });

    if (account.id) {
      query = query.eq("id", account.id);
    } else {
      query = query.eq("account_name", account.account_name);
    }

    const { error: updateError } = await query;
    if (updateError) {
      throw updateError;
    }
  } catch (updateError) {
    console.warn("[login-1688] 回写登录状态失败:", updateError?.message ?? updateError);
  }
}

function sanitizeProfileSegment(value) {
  return String(value ?? "")
    .replace(/[^a-zA-Z0-9_-]/g, "_")
    .replace(/_+/g, "_")
    .replace(/^_+|_+$/g, "")
    .slice(0, 80);
}

function resolveProfileDir(options, account) {
  const explicit = options.profileDir || process.env.SEARCH_1688_PROFILE_DIR || "";
  if (explicit) {
    return path.isAbsolute(explicit) ? explicit : path.resolve(process.cwd(), explicit);
  }

  if (account?.id) {
    return path.join(DEFAULT_PROFILE_ROOT, sanitizeProfileSegment(account.id));
  }

  if (account?.account_name) {
    return path.join(DEFAULT_PROFILE_ROOT, sanitizeProfileSegment(account.account_name));
  }

  return LEGACY_PROFILE_DIR;
}

async function ensureDir(target) {
  await mkdir(target, { recursive: true });
}

async function slowType(page, selector, value) {
  if (!value) {
    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 value) {
    await page.keyboard.type(char, { delay: randomBetween(90, 220) });
    await sleep(randomBetween(30, 120));
  }
}

function isLoginUrl(url) {
  return url.includes("login.taobao.com") || url.includes("login.1688.com");
}

function getErrorMessage(error) {
  return error instanceof Error ? error.message : String(error);
}

function isRetriableLoginError(error) {
  return /Page crashed|Target page, context or browser has been closed|Navigation timeout/i.test(getErrorMessage(error));
}

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 (!page || page.isClosed()) {
    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(String(bodyText ?? "").replace(/\s+/g, " ").trim());
}

async function settleSlideVerification(page) {
  const resolved = await ensureSlideVerification(page, { logPrefix: "[login-1688][验证码]" }).catch(() => false);
  if (resolved) {
    await page.waitForLoadState("domcontentloaded", { timeout: 5000 }).catch(() => {});
  }

  await waitForManualVerificationIfNeeded(page).catch(() => false);
  return !(await pageLooksLikeVerification(page));
}

async function verifyExistingSession(page, context, timeoutMs) {
  try {
    await page.goto(SEARCH_HOME_URL, { waitUntil: "domcontentloaded", timeout: timeoutMs });
    await settleSlideVerification(page);
    await antiDetectPause(800, 1500);
  } catch {
    return false;
  }

  if (isLoginUrl(page.url())) {
    return false;
  }

  return await settleSlideVerification(page) && await hasLoginSession(context);
}

async function createFreshPage(context, page = null) {
  const nextPage = await context.newPage();

  if (page && !page.isClosed()) {
    await page.close().catch(() => {});
  }

  return nextPage;
}

async function gotoWithRetry(context, page, url, options, label, maxAttempts = 3) {
  let currentPage = page;

  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
    try {
      await currentPage.goto(url, options);
      return currentPage;
    } catch (error) {
      const message = getErrorMessage(error);
      const retriable = isRetriableLoginError(error);

      if (!retriable || attempt >= maxAttempts) {
        throw error;
      }

      console.warn(`[login-1688] ${label} 第 ${attempt}/${maxAttempts} 次导航失败，准备重试: ${message}`);

      currentPage = await createFreshPage(context, currentPage);

      await antiDetectPause(500, 1200);
    }
  }

  return currentPage;
}

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

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()) === 0) {
      continue;
    }
    if (!(await candidate.isVisible().catch(() => false))) {
      continue;
    }

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

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

async function detectFailureReason(page) {
  const bodyText = await page.locator("body").innerText().catch(() => "");

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

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

  return "自动密码登录失败：未获得有效的 1688 登录态";
}

async function waitForLoginSuccess(page, context, timeoutMs) {
  const startedAt = Date.now();

  while (Date.now() - startedAt < timeoutMs) {
    const currentPageClear = await settleSlideVerification(page);
    if (currentPageClear && !isLoginUrl(page.url()) && await hasLoginSession(context)) {
      return true;
    }

    for (const candidate of context.pages()) {
      if (!isLoginUrl(candidate.url()) && (candidate.url().includes("1688.com") || candidate.url().includes("taobao.com"))) {
        const candidateClear = candidate === page ? currentPageClear : await settleSlideVerification(candidate);
        if (candidateClear && await hasLoginSession(context)) {
          return true;
        }
      }
    }

    await sleep(1500);
  }

  return false;
}

async function resolveAccount(options) {
  if (options.accountId) {
    const account = await fetchAccountById(options.accountId);
    if (!account) {
      throw new Error(`未找到 id=${options.accountId} 的 1688 账户`);
    }
    if (!account.account_name || !account.password) {
      throw new Error(`1688 账户 ${options.accountId} 缺少账号名或密码`);
    }
    return account;
  }

  const accountName = process.env.LOGIN_1688_USER ?? "";
  const password = process.env.LOGIN_1688_PASS ?? "";
  if (!accountName || !password) {
    throw new Error("未提供 account_id，且环境变量 LOGIN_1688_USER / LOGIN_1688_PASS 不完整");
  }

  return {
    id: process.env.SEARCH_1688_ACCOUNT_ID ?? "",
    account_name: accountName,
    password,
  };
}

async function launchLoginContext(profileDir, headless) {
  return chromium.launchPersistentContext(profileDir, {
    channel: "chrome",
    headless,
    locale: "zh-CN",
    viewport: { width: 1440, height: 960 },
    args: ["--disable-blink-features=AutomationControlled"],
  });
}

async function runLoginAttempt(context, account, options) {
  let page = context.pages()[0] ?? await context.newPage();

  if (await verifyExistingSession(page, context, options.timeoutMs)) {
    console.log("[login-1688] 检测到已有有效登录态，跳过自动登录。");
    return;
  }

  page = await createFreshPage(context, page);
  page = await gotoWithRetry(
    context,
    page,
    LOGIN_URL,
    { waitUntil: "domcontentloaded", timeout: options.timeoutMs },
    "登录页"
  );
  await antiDetectPause();
  await clickPasswordTab(page);
  await slowType(page, "#fm-login-id", account.account_name);
  await antiDetectPause(800, 1500);
  await slowType(page, "#fm-login-password", account.password);
  await antiDetectPause(800, 1500);
  await clickLoginButton(page);
  await antiDetectPause(1200, 2200);

  const success = await waitForLoginSuccess(page, context, options.timeoutMs);
  if (!success) {
    throw new Error(await detectFailureReason(page));
  }

  await settleSlideVerification(page);

  console.log("[login-1688] 已获取有效登录态。");
}

async function main() {
  const options = parseArgs(process.argv.slice(2));
  const account = await resolveAccount(options);
  const profileDir = resolveProfileDir(options, account);

  await ensureDir(profileDir);

  console.log(`[login-1688] 使用账户: ${account.account_name}`);
  console.log(`[login-1688] 浏览器档案: ${profileDir}`);

  for (let attempt = 1; attempt <= LOGIN_CONTEXT_RETRY_LIMIT; attempt += 1) {
    let context;
    let verificationMonitor;

    try {
      context = await launchLoginContext(profileDir, options.headless);
      verificationMonitor = attachVerificationMonitor(context, { logPrefix: "[login-1688][验证码]" });
      await runLoginAttempt(context, account, options);
      await reportLoginStatus(account, "success", null);
      if (options.keepOpen) {
        console.log("[login-1688] --keep-open 已启用，浏览器将保持打开；按 Ctrl+C 结束。");
        await new Promise(() => {});
      }
      return;
    } catch (error) {
      const errorMsg = getErrorMessage(error);
      const retriable = isRetriableLoginError(error);

      if (!retriable || attempt >= LOGIN_CONTEXT_RETRY_LIMIT) {
        await reportLoginStatus(account, "failed", errorMsg);
        throw error;
      }

      console.warn(`[login-1688] 浏览器会话第 ${attempt}/${LOGIN_CONTEXT_RETRY_LIMIT} 次执行失败，将重启浏览器后重试: ${errorMsg}`);
      await antiDetectPause(800, 1500);
    } finally {
      verificationMonitor?.dispose();
      if (!options.keepOpen) {
        await context?.close().catch(() => {});
      }
    }
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
