import { Request, Response, NextFunction } from 'express'

/**
 * 입력값 살균(sanitize) 유틸리티
 * SQL 인젝션 및 XSS 공격에 사용되는 위험 문자를 제거/이스케이프
 */

// HTML 특수문자 이스케이프 (XSS 방지)
export function escapeHtml(str: string): string {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#x27;')
}

// SQL 인젝션에 사용되는 위험 패턴 탐지
const SQL_INJECTION_PATTERNS = [
  /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|EXECUTE|UNION|TRUNCATE|GRANT|REVOKE)\b)/i,
  /(--|#|\/\*|\*\/)/,           // SQL 주석
  /(\b(OR|AND)\b\s+\d+\s*=\s*\d+)/i, // OR 1=1, AND 1=1
  /(;[\s]*$)/,                  // SQL 종료자
  /(\bxp_\w+)/i,               // SQL Server xp_ 프로시저
  /(\bsp_\w+)/i,               // SQL Server sp_ 프로시저
]

// XSS 공격에 사용되는 위험 패턴 탐지
const XSS_PATTERNS = [
  /<script\b[^>]*>/i,
  /javascript\s*:/i,
  /on\w+\s*=/i,                 // onclick=, onerror= 등
  /eval\s*\(/i,
  /expression\s*\(/i,
  /vbscript\s*:/i,
  /data\s*:\s*text\/html/i,
]

// 문자열에서 위험 패턴 검사
export function containsSqlInjection(input: string): boolean {
  return SQL_INJECTION_PATTERNS.some(pattern => pattern.test(input))
}

export function containsXss(input: string): boolean {
  return XSS_PATTERNS.some(pattern => pattern.test(input))
}

// 문자열 살균: 위험 HTML 태그와 특수문자 제거
export function sanitizeString(input: string): string {
  return input
    .replace(/<[^>]*>/g, '')    // HTML 태그 제거
    .replace(/[<>"'`;]/g, '')   // 위험 특수문자 제거
    .trim()
}

// 숫자 검증
export function isValidNumber(value: unknown, min?: number, max?: number): value is number {
  if (typeof value !== 'number' || isNaN(value) || !isFinite(value)) return false
  if (min !== undefined && value < min) return false
  if (max !== undefined && value > max) return false
  return true
}

// 위도 검증 (-90 ~ 90)
export function isValidLatitude(lat: unknown): lat is number {
  return isValidNumber(lat, -90, 90)
}

// 경도 검증 (-180 ~ 180)
export function isValidLongitude(lon: unknown): lon is number {
  return isValidNumber(lon, -180, 180)
}

// 허용된 값 목록 검증 (화이트리스트)
export function isAllowedValue<T>(value: T, allowedValues: T[]): boolean {
  return allowedValues.includes(value)
}

// 문자열 길이 검증
export function isValidStringLength(str: string, maxLength: number): boolean {
  return typeof str === 'string' && str.length <= maxLength
}

/**
 * 요청 본문(body) 살균 미들웨어
 * 모든 문자열 필드에서 XSS/SQL 인젝션 패턴을 검사
 */
export function sanitizeBody(req: Request, res: Response, next: NextFunction): void {
  if (req.body && typeof req.body === 'object') {
    for (const [key, value] of Object.entries(req.body)) {
      if (typeof value === 'string') {
        // XSS 패턴 검사
        if (containsXss(value)) {
          res.status(400).json({
            error: '유효하지 않은 입력값',
            field: key,
            message: '허용되지 않는 문자가 포함되어 있습니다.'
          })
          return
        }
        // SQL 인젝션 패턴 검사
        if (containsSqlInjection(value)) {
          res.status(400).json({
            error: '유효하지 않은 입력값',
            field: key,
            message: '허용되지 않는 문자가 포함되어 있습니다.'
          })
          return
        }
      }
    }
  }
  next()
}

/**
 * 요청 파라미터(params) 살균 미들웨어
 * URL 파라미터에서 위험 문자 검사
 */
export function sanitizeParams(req: Request, res: Response, next: NextFunction): void {
  for (const [key, value] of Object.entries(req.params)) {
    if (typeof value === 'string') {
      if (containsXss(value) || containsSqlInjection(value)) {
        res.status(400).json({
          error: '유효하지 않은 파라미터',
          field: key,
          message: '허용되지 않는 문자가 포함되어 있습니다.'
        })
        return
      }
    }
  }
  next()
}

/**
 * 요청 쿼리(query) 살균 미들웨어
 */
export function sanitizeQuery(req: Request, res: Response, next: NextFunction): void {
  for (const [key, value] of Object.entries(req.query)) {
    if (typeof value === 'string') {
      if (containsXss(value) || containsSqlInjection(value)) {
        res.status(400).json({
          error: '유효하지 않은 쿼리 파라미터',
          field: key,
          message: '허용되지 않는 문자가 포함되어 있습니다.'
        })
        return
      }
    }
  }
  next()
}

/**
 * JSON 본문 크기 제한 (보고서 지도 캡처 이미지 포함 대응: 5mb)
 */
export const BODY_SIZE_LIMIT = '5mb'

/**
 * 응답 헤더에서 서버 정보 제거
 */
export function removeServerInfo(req: Request, res: Response, next: NextFunction): void {
  res.removeHeader('X-Powered-By')
  next()
}
