import type { Page } from "playwright";
import { RPA_PAGE_GOTO_TIMEOUT_MS } from "@/lib/constants/rpa";

const GOTO_STRATEGIES = ["domcontentloaded", "commit", "load"] as const;

function sleep(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export function readPageGotoTimeoutMs(): number {
  const raw = process.env.RPA_PAGE_GOTO_TIMEOUT_MS?.trim();
  if (!raw) {
    return RPA_PAGE_GOTO_TIMEOUT_MS;
  }
  const parsed = Number(raw);
  return Number.isFinite(parsed) && parsed >= 10_000
    ? parsed
    : RPA_PAGE_GOTO_TIMEOUT_MS;
}

function readGotoRetryCount(): number {
  const raw = process.env.RPA_GOTO_RETRY_COUNT?.trim();
  const parsed = Number(raw);
  if (Number.isFinite(parsed) && parsed >= 1 && parsed <= 5) {
    return Math.floor(parsed);
  }
  return 3;
}

/** 网络层瞬时错误 — 可重试 */
export function isTransientNavigationError(error: unknown): boolean {
  const msg = error instanceof Error ? error.message : String(error ?? "");
  return /ERR_CONNECTION_RESET|ERR_CONNECTION_CLOSED|ERR_NETWORK_CHANGED|ERR_TIMED_OUT|ECONNRESET|ETIMEDOUT|NS_ERROR_NET_RESET|net::ERR_/i.test(
    msg,
  );
}

/** Priority1 等外站导航失败 — 用户可读中文 */
export function formatExternalSiteNavigationError(
  siteLabel: string,
  error: unknown,
): string {
  const detail = error instanceof Error ? error.message : String(error ?? "");
  if (/ERR_CONNECTION_RESET|ECONNRESET/i.test(detail)) {
    return (
      `无法连接 ${siteLabel}（连接被重置）。` +
      "请确认本机或服务器可访问该官网，或联系管理员检查网络与代理配置后重试。"
    );
  }
  if (/ERR_TIMED_OUT|ETIMEDOUT|Timeout/i.test(detail)) {
    return (
      `打开 ${siteLabel} 超时。` +
      "请稍后重试，或联系管理员检查网络与代理配置。"
    );
  }
  return `无法打开 ${siteLabel}，请稍后重试或联系管理员`;
}

/**
 * page.goto 多策略 + 瞬时网络错误重试（与 MotherShip quote-page-adapter 一致并增强）
 */
export async function gotoWithResilience(
  page: Page,
  url: string,
  options?: { timeoutMs?: number },
): Promise<void> {
  const timeout = options?.timeoutMs ?? readPageGotoTimeoutMs();
  const maxAttempts = readGotoRetryCount();
  let lastError: Error | undefined;

  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
    for (const waitUntil of GOTO_STRATEGIES) {
      try {
        await page.goto(url, { waitUntil, timeout });
        return;
      } catch (error) {
        lastError = error instanceof Error ? error : new Error(String(error));
      }
    }

    const transient = isTransientNavigationError(lastError);
    if (attempt < maxAttempts && transient) {
      const backoffMs = 1_500 * attempt;
      console.warn(
        `[page-goto] 第 ${attempt}/${maxAttempts} 次打开失败，${backoffMs}ms 后重试: ${url}`,
      );
      await sleep(backoffMs);
      continue;
    }
    break;
  }

  throw lastError ?? new Error(`无法打开 ${url}`);
}
