'use strict'
// Rate-limiter EN MÉMOIRE (fenêtre glissante), par IP × bucket. Aucune dépendance, FAIL-OPEN (toute erreur =
// autorise). But : freiner l'énumération de tokens / brute-force OTP sur les routes PUBLIQUES sensibles, sans
// jamais bloquer un vrai usager (limites généreuses). Derrière Traefik → vraie IP dans X-Forwarded-For.
const buckets = new Map() // "ip|bucket" → [timestamps ms]
let _lastSweep = 0

function clientIp (req) {
  const xff = String(req.headers['x-forwarded-for'] || '').split(',')[0].trim()
  return xff || (req.socket && req.socket.remoteAddress) || 'unknown'
}

// true = la requête DOIT être bloquée (limite atteinte dans la fenêtre).
function limited (req, bucket, max, windowMs) {
  try {
    const key = clientIp(req) + '|' + bucket
    const now = Date.now(); const cut = now - windowMs
    let arr = buckets.get(key); if (!arr) { arr = []; buckets.set(key, arr) }
    while (arr.length && arr[0] < cut) arr.shift()
    if (arr.length >= max) return true
    arr.push(now)
    return false
  } catch (e) { return false } // fail-open : ne jamais casser une requête à cause du limiteur
}

// Purge périodique des buckets vidés/anciens (borne la mémoire) — appelée à l'occasion, pas de timer.
function maybeSweep (windowMs) {
  const now = Date.now(); if (now - _lastSweep < 60000) return; _lastSweep = now
  const cut = now - Math.max(windowMs || 60000, 60000)
  for (const [k, arr] of buckets) { while (arr.length && arr[0] < cut) arr.shift(); if (!arr.length) buckets.delete(k) }
}

module.exports = { limited, clientIp, maybeSweep }
