/**
 * AI 话术生成 CLI — harness 测试入口
 *
 * 用法:
 *   pnpm ai:gen-script -- --plan-id=<uuid>             # 用 plan id
 *   pnpm ai:gen-script -- --pid=F1001                  # 用 patient externalId(自动取该 patient active plan)
 *   pnpm ai:gen-script -- --pid=F1001 --bust-cache     # 跳过缓存重新调
 *   pnpm ai:gen-script -- --pid=F1001 --dry-run        # 不写 PlanScript 表
 *   pnpm ai:gen-script -- --pid=F1001 --model=deepseek-v4-flash  # 切模型
 *
 * 输入完全确定(不重新 pull 患者数据),仅用于验证 prompt + model 改动效果。
 */
import { NestFactory } from '@nestjs/core';
import { Logger } from '@nestjs/common';
import { AppModule } from '../app.module';
import { PrismaService } from '../prisma/prisma.service';
import { PlanScriptOrchestrator } from '../modules/ai/orchestrators/plan-script.orchestrator';

interface CliArgs {
  planId?: string;
  pid?: string;
  host: string;
  tenant: string;
  bustCache: boolean;
  dryRun: boolean;
  model?: string;
  tier?: 'stable' | 'standard' | 'deep';
  help: boolean;
}

function parseArgs(argv: string[]): CliArgs {
  const out: CliArgs = {
    host: 'demo',
    tenant: 't_demo',
    bustCache: false,
    dryRun: false,
    help: false,
  };
  for (const a of argv) {
    if (a === '--help' || a === '-h') out.help = true;
    else if (a === '--bust-cache') out.bustCache = true;
    else if (a === '--dry-run') out.dryRun = true;
    else if (a.startsWith('--plan-id=')) out.planId = a.slice('--plan-id='.length);
    else if (a.startsWith('--pid=')) out.pid = a.slice('--pid='.length);
    else if (a.startsWith('--host=')) out.host = a.slice('--host='.length);
    else if (a.startsWith('--tenant=')) out.tenant = a.slice('--tenant='.length);
    else if (a.startsWith('--model=')) out.model = a.slice('--model='.length);
    else if (a.startsWith('--tier=')) out.tier = a.slice('--tier='.length) as CliArgs['tier'];
  }
  return out;
}

function printHelp() {
  // eslint-disable-next-line no-console
  console.log(`
AI Plan Script Generator CLI

Usage:
  pnpm ai:gen-script -- --plan-id=<uuid>
  pnpm ai:gen-script -- --pid=<patient-external-id>
  pnpm ai:gen-script -- --pid=F1001 --bust-cache       # 跳过缓存,真实调 LLM
  pnpm ai:gen-script -- --pid=F1001 --dry-run          # 不写 PlanScript
  pnpm ai:gen-script -- --pid=F1001 --model=deepseek-v4-flash

Options:
  --plan-id=<uuid>    直接指定 plan id
  --pid=<str>         patient externalId(自动取该患者 active/assigned plan)
  --host=<str>        默认 demo
  --tenant=<str>      默认 t_demo
  --bust-cache        强制重跑(不走 prompt cache)
  --dry-run           只生成不写库
  --model=<id>        覆盖默认模型(deepseek-v4-pro / deepseek-v4-flash)
`);
}

async function bootstrap() {
  const args = parseArgs(process.argv.slice(2));
  if (args.help || (!args.planId && !args.pid)) {
    printHelp();
    process.exit(args.help ? 0 : 1);
  }

  const logger = new Logger('ai:gen-script');
  const app = await NestFactory.createApplicationContext(AppModule, {
    logger: ['warn', 'error', 'log'],
  });

  try {
    const prisma = app.get(PrismaService);
    const orchestrator = app.get(PlanScriptOrchestrator);

    // 解析 planId
    let planId = args.planId;
    if (!planId && args.pid) {
      const host = await prisma.host.findUnique({ where: { name: args.host } });
      if (!host) {
        logger.error(`host "${args.host}" not found`);
        process.exit(2);
      }
      // findFirst:source_unit 加入唯一键后,CLI 按 externalId 查不带命名空间,用 findFirst 取一条
      const patient = await prisma.patient.findFirst({
        where: { hostId: host.id, tenantId: args.tenant, externalId: args.pid },
      });
      if (!patient) {
        logger.error(`patient externalId="${args.pid}" not found in ${args.host}/${args.tenant}`);
        process.exit(2);
      }
      const plan = await prisma.followupPlan.findFirst({
        where: {
          patientId: patient.id,
          status: { in: ['active', 'assigned'] },
        },
        orderBy: { version: 'desc' },
      });
      if (!plan) {
        logger.error(`no active/assigned plan for patient ${args.pid}`);
        process.exit(2);
      }
      planId = plan.id;
    }

    const t0 = Date.now();
    const result = await orchestrator.generate(planId!, {
      bustCache: args.bustCache,
      dryRun: args.dryRun,
      modelIdOverride: args.model,
      tier: args.tier,
    });
    const elapsed = Date.now() - t0;

    // eslint-disable-next-line no-console
    console.log(`
═══════════════════════════════════════════════════════════════════════════
 AI Plan Script Generated
───────────────────────────────────────────────────────────────────────────
  planId:            ${result.planId}
  planScriptId:      ${result.planScriptId ?? '(dry-run)'}
  agentInvocationId: ${result.agentInvocationId}
  source:            ${result.source}
  cacheHit:          ${result.cacheHit}
  costYuan:         ${result.costYuan}
  elapsed:           ${elapsed}ms
───────────────────────────────────────────────────────────────────────────
${result.content}
═══════════════════════════════════════════════════════════════════════════

# 结构化原始输出(给 eval / 调试):
${JSON.stringify(result.structured, null, 2)}
`);
  } catch (err) {
    logger.error('Generation failed:');
    logger.error(err instanceof Error ? (err.stack ?? err.message) : String(err));
    process.exitCode = 2;
  } finally {
    await app.close();
  }
}

bootstrap().catch((err) => {
  // eslint-disable-next-line no-console
  console.error(err);
  process.exit(3);
});
