import './instrument'; // ⚠️ 必须首行:Sentry 自动埋点需在其它模块加载前初始化
import 'reflect-metadata';
import { NestFactory } from '@nestjs/core';
import { Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { apiReference } from '@scalar/nestjs-api-reference';
import type { NestExpressApplication } from '@nestjs/platform-express';
import helmet from 'helmet';
import { AppModule } from './app.module';
import { AllExceptionsFilter } from './common/filters/all-exceptions.filter';
import {
  GLOBAL_PREFIX,
  GLOBAL_PREFIX_EXCLUDE,
  buildPacOpenApiDocument,
} from './openapi/build-document';

/// 请求体上限(json / urlencoded 共用)。见 bootstrap 内注释:不显式设就是 100KB 默认值。
const PAC_BODY_LIMIT = '10mb';

async function bootstrap() {
  // rawBody: true → req.rawBody 保留原始 bytes,push HMAC 验签需要(parsed JSON 字段顺序不稳)
  const app = await NestFactory.create<NestExpressApplication>(AppModule, {
    bufferLogs: true,
    rawBody: true,
  });

  // ⭐ 请求体上限 —— **必须显式设**:不设就吃 body-parser 出厂默认 100KB。
  //   2026-07 线上(测试机)踩雷:FRIDAY 增量 push `med_emr_info` 整点重试连挂 25 次,
  //   报 `PayloadTooLargeError` → 兜成 500/90000,对面完全看不出是自己包太大。
  //   接入文档 §10 只约束"≤500 条/请求",没约束字节 —— 而病历行带自由文本(实测均 1.4KB、
  //   峰值 2.2KB),500 条 ≈ 710KB,超默认值 7 倍必挂;连 405B 的预约行 500 条(≈198KB)也超。
  //   取 10MB:覆盖 500 条病历十几倍余量,又远低于网关 client_max_body_size 50M(不会把
  //   拦截点推到网关,那层返 HTML 413,对面更难排查)。rawBody 由上面的 create 选项保留,
  //   useBodyParser 重设 limit 不影响验签。
  app.useBodyParser('json', { limit: PAC_BODY_LIMIT });
  app.useBodyParser('urlencoded', { limit: PAC_BODY_LIMIT, extended: true });

  app.use(helmet({ contentSecurityPolicy: false }));

  const corsOrigins = app.get(ConfigService).getOrThrow<{ origins: string[] }>('cors').origins;
  app.enableCors({
    origin: corsOrigins.length > 0 ? corsOrigins : true,
    credentials: false,
    methods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'OPTIONS'],
    allowedHeaders: ['Authorization', 'Content-Type', 'Accept'],
  });

  app.setGlobalPrefix(GLOBAL_PREFIX, { exclude: GLOBAL_PREFIX_EXCLUDE });

  app.useGlobalFilters(new AllExceptionsFilter());

  const document = buildPacOpenApiDocument(app);

  app.use('/api/openapi.json', (_req: unknown, res: { json: (b: unknown) => void }) =>
    res.json(document),
  );
  app.use(
    '/api/docs',
    apiReference({
      content: document,
      theme: 'purple',
      layout: 'modern',
    }),
  );

  const port = Number(process.env.PORT ?? 3001);
  await app.listen(port, '0.0.0.0');
  Logger.log(`PAC service listening on http://localhost:${port}`, 'Bootstrap');
  Logger.log(`API reference: http://localhost:${port}/api/docs`, 'Bootstrap');
}

bootstrap().catch((err) => {
  console.error('Failed to bootstrap', err);
  process.exit(1);
});
