import { QUOTE_TIMEOUT_MS } from "@/lib/constants/quote";
import { RpaDataInvalidError } from "@/modules/quote/types";
import type { RpaErrorCode } from "@/modules/rpa/errors";
import { RpaError } from "@/modules/rpa/errors";
import {
  isProviderLoginFailureMessage,
  PROVIDER_LOGIN_FAILED_USER_MESSAGE,
} from "@/modules/rpa/provider-login-message";

const QUOTE_TIMEOUT_SEC = Math.round(QUOTE_TIMEOUT_MS / 1_000);

/** 对外展示的询价失败 error_code（domain-rules §10 + RPA 细分） */
export type QuoteFailureCode =
  | "QUOTE_UNAVAILABLE"
  | "QUOTE_TIMEOUT"
  | "QUOTE_ENTRY_UNAVAILABLE"
  | "ADDRESS_SUGGESTION_NOT_FOUND"
  | "CARRIER_NO_CAPACITY"
  | "ADDRESS_NOT_SUPPORTED"
  | "RPA_CAPTCHA"
  | "SESSION_EXPIRED"
  | "PROVIDER_LOGIN_FAILED"
  | "RPA_DATA_INVALID"
  | "PAGE_LOAD_TIMEOUT";

/** 面向中文客户的默认文案（禁止英文技术栈细节） */
const DEFAULT_MESSAGES: Record<QuoteFailureCode, string> = {
  QUOTE_UNAVAILABLE: "暂时无法获取报价，请稍后重试",
  QUOTE_TIMEOUT: `查询超时（超过 ${QUOTE_TIMEOUT_SEC} 秒），请重试`,
  QUOTE_ENTRY_UNAVAILABLE: "报价入口暂时无法打开，请稍后重试或联系管理员",
  ADDRESS_SUGGESTION_NOT_FOUND:
    "无法匹配您已确认的地址，请重新选择提货/派送地址",
  CARRIER_NO_CAPACITY: "该线路暂无可用运力，请调整货物或地址后重试",
  ADDRESS_NOT_SUPPORTED: "该地址不在承运商服务范围内",
  RPA_CAPTCHA: "承运商触发验证码，询价已暂停，请稍后重试或联系管理员",
  SESSION_EXPIRED: "承运商登录会话已过期，请确认账密后重试",
  PROVIDER_LOGIN_FAILED: PROVIDER_LOGIN_FAILED_USER_MESSAGE,
  RPA_DATA_INVALID: "未能解析完整报价结果，请稍后重试",
  PAGE_LOAD_TIMEOUT: "承运商页面加载超时，请稍后重试",
};

const RPA_CODE_MAP: Partial<Record<RpaErrorCode, QuoteFailureCode>> = {
  QUOTE_ENTRY_UNAVAILABLE: "QUOTE_ENTRY_UNAVAILABLE",
  ADDRESS_SUGGESTION_NOT_FOUND: "ADDRESS_SUGGESTION_NOT_FOUND",
  CARRIER_NO_CAPACITY: "CARRIER_NO_CAPACITY",
  ADDRESS_NOT_SUPPORTED: "ADDRESS_NOT_SUPPORTED",
  RPA_CAPTCHA: "RPA_CAPTCHA",
  SESSION_EXPIRED: "SESSION_EXPIRED",
  PROVIDER_LOGIN_FAILED: "PROVIDER_LOGIN_FAILED",
  RPA_DATA_INVALID: "RPA_DATA_INVALID",
  PAGE_LOAD_TIMEOUT: "PAGE_LOAD_TIMEOUT",
  STRUCT_CHANGE: "QUOTE_ENTRY_UNAVAILABLE",
};

const CJK_PATTERN = /[\u4e00-\u9fff]/;
/** 仅匹配明确的 Playwright/堆栈噪声；勿把「含少量英文的中文业务句」整段判死刑 */
const TECHNICAL_NOISE_PATTERN =
  /locator\.|page\.goto|Timeout \d+\s*ms|waitFor|getByTestId|Call log|ERR_|ECONN|Prisma|webpack|playwright|browserType|net::|stack|at Object\.|node_modules/i;

