import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis';
import { BullModule } from '@nestjs/bullmq';
import { type MiddlewareConsumer, Module, type NestModule, RequestMethod } from '@nestjs/common';
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core';
import { CqrsModule } from '@nestjs/cqrs';
import { ScheduleModule } from '@nestjs/schedule';
import { ThrottlerModule } from '@nestjs/throttler';
import { SentryGlobalFilter, SentryModule } from '@sentry/nestjs/setup';
import { AdminModule } from '@modules/admin';
import { AgentsModule } from '@modules/agents';
import { AnalyticsModule } from '@modules/analytics';
import { AuthModule } from '@modules/auth';
import { FavoritesModule } from '@modules/favorites';
import { HealthModule } from '@modules/health';
import { IndustrialModule } from '@modules/industrial';
import { InquiriesModule } from '@modules/inquiries';
import { LeadsModule } from '@modules/leads';
import { ListingsModule } from '@modules/listings';
import { McpIntegrationModule } from '@modules/mcp';
import { MessagingModule } from '@modules/messaging';
import { HttpMetricsInterceptor, MetricsModule } from '@modules/metrics';
import { NotificationsModule } from '@modules/notifications';
import { PaymentsModule } from '@modules/payments';
import { ProjectsModule } from '@modules/projects';
import { ReportsModule } from '@modules/reports';
import { ReviewsModule } from '@modules/reviews';
import { SearchModule } from '@modules/search';
import { SharedModule } from '@modules/shared';
import { ThrottlerBehindProxyGuard } from '@modules/shared/infrastructure/guards/throttler-behind-proxy.guard';
import { CsrfMiddleware } from '@modules/shared/infrastructure/middleware/csrf.middleware';
import { SanitizeInputMiddleware } from '@modules/shared/infrastructure/middleware/sanitize-input.middleware';
import { SubscriptionsModule } from '@modules/subscriptions';
import { TransferModule } from '@modules/transfer';
import { AppController } from './app.controller';

@Module({
  imports: [
    SentryModule.forRoot(),
    BullModule.forRoot({
      connection: {
        host: process.env['REDIS_HOST'] ?? 'localhost',
        port: Number(process.env['REDIS_PORT'] ?? 6379),
        password: process.env['REDIS_PASSWORD'] ?? undefined,
      },
    }),
    CqrsModule.forRoot(),
    ScheduleModule.forRoot(),
    SharedModule,
    HealthModule,
    AuthModule,
    AgentsModule,
    InquiriesModule,
    LeadsModule,
    ListingsModule,
    ReviewsModule,
    FavoritesModule,
    SearchModule,
    NotificationsModule,
    PaymentsModule,
    SubscriptionsModule,
    AdminModule,
    AnalyticsModule,
    MetricsModule,
    McpIntegrationModule,
    MessagingModule,
    ReportsModule,
    ProjectsModule,
    IndustrialModule,
    TransferModule,

    // ── Rate Limiting ──
    // Default: 60 requests per 60 seconds per IP
    // Override per-route with @Throttle() decorator
    // Storage: Redis-backed sliding window so limits are shared across
    // every API instance (required for TEC-2930 feature-listing throttling).
    ThrottlerModule.forRoot({
      throttlers: [
        {
          name: 'default',
          ttl: 60_000,
          limit: process.env['NODE_ENV'] === 'test' || process.env['NODE_ENV'] === 'development' ? 10_000 : 60,
        },
        {
          name: 'auth',
          ttl: 60_000,
          limit: process.env['NODE_ENV'] === 'test' || process.env['NODE_ENV'] === 'development' ? 10_000 : 10,
        },
        {
          name: 'payment-callback',
          ttl: 60_000,
          limit: process.env['NODE_ENV'] === 'test' || process.env['NODE_ENV'] === 'development' ? 10_000 : 20,
        },
      ],
      storage: new ThrottlerStorageRedisService({
        host: process.env['REDIS_HOST'] ?? 'localhost',
        port: Number(process.env['REDIS_PORT'] ?? 6379),
        password: process.env['REDIS_PASSWORD'] ?? undefined,
        // Single retry per command + bounded reconnect backoff so a
        // transient Redis blip cannot stall the request path. Behaviour
        // matches RedisService for consistency.
        maxRetriesPerRequest: 1,
        enableReadyCheck: false,
        lazyConnect: true,
        retryStrategy(times: number): number {
          return Math.min(times * 1000, 5000);
        },
        keyPrefix: 'throttler:',
      }),
    }),
  ],
  controllers: [AppController],
  providers: [
    {
      provide: APP_FILTER,
      useClass: SentryGlobalFilter,
    },
    {
      provide: APP_GUARD,
      useClass: ThrottlerBehindProxyGuard,
    },
    {
      provide: APP_INTERCEPTOR,
      useClass: HttpMetricsInterceptor,
    },
  ],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer): void {
    // Sanitize all incoming request strings to prevent stored XSS
    consumer
      .apply(SanitizeInputMiddleware)
      .forRoutes('*');

    // CSRF double-submit cookie (sets on GET, validates on state-changing methods)
    // Exclude health endpoints — they must remain accessible without cookies
    // Skip entirely in test mode so E2E / API tests can POST without a CSRF cookie
    if (process.env['NODE_ENV'] !== 'test') {
      consumer
        .apply(CsrfMiddleware)
        .exclude(
          { path: 'health', method: RequestMethod.GET },
          { path: 'health/(.*)', method: RequestMethod.GET },
          { path: 'api/v1/web-vitals', method: RequestMethod.POST }, // sendBeacon cannot send CSRF headers
          { path: 'web-vitals', method: RequestMethod.POST }, // middleware exclude uses controller-relative path
        )
        .forRoutes('*');
    }
  }
}
