/**
 * Flock Worker 浏览器会话复用 — 对齐 batch-flock-stress 单浏览器模式
 * 登录凭据模式：复用已登录 page，不换号注册
 * 无登录：同一随机 QQ 邮箱连续查价，满 FLOCK_QUOTES_PER_EMAIL 后换新邮箱重注册
 */
import fs from "node:fs";
import path from "node:path";
import type { Browser, BrowserContext, Page } from "playwright";
import {
  getFlockQuoteUrl,
  getFlockQuotesPerEmail,
  getFlockSessionMaxRotations,
  getFlockStorageStatePath,
  getFlockTestAccount,
  shouldLoadFlockStorageState,
  type FlockTestAccount,
} from "@/lib/flock/env";
import {
  getEffectiveFlockLogin,
  hasEffectiveFlockLogin,
  mustForceFlockAccountLogin,
} from "@/lib/flock/login-context";
import { resolveFlockQuoteAccount } from "@/lib/flock/resolve-account";
import { launchRpaBrowser } from "@/lib/rpa/browser-launch";
import { createRpaBrowserContext } from "@/lib/rpa/browser-context";
import { gotoWithResilience } from "@/lib/rpa/page-goto";
import {
  isRpaHeaded,
  resolveRpaHeadless,
  resolveRpaSlowMoMs,
} from "@/lib/rpa/env";
import {
  navigateFlockNewQuote,
  waitForFlockQuoteForm,
} from "@/workers/rpa/flock/form-interactions";
import { ensureFlockLoggedIn } from "@/workers/rpa/flock/login";
import { flockQuoteLimitMessage } from "@/workers/rpa/flock/page-state";
import { runFlockQuoteOnPage } from "@/workers/rpa/flock/visual-chain";
import type { FlockQuoteInput, FlockRunQuoteResult } from "@/workers/rpa/flock/types";
import {
  reportFlockStage,
  type FlockStageReporter,
} from "@/lib/flock/rpa-progress";

type SessionState = {
  browser: Browser;
  context: BrowserContext;
  page: Page;
  account: FlockTestAccount;
  quoteCount: number;
  /** 客户/环境登录邮箱；换号必须重开会话 */
  loginEmail: string | null;
};

let session: SessionState | null = null;

/** 需换新邮箱+浏览器的失败（不含报价上限：上限多为 IP 级，换号无效） */
export function shouldRotateFlockSession(errorMessage?: string): boolean {
  if (!errorMessage) {
    return false;
  }
  if (hasEffectiveFlockLogin()) {
    return false;
  }
  if (
    /flock_quote_limit|thank you for your interest|报价.*上限|quote\s*limit/i.test(
      errorMessage,
    )
  ) {
    return false;
  }
  return (
    /flock_account_rejected|issue creating your account/i.test(errorMessage) ||
    /flock_account_exists|already (have|registered)/i.test(errorMessage)
  );
}

async function closeSession(): Promise<void> {
  if (!session) {
    return;
  }
  await session.browser.close().catch(() => undefined);
  session = null;
}

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

async function openSession(
  input: FlockQuoteInput,
  onStage?: FlockStageReporter,
): Promise<SessionState> {
  await closeSession();
  const url = getFlockQuoteUrl();
  const headless = resolveRpaHeadless();
  const slowMo = resolveRpaSlowMoMs();
  const loginMode = hasEffectiveFlockLogin();
  const loginEmail = getEffectiveFlockLogin()?.email ?? null;
  const account = loginMode
    ? getFlockTestAccount()
    : resolveFlockQuoteAccount(input);
  const statePath = resolveStoragePath();
  const loadStorage =
    loginMode &&
    !mustForceFlockAccountLogin() &&
    shouldLoadFlockStorageState(fs.existsSync(statePath));

  console.log(
    `[flock-session] open headed=${!headless} slowMo=${slowMo ?? 0} loginMode=${loginMode}`,
  );

  const browser = await launchRpaBrowser({
    headless,
    incognito: !loginMode || mustForceFlockAccountLogin(),
    ...(slowMo !== undefined ? { slowMo } : {}),
  });
  const context = await createRpaBrowserContext(
    browser,
    loadStorage ? { storageState: statePath } : {},
  );
  const page = await context.newPage();
  await gotoWithResilience(page, url);

  if (loginMode) {
    await reportFlockStage(onStage, "login");
    let login = await ensureFlockLoggedIn(page, context, {
      forceFreshLogin: mustForceFlockAccountLogin(),
    });
    if (!login.ok) {
      login = await ensureFlockLoggedIn(page, context, {
        reloginAttempt: true,
        forceFreshLogin: true,
      });
    }
    if (!login.ok) {
      if (isRpaHeaded()) {
        await page.waitForTimeout(5_000).catch(() => undefined);
      }
      await browser.close().catch(() => undefined);
      throw new Error(login.errorMessage);
    }
  }

  session = {
    browser,
    context,
    page,
    account,
    quoteCount: 0,
    loginEmail,
  };
  console.log(
    loginMode
      ? `[flock-session] 登录会话已就绪 email=${loginEmail ?? "?"} quotesPerEmail=${getFlockQuotesPerEmail()}`
      : `[flock-session] 新会话 email=${account.email} quotesPerEmail=${getFlockQuotesPerEmail()}`,
  );
  return session;
}

