import { Logger, Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';
import { ScheduleModule } from '@nestjs/schedule';
import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
import { CacheModule } from '@nestjs/cache-manager';
import { createKeyv } from '@keyv/redis';
import type Keyv from 'keyv';
import { APP_GUARD } from '@nestjs/core';

import { AppController } from './app.controller';
import { AppService } from './app.service';
import { HealthModule } from './modules/health/health.module';
import { UserModule } from './modules/user/user.module';
import { AuthModule } from './modules/auth/auth.module';
import { TaskModule } from './modules/task/task.module';
import { ActivityModule } from './modules/activity/activity.module';
import { NotificationModule } from './modules/notification/notification.module';
import { AiModule } from './modules/ai/ai.module';
import { ProjectModule } from './modules/project/project.module';

@Module({
  imports: [
    ScheduleModule.forRoot(),
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: `.env.${process.env.NODE_ENV || 'development'}`,
    }),
    // 전역 캐시 매니저 — REDIS_* 설정 시 Redis(keyv) 스토어, 없으면 인메모리 폴백.
    // docker-compose 가 REDIS_HOST/PORT/PASSWORD 를 주입한다(단독 실행/테스트는 미설정 → 인메모리).
    CacheModule.registerAsync({
      isGlobal: true,
      useFactory: (): { ttl: number; stores?: Keyv[] } => {
        const logger = new Logger('CacheModule');
        const options: { ttl: number; stores?: Keyv[] } = { ttl: 60_000 };
        const host = process.env.REDIS_HOST;
        if (!host) {
          // Redis 미설정 — 인메모리 폴백(LRU). 로컬/테스트에서 Redis 없이도 동작.
          // 주의: 다중 인스턴스를 인메모리 모드로 띄우면 인스턴스별 캐시·무효화가 분리됨(단일 인스턴스 전용)
          logger.log('캐시: 인메모리 모드(단일 인스턴스) — REDIS_HOST 미설정');
          return options;
        }
        const port = process.env.REDIS_PORT || '6379';
        const password = process.env.REDIS_PASSWORD;
        const auth = password ? `:${encodeURIComponent(password)}@` : '';
        const keyv = createKeyv(`redis://${auth}${host}:${port}`);
        // Redis 연결 오류가 unhandled 로 프로세스를 죽이지 않도록 흡수(캐시 장애는 DB 조회로 폴백)
        keyv.on('error', (err: unknown) => {
          logger.warn(
            `Redis 캐시 오류(무시, DB 폴백): ${err instanceof Error ? err.message : String(err)}`,
          );
        });
        options.stores = [keyv];
        logger.log('캐시: Redis 모드(다중 인스턴스 무효화 정합)');
        return options;
      },
    }),
    // 전역 Rate Limiter — main.ts 의 express-rate-limit 와 별개로 라우트 단위 제어 가능
    // NOTE: 개발/테스트 편의를 위해 분당 1000회로 완화함. 운영 배포 전 적정값으로 되돌릴 것.
    ThrottlerModule.forRoot([
      {
        ttl: 60_000,
        limit: 1000,
      },
    ]),
    TypeOrmModule.forRootAsync({
      useFactory: () => {
        const isProduction = process.env.NODE_ENV === 'production';
        return {
          type: 'postgres',
          host: process.env.DB_HOST || 'db',
          port: parseInt(process.env.DB_PORT || '5432', 10),
          username:
            process.env.DB_USER || process.env.DB_USERNAME || 'postgres',
          password: process.env.DB_PASSWORD || 'password',
          database:
            process.env.DB_NAME || process.env.DB_DATABASE || 'project_db',
          autoLoadEntities: true,
          // 운영 환경에서는 절대 synchronize 사용 금지 (스키마 자동 변경 → 데이터 손실 위험)
          synchronize: !isProduction,
          migrationsRun: isProduction,
          logging: !isProduction,
          extra: {
            max: 100,
            connectionTimeoutMillis: 5000,
            idleTimeoutMillis: 30000,
          },
          entities: [__dirname + '/**/*.entity{.ts,.js}'],
          migrations: [__dirname + '/migrations/*{.ts,.js}'],
        };
      },
    }),
    HealthModule,
    UserModule,
    AuthModule,
    TaskModule,
    ActivityModule,
    NotificationModule,
    AiModule,
    ProjectModule,
  ],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: APP_GUARD,
      useClass: ThrottlerGuard,
    },
  ],
})
export class AppModule {}
