/**
 * Cold Import CLI 入口
 *
 * Usage:
 *   pnpm cold-import -- --dir=./data/jvs-dw
 *   pnpm cold-import -- --dir=./data/jvs-dw --dry-run
 *
 * 通过 NestApplicationContext 启动(不起 HTTP),复用 DI:Prisma / ColdImportService。
 */

import { NestFactory } from '@nestjs/core';
import { Logger } from '@nestjs/common';
import { AppModule } from '../app.module';
import {
  ColdImportService,
  SyncAlreadyRunningError,
} from '../modules/sync/cold-import/cold-import.service';

interface CliArgs {
  dir?: string;
  dryRun: boolean;
  help: boolean;
  incremental: boolean;
  cohortBatchSize?: number | null; // null = 显式禁用分批,undefined = 用 env / 默认
  clinics?: string[]; // --clinics=X,Y:只列"在这些诊所看过"的患者(其全部相关主体仍全摄);不传=全量
  since?: string; // 收窄到"该日期后有来诊"的患者(YYYY-MM-DD);--months=N 会换算成日期
  months?: number; // --months=N:最近 N 个月(换算成 since 的 cutoff 日期)
}

function parseArgs(argv: string[]): CliArgs {
  const args: CliArgs = { dryRun: false, help: false, incremental: 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 === '--incremental') args.incremental = true;
    else if (a === '--no-cohort') args.cohortBatchSize = 0; // 显式禁用分批,跑 single-shot
    else if (a.startsWith('--cohort-batch=')) {
      const n = parseInt(a.slice('--cohort-batch='.length), 10);
      args.cohortBatchSize = Number.isFinite(n) && n >= 0 ? n : undefined;
    } else if (a.startsWith('--clinics=')) {
      args.clinics = a
        .slice('--clinics='.length)
        .split(',')
        .map((s) => s.trim())
        .filter(Boolean);
    } else if (a.startsWith('--since=')) {
      args.since = a.slice('--since='.length).trim();
    } else if (a.startsWith('--months=')) {
      const n = parseInt(a.slice('--months='.length), 10);
      if (Number.isFinite(n) && n > 0) args.months = n;
    } else if (a.startsWith('--dir=')) args.dir = a.slice('--dir='.length);
  }
  // --since 显式优先;否则 --months=N 换算成 cutoff 日期(今天往前推 N 个月)。
  if (!args.since && args.months && args.months > 0) {
    const d = new Date();
    d.setMonth(d.getMonth() - args.months);
    args.since = d.toISOString().slice(0, 10); // YYYY-MM-DD
  }
  return args;
}

