import { Injectable, OnModuleDestroy, OnModuleInit, Logger } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import { tenantGuardExtension } from './tenant-guard.extension';

/**
 * 连接池随**本进程声明的并发**自动放大 —— 免去手动给 DATABASE_URL 追加 `?connection_limit=`。
 *
 * 病根:Prisma 默认池大小是 `核数×2+1`,跟业务并发旋钮完全无关 → 一调并发就得手动调池。
 * 这里把两者联动:
 *   - 未设 / =1(常驻 API 进程)→ 不改 URL,走 Prisma 默认池;
 *   - >1(各 CLI 是独立 process)→ connection_limit = N×6+5,封顶 40(守住 Postgres max_connections=100);
 *   - URL 里已显式写了 connection_limit → 尊重,不覆盖(逃生口)。
 * CLI 是独立进程,只有它拿大池,常驻服务仍是小池,天然隔离。
 *
 * ⭐ 2026-07-26 修:原来只认 `PAC_COHORT_CONCURRENCY`(**摄入**的旋钮),于是
 * `recompute-persona --concurrency=N` 这类 CLI 的旋钮**不会放大池**。
 *
 * ⚠️ 这是**潜在**缺陷,咬不咬人取决于该机 .env 有没有显式写 connection_limit(核对过两台):
 *   测试服 47.251.104.47  URL 里显式 `connection_limit=30` → 本函数提前返回,池=30,不是瓶颈
 *                          (那里观测到的 9 条连接 = 8 个 worker + 1 空闲,不是池上限 —— 别再据此推断)
 *   生产   47.99.62.30    URL **没写** → 走 Prisma 默认 `核数×2+1`,4 核机 = **9**
 *                          → `--concurrency` 超过 8 就会被卡在 9,多出来的 worker 排队等连接
 * 也就是说:按 --concurrency=8 跑生产,9 条刚好够,本修复不产生收益;它的价值是**解锁更高并发**。
 *
 * 改成读**通用**的 PAC_DB_CONCURRENCY,由各 CLI 在 Nest 启动前按自己的 --concurrency 设进来;
 * PAC_COHORT_CONCURRENCY 保留为向后兼容的别名,两者取大。
 */
export function withCohortDerivedPool(rawUrl: string | undefined): string | undefined {
  if (!rawUrl) return rawUrl;
  let url: URL;
  try {
    url = new URL(rawUrl);
  } catch {
    return rawUrl; // 非标准 URL(如 unix socket)— 交给 Prisma 原样处理
  }
  if (url.searchParams.has('connection_limit')) return rawUrl;
  const num = (v: string | undefined) => Math.max(1, parseInt(v ?? '1', 10) || 1);
  const n = Math.max(num(process.env.PAC_DB_CONCURRENCY), num(process.env.PAC_COHORT_CONCURRENCY));
  if (n <= 1) return rawUrl;
  url.searchParams.set('connection_limit', String(Math.min(n * 6 + 5, 40)));
  return url.toString();
}

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger(PrismaService.name);

  constructor() {
    super({
      datasourceUrl: withCohortDerivedPool(process.env.DATABASE_URL),
      log: [
        { level: 'warn', emit: 'event' },
        { level: 'error', emit: 'event' },
      ],
    });
    // 纵深防御:$extends 返回扩展客户端,从构造器 return 让 DI 拿到的就是它(透明,
    //   services 的 this.prisma.patient 不用改)。扩展按请求级 ALS 上下文自动注入 tenantId。
    //   $extends 不可变,故只能"构造器 return"这一处接入。
    return this.$extends(tenantGuardExtension) as unknown as PrismaService;
  }

  async onModuleInit(): Promise<void> {
    await this.$connect();
    this.logger.log('Prisma connected');
  }

  async onModuleDestroy(): Promise<void> {
    await this.$disconnect();
  }
}
