import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ConfigService } from '@nestjs/config';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { ApiErrorExceptionFilter } from './common/error/api-error-exception-filter';
import { ValidationPipe } from '@nestjs/common';
import { ApiError } from './common/error/api-error';
import { ApiErrorCodes } from './common/error/api-error-codes';
import { BadRequestExceptionFilter } from './common/error/bad-request-exception-filter';
import * as basicAuth from 'express-basic-auth';
import { HttpExceptionFilter } from './common/error/http-exception-filter';
import { PayloadTooLargeExceptionFilter } from './payload-too-large-exception-filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors();

  const configService = app.get<ConfigService>(ConfigService);
  const swaggerUser = configService.get('swagger.user');
  if (swaggerUser) {
    (app as any).use(
      configService.get('swagger.path'),
      (basicAuth as any)({
        challenge: true,
        users: { [swaggerUser]: configService.get('swagger.password') },
      }),
    );
  }

  const swaggerDocument = new DocumentBuilder()
    .setTitle(configService.get('swagger.title'))
    .setDescription(configService.get('swagger.description'))
    .setVersion(configService.get('swagger.version'))
    .addTag(configService.get('swagger.tag'))
    .addBearerAuth()
    .addServer(configService.get('swagger.serverPath'))
    .build();
  const document = SwaggerModule.createDocument(app, swaggerDocument);
  SwaggerModule.setup(configService.get('swagger.path'), app, document);

  app.useGlobalFilters(new BadRequestExceptionFilter());
  app.useGlobalFilters(new HttpExceptionFilter());
  app.useGlobalFilters(new ApiErrorExceptionFilter());
  app.useGlobalFilters(new PayloadTooLargeExceptionFilter());
  app.useGlobalPipes(
    new ValidationPipe({
      exceptionFactory: (errors) => {
        const constraints =
          errors.length > 0 && errors[0].constraints
            ? errors[0].constraints
            : {};
        const errorMessages = {
          field: errors[0].property,
          constraint: Object.keys(constraints)[0],
        };
        const constraints_key = Object.values(constraints).join('. ').trim();
        let code;
        if (constraints_key === 'isNotEmpty') {
          code = 'PARAM_EMPTY';
        } else {
          code = 'PARAM_BAD_FORMAT';
        }
        return new ApiError(
          constraints_key,
          ApiErrorCodes[code as ApiErrorCodes],
          errorMessages,
        );
      },
    }),
  );

  await app.startAllMicroservices();
  await app.listen(
    configService.get('server.port'),
    configService.get('server.host'),
  );
}
bootstrap();
