import 'dotenv/config'
import express from 'express'
import cors from 'cors'
import helmet from 'helmet'
import rateLimit from 'express-rate-limit'
import cookieParser from 'cookie-parser'
import { testWingDbConnection } from './db/wingDb.js'
import layersRouter from './routes/layers.js'
import simulationRouter from './routes/simulation.js'
import tilesRouter from './routes/tiles.js'
import authRouter from './auth/authRouter.js'
import userRouter from './users/userRouter.js'
import roleRouter from './roles/roleRouter.js'
import settingsRouter from './settings/settingsRouter.js'
import menuRouter from './menus/menuRouter.js'
import auditRouter from './audit/auditRouter.js'
import boardRouter from './board/boardRouter.js'
import hnsRouter from './hns/hnsRouter.js'
import reportsRouter from './reports/reportsRouter.js'
import assetsRouter from './assets/assetsRouter.js'
import incidentsRouter from './incidents/incidentsRouter.js'
import gscAccidentsRouter from './gsc/gscAccidentsRouter.js'
import scatRouter from './scat/scatRouter.js'
import predictionRouter from './prediction/predictionRouter.js'
import aerialRouter from './aerial/aerialRouter.js'
import rescueRouter from './rescue/rescueRouter.js'
import mapBaseRouter from './map-base/mapBaseRouter.js'
import monitorRouter from './monitor/monitorRouter.js'
import {
  sanitizeBody,
  sanitizeQuery,
  removeServerInfo,
  BODY_SIZE_LIMIT
} from './middleware/security.js'

const app = express()
const PORT = process.env.PORT || 3001

// ============================================================
// 보안 미들웨어
// ============================================================

// 1. Helmet: HTTP 보안 헤더 설정
//    - X-Content-Type-Options: nosniff (MIME 스니핑 방지)
//    - X-Frame-Options: DENY (클릭재킹 방지)
//    - X-XSS-Protection: 1 (브라우저 XSS 필터)
//    - Strict-Transport-Security (HTTPS 강제)
//    - Content-Security-Policy (컨텐츠 보안 정책)
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "data:", "blob:"],
      connectSrc: [
        "'self'",
        ...(process.env.NODE_ENV !== 'production' ? ['http://localhost:*'] : []),
        'https://*.gc-si.dev',
        'https://*.data.go.kr',
        'https://*.khoa.go.kr',
      ],
      fontSrc: ["'self'"],
      objectSrc: ["'none'"],
      frameSrc: ["'none'"],
    }
  },
  crossOriginEmbedderPolicy: false, // API 서버이므로 비활성
  crossOriginResourcePolicy: { policy: 'cross-origin' }, // sendBeacon cross-origin 허용
}))

// 2. 서버 정보 제거 (공격자에게 기술 스택 노출 방지)
app.use(removeServerInfo)
app.disable('x-powered-by')

// 3. CORS: 허용된 출처만 접근 가능
const allowedOrigins = [
  process.env.FRONTEND_URL || 'https://wing-demo.gc-si.dev',
  ...(process.env.NODE_ENV !== 'production' ? [
    'http://localhost:5173',
    'http://localhost:5174',
    'http://localhost:5175',
    'http://localhost:3000',
  ] : []),
].filter(Boolean) as string[]

app.use(cors({
  origin: (origin, callback) => {
    // 서버-to-서버 요청 (origin 없음) 또는 허용된 출처
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true)
    } else {
      callback(new Error('CORS 정책에 의해 차단되었습니다.'))
    }
  },
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400, // preflight 캐시 24시간
}))

// 4. 요청 속도 제한 (Rate Limiting) - DDoS/브루트포스 방지
const generalLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15분
  max: 500,                  // IP당 최대 500요청 (HLS 스트리밍 고려)
  standardHeaders: true,
  legacyHeaders: false,
  skip: (req) => {
    // HLS 스트리밍 프록시는 빈번한 세그먼트 요청이 발생하므로 제외
    return req.path.startsWith('/api/aerial/cctv/stream-proxy') ||
           req.path.startsWith('/api/tiles/');
  },
  message: {
    error: '요청 횟수 초과',
    message: '너무 많은 요청을 보냈습니다. 15분 후 다시 시도하세요.'
  }
})

