import './lib/tracing'; // OpenTelemetry Prisma instrumentation - 必须第一个导入

import { AllExceptionsFilter, UserType } from '@gadmin2n/nest-common';
import {
  ClassSerializerInterceptor,
  UnauthorizedException,
  ValidationPipe,
} from '@nestjs/common';
import './alias.config';

import { ConfigService } from '@nestjs/config';
import { HttpAdapterHost, NestFactory, Reflector } from '@nestjs/core';
import {
  DocumentBuilder,
  SwaggerDocumentOptions,
  SwaggerModule,
} from '@nestjs/swagger';
import * as cookieParser from 'cookie-parser';
import 'dotenv/config';
import { PrismaClientExceptionFilter, PrismaService } from 'nestjs-prisma';
import { AppModule } from './app.module';
import AuthGuard from './lib/auth.guard';
import RolesGuard from './lib/roles.guard';
import { UserService } from './modules/user/user.service';

// https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields#working-with-bigint
const stringify = JSON.stringify;
JSON.stringify = (value: any, _: any, space: string | number | undefined) => {
  return stringify(
    value,
    (key, value) => (typeof value === 'bigint' ? Number(value) : value), // return everything else unchanged)
    space,
  );
};

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    cors: true,
    rawBody: true, // Required for HMAC verification on /api/workflow/webhook/:path
  });
  // 添加全局前缀 '/api'
  app.setGlobalPrefix(`${process.env.DEPLOY_NAME || ''}/api`);

  const configService = app.get(ConfigService);

  const prismaService: PrismaService = app.get(PrismaService);

  app.use(cookieParser());
  app.useGlobalGuards(
    new AuthGuard(new Reflector(), app.get(UserService)),

    new RolesGuard((user: UserType) => {
      return prismaService.user
        .findMany({
          where: {
            userid: user.username,
          },
        })
        .then((records) => {
          if (records.length === 0) {
            if (['jasonlan', 'gc_lvhenan'].includes(user.username)) {
              return { business: [-1], roles: ['SYSTEM_ADMIN'] };
            }
            return { business: [], roles: [] };
          }
          if (!records[0].isActive) {
            throw new UnauthorizedException('用户未激活');
          }
          return {
            business: [],
            roles: JSON.parse(records[0].roles),
          };
        })
        .catch(() => {
          if (['jasonlan', 'gc_lvhenan'].includes(user.username)) {
            return { business: [-1], roles: ['SYSTEM_ADMIN'] };
          }
          return { business: [], roles: [] };
        });
    }, new Reflector()),
  );

  // binds ValidationPipe to the entire application 。自动转换负载数据（url query和body），并验证数据是否符合dto的数据约束
  app.useGlobalPipes(
    new ValidationPipe({
      transform: true, // 👈 automatically transform payloads
      transformOptions: {
        enableImplicitConversion: true, // 👈  transform payloads based on TS type
      },
    }),
  );

  // apply transform to all responses
  app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));

  // apply PrismaClientExceptionFilter to entire application, requires HttpAdapterHost because it extends BaseExceptionFilter
  const { httpAdapter } = app.get(HttpAdapterHost);
  app.useGlobalFilters(
    new PrismaClientExceptionFilter(httpAdapter),
    new AllExceptionsFilter(),
  );

  // Swagger Api
  const swaggerConfig = configService.get('swagger');
  swaggerConfig.path =
    `${process.env.DEPLOY_NAME || ''}/api` || swaggerConfig.path || 'api';
  if (swaggerConfig.enabled) {
    const config = new DocumentBuilder()
      .setTitle(swaggerConfig.title || 'Nestjs')
      .setDescription(swaggerConfig.description || 'The nestjs API description')
      .setVersion(swaggerConfig.version || '1.0')
      //.addBearerAuth()
      .addApiKey({ type: 'apiKey' }, 'X-API-KEY')
      .build();
    const options: SwaggerDocumentOptions = {
      operationIdFactory: (controllerKey: string, methodKey: string) =>
        methodKey,
    };
    const document = SwaggerModule.createDocument(app, config, options);

    SwaggerModule.setup(swaggerConfig.path, app, document, {
      customSiteTitle: 'Prisma Day',
    });
  }

  // 启用 NestJS 生命周期钩子，确保 SIGTERM 时触发 onApplicationShutdown / onModuleDestroy
  app.enableShutdownHooks();

  // 启用 NestJS 生命周期钩子，确保 SIGTERM 时触发 onApplicationShutdown / onModuleDestroy
  app.enableShutdownHooks();

  await app.listen(configService.get('nest').port);

  console.log(
    `Application is running on: ${await app.getUrl()}/${swaggerConfig.path}`,
  );
}

bootstrap();
