import { System, setTimeZoneApplication } from '@aurorajs.dev/core-back';
import { ConfigService } from '@nestjs/config';
import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import dayjs from 'dayjs';
import advancedFormat from 'dayjs/plugin/advancedFormat';
import dayOfYear from 'dayjs/plugin/dayOfYear';
import isoWeek from 'dayjs/plugin/isoWeek';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
import weekOfYear from 'dayjs/plugin/weekOfYear';
import { json, urlencoded } from 'express';
import { env } from 'node:process';
import { logger } from './@aurora/services';
import { AppModule } from './app.module';

// dayjs configuration
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.extend(advancedFormat);
dayjs.extend(weekOfYear);
dayjs.extend(isoWeek);
dayjs.extend(dayOfYear);

async function bootstrap(): Promise<void> {
  const app = await NestFactory.create(AppModule, { logger: logger() });
  const configService = app.get(ConfigService);

  if (configService.get<string>('SWAGGER') === 'true') {
    const options = new DocumentBuilder()
      .setTitle('Aurora API')
      .setDescription('Aurora API description')
      .setVersion('1.0')
      .addBearerAuth()
      .build();
    const document = SwaggerModule.createDocument(app, options);
    SwaggerModule.setup('api', app, document);
  }

  // set limit of request allowed
  app.use(json({ limit: configService.get<string>('APP_LIMIT_REQUEST_SIZE') }));
  app.use(
    urlencoded({
      extended: true,
      limit: configService.get<string>('APP_LIMIT_REQUEST_SIZE'),
    }),
  );

  // set graphql upload files, workaround for import graphql-upload
  const { default: graphqlUploadExpress } = await (eval(
    "import('graphql-upload/graphqlUploadExpress.mjs')",
  ) as Promise<typeof import('graphql-upload/graphqlUploadExpress.mjs')>);
  app.use(
    '/graphql',
    graphqlUploadExpress({
      maxFileSize: +configService.get<string>('APP_MAX_FILE_SIZE_UPLOADED'), // 50000000 = 50 MB, value in bytes
      maxFiles: configService.get<string>('APP_MAX_FILES_UPLOADED'), // max files uploaded at once
    }),
  );

  // Trust Proxy — env-driven, default DISABLED (safe for direct-connect; prevents
  // X-Forwarded-For spoofing when there is no reverse proxy in front of the server).
  // Set TRUST_PROXY to enable: use a numeric hop count (1, 2, …) or the literal
  // "true" (trust all) / "false" (explicit disable). Required for correct per-IP
  // throttling and OAuthCredential.ipAddress when running behind nginx / ALB / Cloud Run.
  const trustProxy = configService.get<string>('TRUST_PROXY');
  if (trustProxy !== undefined && trustProxy !== '') {
    // app.set() is Express-specific — access via the underlying HTTP adapter instance.
    // eslint-disable-next-line @typescript-eslint/no-unsafe-call
    const expressApp = app.getHttpAdapter().getInstance() as {
      set: (key: string, value: unknown) => void;
    };
    const numericValue = Number(trustProxy);
    if (trustProxy === 'true') {
      expressApp.set('trust proxy', true);
    } else if (trustProxy === 'false') {
      expressApp.set('trust proxy', false);
    } else if (Number.isFinite(numericValue)) {
      expressApp.set('trust proxy', numericValue);
    }
    // Unrecognised values are silently ignored — default (disabled) is preserved.
  }

  // set timezone application from .env APP_TIMEZONE on dayjs
  setTimeZoneApplication(configService, dayjs);

  app.setGlobalPrefix('api', {
    exclude: ['graphql', '.well-known/jwks.json'],
  });

  // CORS. When APP_CORS_ORIGIN is set (comma-separated allowlist), enable credentialed CORS so the
  // Authorization Code continue-flow works cross-origin: the hub frontend POSTs the access token
  // to POST /api/o-auth/session with `withCredentials` and the backend must accept it and set the
  // `hub_session` cookie. A credentialed response cannot use a wildcard origin, so the allowlist is
  // explicit. With APP_CORS_ORIGIN unset, fall back to the previous open (credential-less) behavior.
  const corsOrigin = configService.get<string>('APP_CORS_ORIGIN');
  if (corsOrigin) {
    app.enableCors({
      origin: corsOrigin.split(',').map((origin) => origin.trim()),
      credentials: true,
    });
  } else {
    app.enableCors();
  }

  await app.listen(configService.get<number>('APP_PORT') ?? 3000);

  if (env.NODE_ENV !== 'production') {
    System.memoryWatcher();
  }
}

bootstrap();
