/**
 * Flock Freight 登录态 — DOM/RPA 保障路径（对齐 MotherShip session 语义）
 * 凭据：FLOCK_LOGIN_EMAIL / FLOCK_LOGIN_PASSWORD；密码不写日志
 */
import fs from "node:fs";
import path from "node:path";
import type { BrowserContext, Page } from "playwright";
import {
  getFlockLoginUrl,
  getFlockQuoteUrl,
  getFlockStorageStatePath,
  shouldSaveFlockStorageState,
} from "@/lib/flock/env";
import {
  getEffectiveFlockLogin,
  hasEffectiveFlockLogin,
  mustForceFlockAccountLogin,
} from "@/lib/flock/login-context";
import { gotoWithResilience } from "@/lib/rpa/page-goto";
import { captureFlockStall } from "@/workers/rpa/flock/stall-watch";
import {
  isFlockLoggedInQuoteForm,
  openFlockLoggedInQuoteEntry,
} from "@/workers/rpa/flock/logged-in-form";

const LOGIN_URL_PATTERNS = ["/login", "/sign-in", "/signin", "/auth"];

/** 进程内是否曾登录成功（用于区分 FLOCK_LOGIN_FAILED vs SESSION_EXPIRED） */
let flockLoginSucceededOnce = false;

export type FlockLoginResult =
  | { ok: true; didLogin: boolean }
  | { ok: false; errorMessage: string };

export function resetFlockLoginSessionFlagForTests(): void {
  flockLoginSucceededOnce = false;
}

export function isFlockLoginPage(page: Page): boolean {
  const url = page.url().toLowerCase();
  return LOGIN_URL_PATTERNS.some((p) => url.includes(p));
}

async function looksLoggedIn(page: Page): Promise<boolean> {
  if (isFlockLoginPage(page)) {
    return false;
  }

  const cookies = await page.context().cookies().catch(() => []);
  const hasJwt = cookies.some((c) => c.name === "ff_jwt" && Boolean(c.value));

  // 配置了登录凭据时：必须以 ff_jwt 为准，避免把匿名 get-a-quote 表单当成已登录
  if (hasEffectiveFlockLogin()) {
    if (hasJwt) {
      return true;
    }
    const url = page.url().toLowerCase();
    // 录制成功路径：login → /home → New Quote
    if (
      /\/(home|dashboard|shipments|account|settings|quote\/)/i.test(url) &&
      !url.includes("get-a-quote") &&
      !url.includes("/login")
    ) {
      return true;
    }
    return false;
  }

  if (hasJwt) {
    return true;
  }
  const url = page.url().toLowerCase();
  if (/\/(dashboard|quotes|shipments|account|settings)/i.test(url)) {
    return true;
  }
  const logout = page.getByRole("button", { name: /log\s*out|sign\s*out/i });
  return (await logout.count().catch(() => 0)) > 0;
}

function resolveStoragePath(): string {
  const raw = getFlockStorageStatePath();
  return path.isAbsolute(raw) ? raw : path.join(process.cwd(), raw);
}

async function persistLoginStorage(context: BrowserContext): Promise<void> {
  if (!shouldSaveFlockStorageState()) {
    return;
  }
  const statePath = resolveStoragePath();
  try {
    fs.mkdirSync(path.dirname(statePath), { recursive: true });
    await context.storageState({ path: statePath });
    console.log("[flock-login] 已写回 storageState");
  } catch {
    /* ignore */
  }
}

async function fillAndSubmitLogin(page: Page): Promise<void> {
  const creds = getEffectiveFlockLogin();
  if (!creds) {
    throw new Error("无可用登录凭据（客户账密未解密或 FLOCK_LOGIN_* 未配置）");
  }

  console.log(
    `[flock-login] 填写客户账密 email=${creds.email.slice(0, 2)}***@${creds.email.split("@")[1] ?? "?"}`,
  );

  const emailCustom = process.env.FLOCK_SELECTOR_LOGIN_EMAIL?.trim();
  const passwordCustom = process.env.FLOCK_SELECTOR_LOGIN_PASSWORD?.trim();
  const nextCustom = process.env.FLOCK_SELECTOR_LOGIN_NEXT?.trim();
  const submitCustom = process.env.FLOCK_SELECTOR_LOGIN_SUBMIT?.trim();

  // 录制：两步登录 Email Address → Next → Password → Log in
  // （勿先等 password：当前屏只有邮箱）
  const emailLoc = emailCustom
    ? page.locator(emailCustom).first()
    : page.getByRole("textbox", { name: /Email Address/i }).first();

  await emailLoc.waitFor({ state: "visible", timeout: 15_000 });
  await emailLoc.click({ clickCount: 3 }).catch(() => undefined);
  await emailLoc.fill(creds.email);

  const passwordProbe = passwordCustom
    ? page.locator(passwordCustom).first()
    : page.getByRole("textbox", { name: /^Password$/i }).first();

  const passwordAlreadyVisible = await passwordProbe
    .isVisible({ timeout: 800 })
    .catch(() => false);

  if (!passwordAlreadyVisible) {
    const nextBtn = nextCustom
      ? page.locator(nextCustom).first()
      : page.getByRole("button", { name: /^Next/i }).first();
    await nextBtn.waitFor({ state: "visible", timeout: 10_000 });
    await nextBtn.click();
    console.log("[flock-login] 已点 Next，等待密码框…");
  }

  const passwordLoc = passwordCustom
    ? page.locator(passwordCustom).first()
    : page.getByRole("textbox", { name: /^Password$/i }).first();
  await passwordLoc.waitFor({ state: "visible", timeout: 15_000 });
  await passwordLoc.click({ clickCount: 3 }).catch(() => undefined);
  await passwordLoc.fill(creds.password);

  const submit = submitCustom
    ? page.locator(submitCustom).first()
    : page.getByRole("button", { name: /Log\s*in/i }).first();
  if ((await submit.count()) > 0 && (await submit.isVisible().catch(() => false))) {
    await submit.click();
  } else {
    const fallback = page.locator(
      'button[type="submit"], button:has-text("Log in"), button:has-text("Sign in")',
    ).first();
    if ((await fallback.count()) > 0) {
      await fallback.click();
    } else {
      await passwordLoc.press("Enter");
    }
  }

  await page.waitForLoadState("networkidle", { timeout: 20_000 }).catch(() => {
    /* ignore */
  });
  // 登录成功会跳 /home；等一下再判 looksLoggedIn
  await page
    .waitForURL(/\/(home|dashboard|quote\/)/i, { timeout: 20_000 })
    .catch(() => undefined);
  await page.waitForTimeout(800);
}

