import {
  AllExceptionsFilter,
  RolesGuard,
  TestUserGuard,
  UserType,
} from '@gadmin2/nest-common';
import { ClassSerializerInterceptor, ValidationPipe } from '@nestjs/common';
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 { WoaAuthGuard } from './lib/woaAuth';

// 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 });

  const configService = app.get(ConfigService);

  // enable shutdown hook
  const prismaService: PrismaService = app.get(PrismaService);
  prismaService.enableShutdownHooks(app);

  app.use(cookieParser());
  app.useGlobalGuards(
    // new YFAuthGuard(),
    process.env.NODE_ENV === 'development'
      ? new TestUserGuard({ username: 'admin', userid: 'admin' })
      : new WoaAuthGuard(
          process.env.WOA_TOKEN || 'xxxx',
        ),

    /*
    new RolesGuard((user: UserType) => {
      return Promise.resolve(configService.get('roles')[user.username]);
    }),
    */

    new RolesGuard((user: UserType) => {
      return prismaService.role
        .findMany({
          where: {
            userid: user.username,
          },
        })
        .then((records) => {
          if (records.length === 0) {
            if (['admin', 'kavenma'].includes(user.username)) {
              return ['SYSTEM_ADMIN'];
            }
            return [];
          }
          return JSON.parse(records[0].roles);
        })
        .catch((_: any) => {
          if (['admin', 'kavenma'].includes(user.username)) {
            return ['SYSTEM_ADMIN'];
          }
          return [];
        });
    }),
  );

  // 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');
  if (swaggerConfig.enabled) {
    const config = new DocumentBuilder()
      .setTitle(swaggerConfig.title || 'Nestjs')
      .setDescription(swaggerConfig.description || 'The nestjs API description')
      .setVersion(swaggerConfig.version || '1.0')
      //.addBearerAuth()
      .build();
    const options: SwaggerDocumentOptions = {
      operationIdFactory: (controllerKey: string, methodKey: string) =>
        methodKey,
    };
    const document = SwaggerModule.createDocument(app, config, options);

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

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

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

bootstrap();
