/**
 * Patient Timeline CLI — Day 7 端到端验证
 *
 * 通过 NestApplicationContext 调 PatientService.getTimeline(绕 HTTP / 鉴权,W2 demo)。
 * 取 demo host / t_demo tenant 下指定 externalId 的 patient,渲染时间轴。
 *
 * 跑:
 *   pnpm timeline -- --pid=F1001         # 用 host externalId
 *   pnpm timeline -- --pid=F1001 --limit=20
 *   pnpm timeline -- --id=<uuid>         # 也可直接 patient.id
 */
import { NestFactory } from '@nestjs/core';
import { Logger } from '@nestjs/common';
import { AppModule } from '../app.module';
import { PrismaService } from '../prisma/prisma.service';
import { PatientService } from '../modules/patient/patient.service';
import type { TenantScopeContext } from '../common/decorators/tenant-scope.decorator';

interface CliArgs {
  pid?: string; // host externalId
  id?: string; // PAC patient.id (uuid)
  host: string;
  tenant: string;
  limit: number;
  help: boolean;
}

function parseArgs(argv: string[]): CliArgs {
  const out: CliArgs = {
    host: 'demo',
    tenant: 't_demo',
    limit: 50,
    help: false,
  };
  for (const a of argv) {
    if (a === '--help' || a === '-h') out.help = true;
    else if (a.startsWith('--pid=')) out.pid = a.slice('--pid='.length);
    else if (a.startsWith('--id=')) out.id = a.slice('--id='.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('--limit=')) out.limit = Math.max(1, Number(a.slice('--limit='.length)) || 50);
  }
  return out;
}

function printHelp() {
  // eslint-disable-next-line no-console
  console.log(`
Patient Timeline CLI

Usage:
  pnpm timeline -- --pid=<host-external-id> [--limit=N]
  pnpm timeline -- --id=<patient-uuid> [--limit=N]

Options:
  --pid=<str>       Host external id(如 F1001)
  --id=<uuid>       PAC patient.id
  --host=<str>  默认 demo
  --tenant=<str>    默认 t_demo
  --limit=<n>       默认 50
`);
}

function formatRow(idx: number, f: TimelineFact): string {
  const when = f.occurredAt ?? f.plannedFor ?? f.createdAt;
  const date = when.slice(0, 10);
  const kindTag = f.kind === 'planned' ? '[P]' : '[A]';
  const title = f.title ?? f.subjectId;
  const tail = f.summary ? `  — ${f.summary}` : '';
  return `  ${String(idx + 1).padStart(2, ' ')}. ${date} ${kindTag} ${f.type.padEnd(20)} v${f.version} ${title}${tail}`;
}

interface TimelineFact {
  kind: string;
  type: string;
  version: number;
  occurredAt: string | null;
  plannedFor: string | null;
  createdAt: string;
  title: string | null;
  summary: string | null;
  subjectId: string;
  content: Record<string, unknown>;
}

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

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

  try {
    const prisma = app.get(PrismaService);
    const svc = app.get(PatientService);

    const host = await prisma.host.findUnique({ where: { name: args.host } });
    if (!host) {
      logger.error(`host "${args.host}" not found`);
      process.exit(2);
    }

    // 解析 patient.id
    let patientId = args.id;
    if (!patientId && args.pid) {
      // findFirst:source_unit 加入唯一键后,CLI 按 externalId 查不带命名空间,用 findFirst 取一条
      const p = await prisma.patient.findFirst({
        where: { hostId: host.id, tenantId: args.tenant, externalId: args.pid },
      });
      if (!p) {
        logger.error(`patient with externalId="${args.pid}" not found in ${args.host}/${args.tenant}`);
        process.exit(2);
      }
      patientId = p.id;
    }

    const scope: TenantScopeContext = {
      hostId: host.id,
      tenantId: args.tenant,
      clinicIds: [], // W2 demo:暂不做诊所级过滤(timeline 不读 clinicIds)
      sourceUnits: [], // 不限品牌(CLI 调试上下文)
      userId: 'cli',
    };

    const result = await svc.getTimeline(scope, patientId!, args.limit);

    // eslint-disable-next-line no-console
    console.log(`
═══════════════════════════════════════════════════════════════════════════
 Patient Timeline — ${result.patient.name ?? '(no name)'}(${result.patient.externalId})
───────────────────────────────────────────────────────────────────────────
  id:        ${result.patient.id}
  phone:     ${result.patient.phone ?? '-'}
  gender:    ${result.patient.gender ?? '-'}
  birthDate: ${result.patient.birthDate?.slice(0, 10) ?? '-'}
  status:    ${result.patient.status}
───────────────────────────────────────────────────────────────────────────
  ${result.facts.length} active fact(s):
`);

    if (result.facts.length === 0) {
      // eslint-disable-next-line no-console
      console.log('  (no facts — patient 主档可能刚 upsert,还没有 transaction 衍生 fact)');
    } else {
      // 按 kind 分组展示
      const actual = result.facts.filter((f) => f.kind === 'actual');
      const planned = result.facts.filter((f) => f.kind === 'planned');

      // eslint-disable-next-line no-console
      console.log('  ── ACTUAL(已发生)──');
      actual.forEach((f, i) => console.log(formatRow(i, f as TimelineFact)));
      // eslint-disable-next-line no-console
      console.log('\n  ── PLANNED(未来安排)──');
      planned.forEach((f, i) => console.log(formatRow(i, f as TimelineFact)));
    }
    // eslint-disable-next-line no-console
    console.log('\n═══════════════════════════════════════════════════════════════════════════\n');
  } catch (err) {
    logger.error('Timeline 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);
});