function printHelp() {
  const lines = [
    '',
    'Cold Import CLI',
    '',
    '把宿主脱敏导出的目录(manifest.yaml + *.csv/*.json)装载到 PAC',
    '',
    'Usage:',
    '  pnpm cold-import -- --dir=<dir> [--dry-run]',
    '',
    'Options:',
    '  --dir=<path>          必填,manifest.yaml 所在目录(相对 / 绝对路径)',
    '  --dry-run             只读 + 翻译预览,不写库',
    '  --incremental         增量模式(读上次 cursor,SQL 注 WHERE > cursor;首跑等价全量)',
    '  --cohort-batch=<N>    按 patient 分批跑(default 5000;env PAC_COHORT_BATCH_SIZE 兜底)',
    '  --no-cohort           显式禁用分批,跑 single-shot(文件源或调试场景)',
    '  --clinics=<id,id>     只列"在这些诊所看过"的患者(其全部相关主体仍全摄;需 manifest',
    '                        cohort.clinic_scope 配置)。不传=全量。用于按诊所分批补摄,幂等追加不清空。',
    '  --since=<YYYY-MM-DD>  只列"该日期后有来诊"的患者(按 cohort.list_cursor_column 收窄;',
    '                        被选中患者的全部历史仍全摄)。如 --since=2026-01-01 = 今年。',
    '  --months=<N>          只列"最近 N 个月有来诊"的患者(换算成 --since 的 cutoff;--since 显式优先)。',
    '                        --clinics 与 --since/--months 可叠加(AND 求交)。',
    '  --help, -h            显示本帮助',
    '',
    'Examples:',
    '  pnpm cold-import -- --dir=./data/jvs-dw --dry-run',
    '  pnpm cold-import -- --dir=./data/jvs-dw',
    '  pnpm cold-import -- --dir=./data/jvs-dw --cohort-batch=2000',
    '  pnpm cold-import -- --dir=./data/jvs-dw --months=12            # 最近 12 个月有来诊的患者',
    '  pnpm cold-import -- --dir=./data/jvs-dw --since=2026-01-01     # 今年有来诊的患者',
    '  pnpm cold-import -- --dir=./data/jvs-dw --clinics=<orgId> --months=24  # 某诊所近 24 月(叠加)',
    '  pnpm cold-import -- --dir=./data/jvs-dw --incremental',
    '',
  ];
  // eslint-disable-next-line no-console
  console.log(lines.join('\n'));
}

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

  const logger = new Logger('cold-import:cli');
  logger.log(
    `Starting cold-import CLI(dir=${args.dir}, dryRun=${args.dryRun}, ` +
      `incremental=${args.incremental}, cohortBatch=${args.cohortBatchSize ?? '(default/env)'}` +
      `${args.clinics?.length ? `, clinics=${args.clinics.join(',')}` : ''}` +
      `${args.since ? `, since=${args.since}${args.months ? `(--months=${args.months})` : ''}` : ''})`,
  );

  const app = await NestFactory.createApplicationContext(AppModule, {
    logger: ['log', 'warn', 'error'],
  });

  try {
    const svc = app.get(ColdImportService);
    const result = await svc.importDirectory(args.dir!, {
      dryRun: args.dryRun,
      incremental: args.incremental,
      cohortBatchSize: args.cohortBatchSize,
      clinics: args.clinics,
      since: args.since,
    });

    logger.log('─────────────────────────────────────────');
    logger.log(`Result:`);
    logger.log(`  runId:                 ${result.runId}`);
    logger.log(`  host:                  ${result.hostName} (${result.hostId})`);
    logger.log(`  tenants:               [${result.tenantIds.join(',')}]`);
    logger.log(`  dryRun:                ${result.dryRun}`);
    logger.log(`  status:                ${result.status}`);
    logger.log(`  syncLogId:             ${result.syncLogId ?? '(dryRun: no log)'}`);
    logger.log(`  patients upserted:     ${result.totals.patientsUpserted}`);
    logger.log(`  transactions written:  ${result.totals.transactionsWritten}`);
    logger.log(`  duplicates (idem):     ${result.totals.duplicates}`);
    logger.log(`  failed:                ${result.totals.failed}`);
    logger.log(
      `  facts:                 created=${result.totals.factsCreated} superseded=${result.totals.factsSuperseded} unchanged=${result.totals.factsUnchanged} evidence=${result.totals.factsEvidenceAppended} failed=${result.totals.factsFailed}`,
    );
    logger.log(`  per resource:`);
    for (const r of result.perResource) {
      logger.log(
        `    - ${r.resource}: fetched=${r.fetched} patients=${r.patientsUpserted} txns=${r.transactionsWritten} dups=${r.duplicates} failed=${r.failed} | facts(c=${r.factsCreated} s=${r.factsSuperseded} u=${r.factsUnchanged} e=${r.factsEvidenceAppended} f=${r.factsFailed})`,
      );
      if (args.dryRun && r.sampleCanonical.length > 0) {
        logger.log(`      sample canonical:`);
        for (const sample of r.sampleCanonical) {
          logger.log(`        ${JSON.stringify(sample)}`);
        }
      }
    }
    logger.log('─────────────────────────────────────────');
  } catch (err) {
    if (err instanceof SyncAlreadyRunningError) {
      logger.error(`并发拦截:已有其他 sync 在跑同 host:${err.message}`);
      logger.error('如果上次 sync 异常崩了未清理,手动 SQL 清:');
      logger.error(
        `  UPDATE sync_logs SET status='aborted', ended_at=NOW() ` +
          `WHERE host_id='<hostId>' AND status='running' AND started_at < NOW() - INTERVAL '12 hours';`,
      );
      process.exitCode = 4; // 4 = lock conflict(区分于 2 = 真失败 / 3 = bootstrap 崩)
    } else {
      logger.error('Cold import 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);
});
