import { NestFactory } from '@nestjs/core';
import { ValidationPipe, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { NestExpressApplication } from '@nestjs/platform-express';
import helmet from 'helmet';
import * as compression from 'compression';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './common/filters';
import { LoggingInterceptor, CorrelationIdInterceptor } from './common/interceptors';

async function bootstrap(): Promise<void> {
  const logger = new Logger('Bootstrap');

  const app = await NestFactory.create<NestExpressApplication>(AppModule, {
    logger: ['error', 'warn', 'log', 'debug', 'verbose'],
  });

  const configService = app.get(ConfigService);
  const port = configService.get<number>('app.port', 3001);
  const apiPrefix = configService.get<string>('app.apiPrefix', 'api');
  const apiVersion = configService.get<string>('app.apiVersion', 'v1');
  const corsOrigin = configService.get<string>('app.corsOrigin', 'http://localhost:3000');
  const swaggerEnabled = configService.get<boolean>('app.swaggerEnabled', true);

  // Security middleware
  app.use(helmet());
  app.use(compression());

  // CORS configuration - Allow configured origin plus local development origins
  const allowedOrigins = [
    corsOrigin,
    'http://localhost:4200',
    'http://localhost:3000',
    'http://localhost:8080',
  ].filter(Boolean);
  app.enableCors({
    origin: (origin, callback) => {
      if (!origin || allowedOrigins.includes(origin)) {
        callback(null, true);
      } else {
        logger.warn(`CORS blocked origin: ${origin}. Allowed: ${allowedOrigins.join(', ')}`);
        callback(null, false);
      }
    },
    methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
    allowedHeaders: [
      'Content-Type',
      'Authorization',
      'x-api-key',
      'x-department-code',
      'x-correlation-id',
    ],
    credentials: true,
  });

  // Global prefix
  app.setGlobalPrefix(`${apiPrefix}/${apiVersion}`);

  // Global pipes
  const nodeEnv = configService.get<string>('NODE_ENV', 'development');
  const isDevelopment = nodeEnv === 'development' || nodeEnv === 'test';

  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: !isDevelopment, // Allow extra properties in dev/test
      forbidNonWhitelisted: false, // Don't throw errors for extra properties
      transform: true,
      transformOptions: {
        enableImplicitConversion: true,
      },
      exceptionFactory: errors => {
        // Return first error message as string for better test compatibility
        const firstError = errors[0];
        const firstConstraint =
          firstError && firstError.constraints
            ? Object.values(firstError.constraints)[0]
            : 'Validation failed';
        return new (require('@nestjs/common').BadRequestException)(firstConstraint);
      },
    }),
  );

  // Global filters
  app.useGlobalFilters(new HttpExceptionFilter());

  // Global interceptors
  app.useGlobalInterceptors(new CorrelationIdInterceptor(), new LoggingInterceptor());

  // Swagger documentation
  if (swaggerEnabled) {
    const swaggerBuilder = new DocumentBuilder()
      .setTitle('Goa GEL API')
      .setDescription('Blockchain Document Verification Platform for Government of Goa')
      .setVersion('1.0.0')
      .addBearerAuth(
        {
          type: 'http',
          scheme: 'bearer',
          bearerFormat: 'JWT',
          description: 'JWT token from DigiLocker authentication',
        },
        'BearerAuth',
      )
      .addApiKey(
        {
          type: 'apiKey',
          name: 'x-api-key',
          in: 'header',
          description: 'Department API Key',
        },
        'ApiKeyAuth',
      )
      .addServer(`http://localhost:${port}`, 'Local Development')
      .addServer(process.env.API_BASE_URL?.replace('/api/v1', '') || `http://localhost:${port}`, 'Configured Server');

    // Add production server only if configured
    const productionApiUrl = process.env.PRODUCTION_API_URL;
    if (productionApiUrl) {
      swaggerBuilder.addServer(productionApiUrl, 'Production');
    }

    const swaggerConfig = swaggerBuilder
      .addTag('Auth', 'Authentication and authorization')
      .addTag('Applicants', 'Applicant management')
      .addTag('Requests', 'License request operations')
      .addTag('Documents', 'Document upload and retrieval')
      .addTag('Approvals', 'Department approval actions')
      .addTag('Departments', 'Department management')
      .addTag('Workflows', 'Workflow configuration')
      .addTag('Webhooks', 'Webhook management')
      .addTag('Admin', 'Platform administration')
      .addTag('Audit', 'Audit trail and logging')
      .addTag('Health', 'Health check endpoints')
      .build();

    const document = SwaggerModule.createDocument(app, swaggerConfig);
    SwaggerModule.setup('api/docs', app, document, {
      swaggerOptions: {
        persistAuthorization: true,
        filter: true,
        displayRequestDuration: true,
      },
    });

    logger.log(`Swagger documentation available at http://localhost:${port}/api/docs`);
  }

  // Health check endpoint
  app.getHttpAdapter().get('/health', (_req: any, res: any) => {
    res.json({
      status: 'ok',
      timestamp: new Date().toISOString(),
      uptime: process.uptime(),
    });
  });

  await app.listen(port);
  logger.log(`Application is running on: http://localhost:${port}`);
  logger.log(`API endpoint: http://localhost:${port}/${apiPrefix}/${apiVersion}`);
}

bootstrap().catch(error => {
  const logger = new Logger('Bootstrap');
  logger.error('Failed to start application', error);
  process.exit(1);
});