const simulationLimiter = rateLimit({
  windowMs: 60 * 1000,      // 1분
  max: 10,                   // IP당 최대 10요청 (시뮬레이션은 비용이 큰 작업)
  message: {
    error: '시뮬레이션 요청 횟수 초과',
    message: '시뮬레이션 요청은 1분당 10회로 제한됩니다.'
  }
})

app.use(generalLimiter)

// 5. 쿠키 파서 (JWT 인증 쿠키 처리)
app.use(cookieParser())

// 6. JSON 본문 파서 (크기 제한 적용)
app.use(express.json({ limit: BODY_SIZE_LIMIT }))
app.use(express.text({ limit: BODY_SIZE_LIMIT }))
app.use(express.urlencoded({ extended: false, limit: BODY_SIZE_LIMIT }))

// 7. 입력값 살균 미들웨어
app.use(sanitizeBody)
app.use(sanitizeQuery)

// ============================================================
// 라우트
// ============================================================

// 루트 경로
app.get('/', (_req, res) => {
  res.json({
    name: 'WING Backend API',
    version: '1.0.0',
    status: 'running',
  })
})

// API 라우트 — 인증
app.use('/api/auth', authRouter)
app.use('/api/users', userRouter)
app.use('/api/roles', roleRouter)
app.use('/api/settings', settingsRouter)
app.use('/api/menus', menuRouter)
app.use('/api/audit', auditRouter)

// API 라우트 — 업무
app.use('/api/board', boardRouter)
app.use('/api/layers', layersRouter)
app.use('/api/simulation/run', simulationLimiter) // 시뮬레이션 실행만 엄격 제한 (status 폴링 제외)
app.use('/api/simulation', simulationRouter)
app.use('/api/hns', hnsRouter)
app.use('/api/reports', reportsRouter)
app.use('/api/assets', assetsRouter)
app.use('/api/incidents', incidentsRouter)
app.use('/api/gsc/accidents', gscAccidentsRouter)
app.use('/api/scat', scatRouter)
app.use('/api/prediction', predictionRouter)
app.use('/api/aerial', aerialRouter)
app.use('/api/rescue', rescueRouter)
app.use('/api/map-base', mapBaseRouter)
app.use('/api/monitor', monitorRouter)
app.use('/api/tiles', tilesRouter)

// 헬스 체크
app.get('/health', (_req, res) => {
  res.json({ status: 'ok' })
})

// ============================================================
// 404 처리
// ============================================================
app.use((_req, res) => {
  res.status(404).json({ error: '요청한 리소스를 찾을 수 없습니다.' })
})

// ============================================================
// 전역 에러 핸들러 (내부 오류 메시지 노출 방지)
// ============================================================
app.use((err: Error, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
  // 운영 환경에서는 내부 오류 메시지를 노출하지 않음
  const isDev = process.env.NODE_ENV !== 'production'
  if (isDev) {
    console.error('서버 오류:', err.message)
  }

  res.status(500).json({
    error: '서버 내부 오류가 발생했습니다.',
    ...(isDev && { detail: err.message })
  })
})

// ============================================================
// 서버 시작
// ============================================================
app.listen(PORT, async () => {
  console.log(`서버가 포트 ${PORT}에서 실행 중입니다.`)

  // wing DB 연결 확인 (wing + auth 스키마 통합)
  const connected = await testWingDbConnection()
  if (connected) {
    // SETTING_VAL VARCHAR(500) → TEXT 마이그레이션 (메뉴 설정 JSON 확장 대응)
    try {
      const { wingPool } = await import('./db/wingDb.js')
      await wingPool.query(`ALTER TABLE AUTH_SETTING ALTER COLUMN SETTING_VAL TYPE TEXT`)
    } catch {
      // 이미 TEXT이거나 권한 없으면 무시
    }
  }
})