const ERROR_CODE_PREFIX =
  /^(?:RPA_[A-Z0-9_]+|FLOCK_[A-Z0-9_]+|QUOTE_[A-Z0-9_]+|STRUCT_CHANGE|SESSION_EXPIRED|PAGE_LOAD_TIMEOUT|PROVIDER_LOGIN_FAILED|MOTHERSHIP_[A-Z0-9_]+|CARRIER_[A-Z0-9_]+|ADDRESS_[A-Z0-9_]+|CARGO_[A-Z0-9_]+)\s*[：:]\s*/i;

/** 常见 Playwright / MotherShip 控件 testId → 中文含义 */
const TEST_ID_ZH: Array<[RegExp, string]> = [
  [
    /address-book-accessorials-option-appointment/i,
    "预约送货（Appointment）附加服务选项",
  ],
  [/address-book-accessorials-option-/i, "地址簿附加服务选项"],
  [/auth-email-input|auth-password-input|auth-log-in-button/i, "登录表单"],
  [/quote-create-pickup-input-search/i, "提货地址搜索框"],
  [/quote-create-delivery-input-search/i, "派送地址搜索框"],
  [/quote-create-|rate-card|ship-create/i, "创建货件/报价表单控件"],
];

/** 常见英文业务/官网文案 → 中文 */
const EN_PHRASE_TO_ZH: Array<[RegExp, string]> = [
  [
    /no rates found[\s\S]*please review all required fields to proceed/i,
    "未找到可用报价。请检查并填写所有必填项后再继续",
  ],
  [/no rates found/i, "未找到可用报价"],
  [
    /please review all required fields to proceed/i,
    "请检查并填写所有必填项后再继续",
  ],
  [
    /temporarily unable to (?:obtain|get|fetch) quotes?/i,
    "暂时无法获取报价，请检查地址、货物或附加服务后重试",
  ],
  [/unable to (?:obtain|get|fetch) quotes?/i, "暂时无法获取报价"],
  [/no carriers available/i, "该线路暂无可用运力"],
  [/no capacity/i, "该线路暂无可用运力"],
  [/unable to find a carrier/i, "未找到可用承运商"],
  [/address not supported/i, "该地址不在服务范围内"],
  [/zip code is not supported/i, "该邮编不在服务范围内"],
  [/we don'?t service this area/i, "该区域暂不提供服务"],
  [/unsupported accessorial/i, "不支持该附加服务"],
  [
    /our carrier partners? do not provide[^\n.]*/i,
    "承运合作伙伴不提供此项服务",
  ],
  [
    /carrier partners? (?:don'?t|do not) provide[^\n.]*/i,
    "承运合作伙伴不提供此项服务",
  ],
  [
    /(?:pickup|pick-up)[^\n.]{0,40}residential|residential[^\n.]{0,40}(?:pickup|pick-up)/i,
    "不支持住宅地址上门取件",
  ],
  [
    /incorrect (?:email|password)|invalid (?:email|password|credentials)/i,
    "账号或密码不正确",
  ],
  [/wrong password/i, "密码不正确"],
  [/captcha|challenge/i, "触发人机验证，请稍后重试"],
];

function hasCjk(text: string): boolean {
  return CJK_PATTERN.test(text);
}

function countCjk(text: string): number {
  return (text.match(/[\u4e00-\u9fff]/g) ?? []).length;
}

/** 去掉 RPA_DATA_INVALID： 一类前缀，便于保留后面的中文原因 */
export function stripErrorCodePrefix(message: string): string {
  return message.replace(ERROR_CODE_PREFIX, "").trim();
}

function describeTestIdTarget(text: string): string | null {
  for (const [pattern, label] of TEST_ID_ZH) {
    if (pattern.test(text)) {
      return label;
    }
  }
  const m = text.match(/getByTestId\(\s*['"]([^'"]+)['"]\s*\)/i);
  if (m?.[1]) {
    return `页面控件「${m[1]}」`;
  }
  if (/getBy(?:Text|Role|Label|Placeholder)\([^)]+\)/i.test(text)) {
    return "目标页面元素";
  }
  return null;
}

/**
 * Playwright / 堆栈 / 已知错误码 → 中文根因（客户与管理端共用）。
 * 无法识别时返回空串（由调用方按 error_code 回退）。
 */
export function normalizeTechnicalReason(raw: string): string {
  const text = raw.trim();
  if (!text) {
    return "";
  }
  if (/browserType\.launch/i.test(text)) {
    return "RPA Worker 无法启动浏览器，请检查 Worker 资源、Chromium 依赖与进程日志";
  }
  if (/Executable doesn't exist|chromium_headless_shell/i.test(text)) {
    return "服务器未安装 Playwright 浏览器，请在 Worker 环境中安装浏览器后重试";
  }
  if (/QUOTE_ENTRY_UNAVAILABLE|报价入口不可用|报价页面无法打开/i.test(text)) {
    return "MotherShip 报价页面无法打开或被重定向到登录页";
  }
  if (
    /PROVIDER_LOGIN_FAILED|FLOCK_LOGIN_FAILED|仍停在登录页|账号或密码错误|账密可能错误/i.test(
      text,
    )
  ) {
    return "承运商官网登录失败：账号或密码不正确，或修改后未在本系统即时保存更新";
  }
  if (/SESSION_EXPIRED|storageState|会话已过期|登录会话失效/i.test(text)) {
    return "MotherShip 登录会话失效，需重新登录或更新登录态文件";
  }
  if (/缺少标准档报价/i.test(text)) {
    return "报价结果缺少标准档，无法展示完整运价";
  }
  if (/官网展示档位为空|报价档位数不足/i.test(text)) {
    return "未能解析出可用承运商报价档位";
  }
  if (/RPA_DATA_INVALID|无法解析.*报价|未能解析出承运商报价/i.test(text)) {
    return "MotherShip 返回的报价数据无法解析为完整档位";
  }
  if (/ADDRESS_NOT_CONFIRMED|地址.*无法定位|地址未确认/i.test(text)) {
    return "已选地址在 MotherShip 无法定位或未确认，需重新联想并确认";
  }
  if (/processing 超过|QUOTE_TIMEOUT/i.test(text)) {
    return `询价超时（超过 ${QUOTE_TIMEOUT_SEC} 秒未完成）`;
  }
  if (
    /locator\.waitFor|waiting for getBy|Timeout \d+\s*ms exceeded/i.test(text)
  ) {
    const target = describeTestIdTarget(text);
    const timeoutMatch = text.match(/Timeout\s+(\d+)\s*ms/i);
    const sec = timeoutMatch
      ? Math.round(Number(timeoutMatch[1]) / 1000)
      : null;
    const waitHint = sec != null ? `（等待约 ${sec} 秒）` : "";
    if (target) {
      return `自动化操作超时${waitHint}：未能等到「${target}」出现或变为可见。可能原因：官网页面结构变更、附加服务选项未展开、网络过慢或登录态异常。`;
    }
    return `自动化操作超时${waitHint}：未能等到目标页面元素出现或变为可见。可能原因：官网页面结构变更、网络过慢或登录态异常。`;
  }
  if (/page\.goto:|net::ERR_/i.test(text)) {
    return "打开承运商官网失败（网络连接异常或被重置），请检查 Worker 出口网络后重试";
  }
  if (/Target (?:page|context|browser) has been closed/i.test(text)) {
    return "浏览器会话已关闭，询价中断，请重试";
  }
  // 已是可读中文业务句：原样返回（管理端与客户展示共用）
  if (isChineseUserFacingMessage(text)) {
    return stripErrorCodePrefix(text);
  }
  return "";
}

/** 是否适合直接给中文客户看（含汉字且非 Playwright 堆栈噪声） */
export function isChineseUserFacingMessage(message: string): boolean {
  const trimmed = stripErrorCodePrefix(message.trim());
  if (!trimmed) return false;
  if (!hasCjk(trimmed)) return false;
  const cjk = countCjk(trimmed);
  // 业务中文句常夹杂 rate-card / Continue：汉字够多且无堆栈特征则放行
  if (cjk >= 4 && !TECHNICAL_NOISE_PATTERN.test(trimmed)) {
    return true;
  }
  if (TECHNICAL_NOISE_PATTERN.test(trimmed)) {
    return false;
  }
  return true;
}

/** 将已知英文短语替换为中文；无法识别则返回原串 */
export function translateKnownEnglishPhrases(message: string): string {
  const trimmed = message.trim();
  if (!trimmed) return trimmed;
  for (const [pattern, zh] of EN_PHRASE_TO_ZH) {
    if (pattern.test(trimmed)) {
      return zh;
    }
  }
  return trimmed;
}

function defaultMessageFor(code: QuoteFailureCode): string {
  return DEFAULT_MESSAGES[code] ?? DEFAULT_MESSAGES.QUOTE_UNAVAILABLE;
}

/**
 * 将任意错误原文转为中文用户可见文案。
 * 规则：合格中文 → 保留；已知英文/Playwright → 译中文；否则按 error_code 回退（禁止吞掉可译原因）。
 */
export function toChineseUserFacingMessage(
  message: string | null | undefined,
  errorCode?: string | null,
): string {
  const code = (errorCode ?? "QUOTE_UNAVAILABLE") as QuoteFailureCode;
  const fallback = defaultMessageFor(
    code in DEFAULT_MESSAGES ? code : "QUOTE_UNAVAILABLE",
  );

  const raw = message?.trim() ?? "";
  if (!raw) {
    return fallback;
  }
  if (
    code === "PROVIDER_LOGIN_FAILED" ||
    isProviderLoginFailureMessage(raw)
  ) {
    return PROVIDER_LOGIN_FAILED_USER_MESSAGE;
  }

  const stripped = stripErrorCodePrefix(raw);
  if (isChineseUserFacingMessage(stripped)) {
    return stripped;
  }
  if (isChineseUserFacingMessage(raw)) {
    return raw;
  }

  const translated = translateKnownEnglishPhrases(stripped || raw);
  if (translated !== (stripped || raw) && hasCjk(translated)) {
    return translated;
  }

  const fromTech = normalizeTechnicalReason(stripped || raw);
  if (fromTech) {
    return fromTech;
  }

  return fallback;
}

export function resolveQuoteFailureCode(error: unknown): QuoteFailureCode {
  if (error instanceof RpaError) {
    if (
      error.code === "PROVIDER_LOGIN_FAILED" ||
      isProviderLoginFailureMessage(error.message)
    ) {
      return "PROVIDER_LOGIN_FAILED";
    }
    return RPA_CODE_MAP[error.code] ?? "QUOTE_UNAVAILABLE";
  }
  if (error instanceof RpaDataInvalidError) {
    return "RPA_DATA_INVALID";
  }
  if (error instanceof Error && isProviderLoginFailureMessage(error.message)) {
    return "PROVIDER_LOGIN_FAILED";
  }
  if (
    error instanceof Error &&
    /Timeout \d+\s*ms|locator\.|page\.goto|net::ERR_/i.test(error.message)
  ) {
    return "PAGE_LOAD_TIMEOUT";
  }
  return "QUOTE_UNAVAILABLE";
}

export function resolveQuoteFailureMessage(
  error: unknown,
  errorCode: QuoteFailureCode,
): string {
  if (errorCode === "PROVIDER_LOGIN_FAILED") {
    return PROVIDER_LOGIN_FAILED_USER_MESSAGE;
  }
  const raw =
    error instanceof Error
      ? error.message.trim()
      : typeof error === "string"
        ? error.trim()
        : "";
  return toChineseUserFacingMessage(raw, errorCode);
}

/** GET /quotes/{id} 展示文案：强制中文客户可读 */
export function formatQuoteErrorMessage(
  errorCode: string | null | undefined,
  storedMessage?: string | null,
): string {
  return toChineseUserFacingMessage(storedMessage, errorCode);
}

/** quote_record.error_message VARCHAR(512) */
export function truncateQuoteErrorMessage(
  message: string,
  maxLen = 512,
): string {
  const trimmed = message.trim();
  if (trimmed.length <= maxLen) {
    return trimmed;
  }
  return `${trimmed.slice(0, maxLen - 1)}…`;
}
