/**
 * Recompute Plans CLI — 批量跑 PlanEngine,产 FollowupPlan + PlanReason
 *
 * Usage:
 *   pnpm recompute-plans                   # 默认 host=demo,全量
 *   pnpm recompute-plans -- --host=friday
 *   pnpm recompute-plans -- --host=jvs-dw --pids=998421,xxx   # 只重算指定 externalId(定向,O(子集))
 */
import { NestFactory } from '@nestjs/core';
import { Logger } from '@nestjs/common';
import { AppModule } from '../app.module';
import { PlanEngineService } from '../modules/plan/engine/plan-engine.service';
import { PrismaService } from '../prisma/prisma.service';

interface Args {
  host: string;
  pids?: string[]; // 指定 externalId(逗号分隔)→ 只重算这些患者(定向,recomputeForPatient)
}

function parseArgs(argv: string[]): Args {
  const args: Args = { host: 'demo' };
  for (const a of argv) {
    if (a.startsWith('--host=')) args.host = a.slice('--host='.length);
    else if (a.startsWith('--pids=')) {
      args.pids = a.slice('--pids='.length).split(',').map((s) => s.trim()).filter(Boolean);
    }
  }
  return args;
}

async function bootstrap() {
  const args = parseArgs(process.argv.slice(2));
  const logger = new Logger('recompute-plans');
  // 连接池对齐本进程的写并发(PlanEngine 的 upsert 段用 PAC_PLAN_BATCH_CONCURRENCY,默认 8)。
  // 必须在 NestFactory 之前设 —— PrismaService 在容器初始化时就定池,晚了不生效。
  // 不设的话池是 Prisma 默认「核数×2+1」(生产 4 核 = 9),把 batch 并发调到 9 以上就会被卡住。
  // 详见 prisma.service.ts 的 withCohortDerivedPool。
  if (!process.env.PAC_DB_CONCURRENCY) {
    const n = Math.max(1, Number(process.env.PAC_PLAN_BATCH_CONCURRENCY) || 8);
    if (n > 1) process.env.PAC_DB_CONCURRENCY = String(n);
  }
  const app = await NestFactory.createApplicationContext(AppModule, {
    logger: ['log', 'warn', 'error'],
  });

  try {
    const prisma = app.get(PrismaService);
    const engine = app.get(PlanEngineService);

    const host = await prisma.host.findUnique({ where: { name: args.host } });
    if (!host) throw new Error(`Host '${args.host}' not found`);

    // ── 定向模式:只重算指定 externalId 的患者(recomputeForPatient,O(子集))──
    if (args.pids?.length) {
      const patients = await prisma.patient.findMany({
        where: { hostId: host.id, externalId: { in: args.pids } },
        select: { id: true, tenantId: true, externalId: true },
      });
      logger.log(`▶ 定向重算 ${patients.length} patients(host=${args.host})...`);
      let created = 0, closed = 0, failed = 0;
      for (const p of patients) {
        try {
          const r = await engine.recomputeForPatient({ hostId: host.id, tenantId: p.tenantId, patientId: p.id });
          created += r.plansCreated;
          closed += r.plansClosed;
        } catch (err) {
          failed++;
          logger.error(`  ${p.externalId} FAILED: ${err instanceof Error ? err.message : err}`);
        }
      }
      logger.log(`Done(定向):patients=${patients.length} plansCreated=${created} plansClosed=${closed} failed=${failed}`);
      return;
    }

    // 取该 host 第一个 tenant(demo 场景固定一个)
    const tenants = await prisma.patient.findMany({
      where: { hostId: host.id },
      select: { tenantId: true },
      distinct: ['tenantId'],
    });
    if (tenants.length === 0) throw new Error('No tenants found for host');

    for (const t of tenants) {
      logger.log(`▶ Running engine for host=${args.host} tenant=${t.tenantId} ...`);
      const r = await engine.runAllForHost({
        hostId: host.id,
        tenantId: t.tenantId,
      });
      logger.log(`──────────────────────────────────────────────────────`);
      logger.log(`Result(${t.tenantId}):`);
      logger.log(`  scenariosRun:         ${r.scenariosRun}`);
      logger.log(`  patientsHit:          ${r.patientsHit}`);
      logger.log(`  plansCreated:         ${r.plansCreated}`);
      logger.log(`  plansSuperseded:      ${r.plansSuperseded}`);
      logger.log(`  plansUnchanged:       ${r.plansUnchanged}`);
      logger.log(`  plansSkippedAssigned: ${r.plansSkippedAssigned}`);
      logger.log(`  plansSuppressed:      ${r.plansSuppressed}`);
      logger.log(`  plansClosed:          ${r.plansClosed}`);
      logger.log(`  duration:             ${r.durationMs}ms`);
      logger.log(`──────────────────────────────────────────────────────`);
    }
  } catch (err) {
    new Logger('recompute-plans').error(err instanceof Error ? err.stack : String(err));
    process.exitCode = 2;
  } finally {
    await app.close();
  }
}

bootstrap().catch((err) => {
  console.error(err);
  process.exit(3);
});
