/**
 * Reparse CLI — 用已存的 transaction.rawPayload 离线重跑装配+解析(不连 DW)。
 *
 * 改了 assembler yaml(enum/keyword/field_mapping / transforms)后,把存量 fact 按新口径
 * 重新衍生(版本流 supersede),**不 truncate、不碰账本、不拉 DW**,执行历史/分配全保住。
 *
 * Usage:
 *   pnpm reparse -- --host=jvs-dw --subject-type=diagnosis            # 实跑 + 定向重算
 *   pnpm reparse -- --host=jvs-dw --subject-type=diagnosis --dry-run  # 只报会改多少,不写
 *   pnpm reparse -- --host=jvs-dw --patient=<uuid>,<uuid>             # 限定患者
 *   pnpm reparse -- --host=jvs-dw --subject-type=diagnosis --no-recompute  # 只重衍 fact,稍后单独 recompute
 *
 * 通过 NestApplicationContext 启动(不起 HTTP),复用 DI。
 */
import { NestFactory } from '@nestjs/core';
import { Logger } from '@nestjs/common';
import { AppModule } from '../app.module';
import { PrismaService } from '../prisma/prisma.service';
import { ColdImportService } from '../modules/sync/cold-import/cold-import.service';
import { PersonaService } from '../modules/persona/persona.service';
import { PlanEngineService } from '../modules/plan/engine/plan-engine.service';

interface CliArgs {
  dir: string;
  host: string;
  subjectTypes?: string[];
  patientIds?: string[];
  dryRun: boolean;
  recompute: boolean;
  help: boolean;
}

function parseArgs(argv: string[]): CliArgs {
  const args: CliArgs = { dir: './data/jvs-dw', host: 'jvs-dw', dryRun: false, recompute: true, help: false };
  for (const a of argv) {
    if (a === '--help' || a === '-h') args.help = true;
    else if (a === '--dry-run') args.dryRun = true;
    else if (a === '--no-recompute') args.recompute = false;
    else if (a.startsWith('--dir=')) args.dir = a.slice('--dir='.length);
    else if (a.startsWith('--host=')) args.host = a.slice('--host='.length);
    else if (a.startsWith('--subject-type=')) args.subjectTypes = a.slice('--subject-type='.length).split(',').map((s) => s.trim()).filter(Boolean);
    else if (a.startsWith('--patient=')) args.patientIds = a.slice('--patient='.length).split(',').map((s) => s.trim()).filter(Boolean);
  }
  return args;
}

async function main(): Promise<void> {
  const logger = new Logger('reparse-cli');
  const args = parseArgs(process.argv.slice(2));
  if (args.help) {
    // eslint-disable-next-line no-console
    console.log(
      'pnpm reparse -- --host=<name> [--dir=./data/<host>] [--subject-type=diagnosis,treatment]\n' +
        '               [--patient=<uuid>,...] [--dry-run] [--no-recompute]',
    );
    return;
  }

  const app = await NestFactory.createApplicationContext(AppModule, { logger: ['log', 'warn', 'error'] });
  try {
    const prisma = app.get(PrismaService);
    const coldImport = app.get(ColdImportService);
    const persona = app.get(PersonaService);
    const planEngine = app.get(PlanEngineService);

    logger.log(
      `reparse start: host=${args.host} dir=${args.dir} ` +
        `subjectTypes=${args.subjectTypes?.join(',') ?? 'ALL'} ` +
        `patients=${args.patientIds?.length ?? 'all'} dryRun=${args.dryRun} recompute=${args.recompute}`,
    );

    const result = await coldImport.reparseFromTransactions({
      dir: args.dir,
      hostName: args.host,
      subjectTypes: args.subjectTypes,
      patientIds: args.patientIds,
      dryRun: args.dryRun,
    });

    if (args.dryRun) {
      logger.log(
        `[DRY-RUN] 上方 reparse[dry] 日志列出了各资源的源行数 → 会装配的 canonical 数。` +
          `不写库。去掉 --dry-run 实跑(非破坏:版本流 supersede 变更的、跳过不变的,会精确报 superseded/unchanged)。`,
      );
      await app.close();
      return;
    }

    const totalSuperseded = result.perResource.reduce((s, r) => s + r.factsSuperseded, 0);
    const totalCreated = result.perResource.reduce((s, r) => s + r.factsCreated, 0);
    logger.log(
      `reparse fact 重衍完成:superseded=${totalSuperseded} created=${totalCreated} ` +
        `affectedPatients=${result.affectedPatientIds.length}`,
    );

    if (!args.recompute) {
      logger.log(`--no-recompute:跳过 persona/plan 重算。记得稍后跑 recompute-persona / recompute-plans。`);
      await app.close();
      return;
    }

    // 定向重算受影响患者(persona force=true:reparse 不动 transaction 水位,需跳幂等闸)
    const ids = result.affectedPatientIds;
    logger.log(`定向重算 ${ids.length} 个患者的 persona + plan …`);
    let done = 0;
    let personaErr = 0;
    let planErr = 0;
    for (const patientId of ids) {
      const patient = await prisma.patient.findUnique({
        where: { id: patientId },
        select: { hostId: true, tenantId: true },
      });
      if (!patient) continue;
      try {
        await persona.recompute({ patientId, source: 'reparse', force: true });
      } catch (e) {
        personaErr++;
        logger.warn(`persona recompute 失败 patient=${patientId}: ${(e as Error).message}`);
      }
      try {
        await planEngine.recomputeForPatient({ hostId: patient.hostId, tenantId: patient.tenantId, patientId });
      } catch (e) {
        planErr++;
        logger.warn(`plan recompute 失败 patient=${patientId}: ${(e as Error).message}`);
      }
      if (++done % 200 === 0) logger.log(`  recompute 进度 ${done}/${ids.length}`);
    }
    logger.log(`reparse DONE:重算 ${done} 患者(persona 失败 ${personaErr} / plan 失败 ${planErr})`);
    await app.close();
  } catch (err) {
    logger.error(`reparse 失败:${err instanceof Error ? err.message : String(err)}`);
    await app.close();
    process.exitCode = 1;
  }
}

void main();
