/**
 * Test DB fail-fast guard
 *
 * 背景：共享测试容器 `ffoa-test-postgres` 在并发 CI 跑测试中途被外部命令重启时，
 * Postgres 抛 `E57P01 admin_shutdown`，后续 18 个 suite 全部因 `Server has closed the
 * connection` 失败，浪费 6 分钟 CI 时间，且真根因被埋在几百条次生错误下面。
 *
 * 本文件提供两层防护：
 *   1. `pingTestDb()`  — 在每个 suite 的 createTestApp() 开头跑 `SELECT 1`，DB 死了直接
 *      abort 整轮（不再让后续 suite 各自慢慢 18s 超时）。
 *   2. `checkAndAbortIfFatal(err)` — 命中"基础设施级"错误模式（连接被 admin 杀、
 *      连接拒绝等）时直接 process.exit(2)，把 CI 失败信号收敛到第一刻。
 *
 * 退出码 2 用于和 Jest 默认的 1（业务断言失败）区分，方便 CI / 人眼快速判断
 * "是 infra 挂了" vs "是代码 bug"。
 */

import { PrismaClient } from '@prisma/client';

const FATAL_PATTERNS: RegExp[] = [
  /E57P01/i,                        // admin_shutdown
  /administrator command/i,
  /Server has closed the connection/i,
  /Can't reach database server/i,
  /ECONNREFUSED.*543\d/i,           // postgres 端口拒绝
  /Connection terminated unexpectedly/i,
];

let aborted = false;
let healthClient: PrismaClient | null = null;

function banner(reason: string, err?: unknown): void {
  const line = '━'.repeat(72);
  // 使用 stderr 直写，避免被 jest 的输出缓冲吞掉
  process.stderr.write(`\n${line}\n`);
  process.stderr.write(`🚨 [test-fail-fast] 检测到测试基础设施不可用，立即终止整轮测试\n`);
  process.stderr.write(`原因：${reason}\n`);
  if (err) {
    const msg = err instanceof Error ? err.stack || err.message : String(err);
    process.stderr.write(`${msg}\n`);
  }
  process.stderr.write(`（避免后续 suite 全部因 connection closed 失败浪费 CI 时间）\n`);
  process.stderr.write(`${line}\n\n`);
}

function abortRun(reason: string, err?: unknown): never {
  if (!aborted) {
    aborted = true;
    banner(reason, err);
  }
  // 退出码 2：infra 故障，区别于 jest 默认的 1（业务断言失败）
  process.exit(2);
}

function isFatal(err: unknown): boolean {
  const msg = err instanceof Error ? err.message : String(err ?? '');
  return FATAL_PATTERNS.some((p) => p.test(msg));
}

/**
 * 命中致命 DB 错误模式则立即 abort，否则原样返回供调用方继续处理。
 * 调用方需要自己 throw / 走原有错误流程——本函数只负责"infra 死了"的快速逃生。
 */
export function checkAndAbortIfFatal(err: unknown, origin: string): void {
  if (isFatal(err)) {
    abortRun(`${origin} 命中致命 DB 错误模式`, err);
  }
}

/**
 * 在 createTestApp 入口调用：DB 不通时立刻 abort 整轮，避免每个 suite 各跑 18s 超时。
 *
 * 成本：每个 suite 多一次 `SELECT 1`（~几 ms）；
 * 收益：DB 死时整轮终止时间从 ~6 分钟降到 ~1 秒。
 */
export async function pingTestDb(): Promise<void> {
  if (!healthClient) {
    healthClient = new PrismaClient({ log: ['error'] });
  }
  try {
    await healthClient.$queryRaw`SELECT 1`;
  } catch (err) {
    abortRun('createTestApp 前 DB 健康检查失败 (SELECT 1)', err);
  }
}

// 进程级兜底：未捕获的 fatal 错误也触发 abort（防止异步任务里 prisma 报错没被 await 到）
const onUnhandled = (origin: string) => (reason: unknown) => {
  if (isFatal(reason)) {
    abortRun(`${origin} 未捕获致命 DB 错误`, reason);
  }
};
process.on('unhandledRejection', onUnhandled('unhandledRejection'));
process.on('uncaughtException', onUnhandled('uncaughtException'));