async function ensureActiveSession(
  input: FlockQuoteInput,
  url: string,
  quotesPerEmail: number,
  onStage?: FlockStageReporter,
): Promise<SessionState> {
  const loginMode = hasEffectiveFlockLogin();
  const loginEmail = getEffectiveFlockLogin()?.email ?? null;
  const customEmail = input.registration?.email?.trim();

  // 客户切换账密：必须重开会话，禁止串号
  if (session && loginMode && session.loginEmail !== loginEmail) {
    console.log(
      `[flock-session] 登录账号变更 ${session.loginEmail ?? "none"} → ${loginEmail ?? "none"}，重开会话`,
    );
    await closeSession();
  }

  if (
    !loginMode &&
    session &&
    customEmail &&
    session.account.email !== customEmail
  ) {
    await closeSession();
  }

  if (!session || (!loginMode && session.quoteCount >= quotesPerEmail)) {
    return openSession(input, onStage);
  }
  await navigateFlockNewQuote(session.page, url);
  if (loginMode) {
    await reportFlockStage(onStage, "login");
    const login = await ensureFlockLoggedIn(session.page, session.context);
    if (!login.ok) {
      await closeSession();
      return openSession(input, onStage);
    }
  }
  return session;
}

/** Worker 内复用浏览器执行 Flock 查价；上限/拒号快失败 */
export async function runFlockQuoteWithWorkerSession(
  input: FlockQuoteInput,
  onStage?: FlockStageReporter,
): Promise<FlockRunQuoteResult> {
  const quotesPerEmail = getFlockQuotesPerEmail();
  const maxRotations = hasEffectiveFlockLogin()
    ? 1
    : getFlockSessionMaxRotations();
  const url = getFlockQuoteUrl();
  let lastResult: FlockRunQuoteResult = {
    ok: false,
    quotes: [],
    errorMessage: "FLOCK_QUOTE_LIMIT：未执行查价",
  };

  for (let rotation = 0; rotation < maxRotations; rotation += 1) {
    let active: SessionState;
    try {
      active = await ensureActiveSession(input, url, quotesPerEmail, onStage);
    } catch (error) {
      const msg = error instanceof Error ? error.message : String(error);
      return { ok: false, quotes: [], errorMessage: msg };
    }

    const limitAtEntry = await flockQuoteLimitMessage(active.page);
    if (limitAtEntry) {
      console.warn(`[flock-session] 入口已达上限，快失败: ${limitAtEntry}`);
      await closeSession();
      return { ok: false, quotes: [], errorMessage: limitAtEntry };
    }

    await reportFlockStage(onStage, "open_quote");
    if (!(await waitForFlockQuoteForm(active.page))) {
      const limitMsg = await flockQuoteLimitMessage(active.page);
      if (limitMsg) {
        console.warn(`[flock-session] 表单不可用（上限），快失败`);
        await closeSession();
        return { ok: false, quotes: [], errorMessage: limitMsg };
      }
      await closeSession();
      return {
        ok: false,
        quotes: [],
        errorMessage: "QUOTE_ENTRY_UNAVAILABLE：查价表单未加载",
      };
    }

    const result = await runFlockQuoteOnPage(
      active.page,
      input,
      active.account,
      onStage,
    );
    lastResult = result;

    if (result.ok) {
      active.quoteCount += 1;
      return result;
    }

    if (
      /flock_quote_limit|thank you for your interest/i.test(
        result.errorMessage ?? "",
      )
    ) {
      await closeSession();
      return result;
    }

    if (shouldRotateFlockSession(result.errorMessage)) {
      console.warn(
        `[flock-session] ${result.errorMessage?.slice(0, 80)}… 换新邮箱 (${rotation + 1}/${maxRotations})`,
      );
      await closeSession();
      continue;
    }

    await closeSession();
    return result;
  }

  await closeSession();
  return {
    ok: false,
    quotes: [],
    errorMessage:
      lastResult.errorMessage ??
      `FLOCK_QUOTE_LIMIT：已换 ${maxRotations} 个邮箱仍无法查价`,
  };
}

export async function closeFlockWorkerSession(): Promise<void> {
  await closeSession();
}
