/**
 * Edge-safe firewall logic — imported by middleware.ts (Edge Runtime only).
 * No Node.js APIs allowed here (no crypto, fs, etc.).
 *
 * Layers:
 *  1. Bad-bot / scanner User-Agent detection
 *  2. Known attack-path detection  (/.env, /wp-admin, /phpMyAdmin …)
 *  3. Injection pattern detection  (SQLi, XSS, path-traversal in query strings)
 *  4. In-memory per-IP rate limiter (sliding 60-second window)
 */

// ── 1. Malicious User-Agent patterns ────────────────────────────────────────
const BAD_BOT_RE = /sqlmap|nikto|nmap|masscan|nuclei|zgrab|zmap|metasploit|havij|acunetix|nessus|openvas|nexpose|dirbuster|gobuster|feroxbuster|ffuf|wfuzz|dirb|hydra|medusa|burpsuite|zaproxy|w3af|skipfish|slowloris|hulk|loic|hoic|rudy|libwww-perl|jakarta|xp_cmdshell/i

// ── 2. Known attack-path patterns ───────────────────────────────────────────
const ATTACK_PATH_RE = /\/(\.env|\.git|\.svn|\.DS_Store|wp-admin|wp-login\.php|wp-config\.php|wp-includes|xmlrpc\.php|phpmyadmin|adminer|phpinfo|shell\.php|cmd\.php|eval\.php|cgi-bin|etc\/passwd|proc\/self|actuator|\.aws|\.ssh)/i

// ── 3. Injection patterns (query strings + pathname) ────────────────────────
const SQLI_RE = /union\s+(?:all\s+)?select|(?:and|or)\s+\d+=\d+|drop\s+(?:table|database)|exec(?:ute)?\s*\(|xp_cmdshell|select.+from|insert\s+into|delete\s+from/i
const XSS_RE = /<\s*script|javascript\s*:|vbscript\s*:|on(?:load|error|click|mouseover|mouseout|focus|blur|keyup|keydown|submit|change)\s*=/i
const PATH_TRAVERSAL_RE = /(?:\.\.\/){2,}|%2e%2e(?:%2f|\/)|%252e%252e/i
const SHELL_RE = /(?:;|\|)\s*(?:ls|cat|id|whoami|uname|pwd|wget|curl)\b/i
const TEMPLATE_RE = /\$\{[^}]{1,100}\}|`[^`]{1,200}`/

// ── 4. In-memory edge rate limiter ───────────────────────────────────────────
interface Bucket {
  count: number
  windowStart: number
}

const RATE_MAP = new Map<string, Bucket>()
const WINDOW_MS = 60_000 // 1 minute window

const LIMITS = {
  page: 120, // max 120 page requests/IP/minute
  api:   30, // max 30 API requests/IP/minute
}

// Cheap FNV-1a 32-bit hash — edge-safe, no crypto API needed
function fnv32(s: string): string {
  let h = 0x811c9dc5
  for (let i = 0; i < s.length; i++) {
    h ^= s.charCodeAt(i)
    h = (Math.imul(h, 0x01000193) >>> 0)
  }
  return h.toString(16)
}

export function edgeRateLimit(ip: string, isApi: boolean): boolean {
  const limit = isApi ? LIMITS.api : LIMITS.page
  const key   = fnv32(ip) + (isApi ? ':a' : ':p')
  const now   = Date.now()

  const bucket = RATE_MAP.get(key)

  if (!bucket || now - bucket.windowStart > WINDOW_MS) {
    RATE_MAP.set(key, { count: 1, windowStart: now })
    // Prune stale entries to prevent memory leaks in long-lived edge workers
    if (RATE_MAP.size > 2000) {
      for (const [k, v] of RATE_MAP) {
        if (now - v.windowStart > WINDOW_MS) RATE_MAP.delete(k)
      }
    }
    return false // not rate limited
  }

  bucket.count++
  return bucket.count > limit
}

// ── Public detection functions ───────────────────────────────────────────────

export function isBadBot(userAgent: string): boolean {
  return BAD_BOT_RE.test(userAgent)
}

export function isAttackPath(pathname: string): boolean {
  return ATTACK_PATH_RE.test(pathname)
}

export function isInjectionAttempt(raw: string): boolean {
  // Decode once so URL-encoded payloads are also caught
  let s = raw
  try { s = decodeURIComponent(raw) } catch { /* keep raw */ }
  return (
    SQLI_RE.test(s) ||
    XSS_RE.test(s) ||
    PATH_TRAVERSAL_RE.test(s) ||
    SHELL_RE.test(s) ||
    TEMPLATE_RE.test(s)
  )
}