/**
 * 确保当前 page 处于登录态；必要时执行登录（含 1 次重登）
 * @param reloginAttempt 是否为「会话失效后的第二次尝试」
 */
export async function ensureFlockLoggedIn(
  page: Page,
  context: BrowserContext,
  options?: { reloginAttempt?: boolean; forceFreshLogin?: boolean },
): Promise<FlockLoginResult> {
  const reloginAttempt = options?.reloginAttempt === true;
  const forceFreshLogin = options?.forceFreshLogin === true;

  if (!forceFreshLogin && (await looksLoggedIn(page))) {
    flockLoginSucceededOnce = true;
    return { ok: true, didLogin: false };
  }

  // 客户账密首登：清掉匿名 Direct / 其他客户残留 cookie
  if (forceFreshLogin && mustForceFlockAccountLogin()) {
    await context.clearCookies().catch(() => undefined);
    console.log("[flock-login] 客户账密模式：已清空 cookie，强制官网登录");
  } else if (await looksLoggedIn(page)) {
    flockLoginSucceededOnce = true;
    return { ok: true, didLogin: false };
  }

  if (!hasEffectiveFlockLogin()) {
    if (isFlockLoginPage(page)) {
      return {
        ok: false,
        errorMessage:
          "STRUCT_CHANGE：登录页但未配置客户承运商账密或 FLOCK_LOGIN_*",
      };
    }
    return { ok: true, didLogin: false };
  }

  console.log(
    mustForceFlockAccountLogin()
      ? "[flock-login] 执行客户官网账密登录（跳过 Direct API）…"
      : "[flock-login] 执行账号密码登录…",
  );
  const loginUrl = getFlockLoginUrl();
  if (!isFlockLoginPage(page) || forceFreshLogin) {
    await gotoWithResilience(page, loginUrl);
  }

  try {
    await fillAndSubmitLogin(page);
  } catch (error) {
    const raw = error instanceof Error ? error.message : String(error);
    await captureFlockStall(page, "登录-无法填表", 3_000).catch(() => undefined);
    return {
      ok: false,
      errorMessage: `FLOCK_LOGIN_FAILED：无法填写登录表单（${raw.slice(0, 120)}）`,
    };
  }

  if (isFlockLoginPage(page) || !(await looksLoggedIn(page))) {
    const expired =
      reloginAttempt || flockLoginSucceededOnce
        ? "SESSION_EXPIRED：Flock 登录会话失效，重登失败"
        : "FLOCK_LOGIN_FAILED：账号或密码错误";
    return { ok: false, errorMessage: expired };
  }

  flockLoginSucceededOnce = true;
  await persistLoginStorage(context);

  // 登录态走 home → New Quote → Quick；禁止只靠匿名 get-a-quote
  if (hasEffectiveFlockLogin()) {
    const opened = await openFlockLoggedInQuoteEntry(page);
    if (!opened && !(await isFlockLoggedInQuoteForm(page))) {
      console.warn(
        "[flock-login] 登录成功但未打开 Quick 查价页，后续 waitForForm 可能失败",
      );
    }
  } else {
    const quoteUrl = getFlockQuoteUrl();
    if (!page.url().includes("get-a-quote")) {
      await gotoWithResilience(page, quoteUrl);
    }
  }

  return { ok: true, didLogin: true };
}

/**
 * 登录态下若仍落到注册页：明确失败，禁止盲填随机注册
 */
export function flockLoginRegistrationBlockedMessage(): string {
  return "STRUCT_CHANGE：已配置登录凭据但仍进入注册页（登录未生效，请检查客户承运商账密或 FLOCK_LOGIN_*）";
}
