/**
 * IP-keyed rate-limit middleware.
 *
 * Sliding-window counter per remote IP, kept in-memory. Use for routes
 * that take expensive upstream calls and aren't behind requireAuth —
 * the public demo (/api/analyze/*) is the canonical caller. Auth'd
 * routes are gated by tier, which already throttles abuse.
 *
 * Why in-memory not Redis: this middleware is the FIRST line of defense
 * — it must not depend on Redis being warm. If Redis is down the API
 * still serves and this still throttles. Memory cost is bounded by
 * MAX_TRACKED_IPS (the LRU-style trim on overflow).
 *
 * Why not the factory in src/utils/rateLimiter.js: that gives one bucket
 * per call site. We need one bucket PER IP.
 */

const DEFAULT_WINDOW_MS = 60_000;
const DEFAULT_MAX = 10;
const MAX_TRACKED_IPS = 10_000;

function clientIp(req) {
  // Express's req.ip respects trust proxy; fall back to socket if not.
  return req.ip || req.socket?.remoteAddress || 'unknown';
}

function createRateLimit({ windowMs = DEFAULT_WINDOW_MS, max = DEFAULT_MAX, key = clientIp } = {}) {
  // Map preserves insertion order; oldest-IP eviction is O(1) via shift.
  const hits = new Map();

  function evictIfFull() {
    if (hits.size <= MAX_TRACKED_IPS) return;
    // Drop the oldest entry — Map.keys() yields in insertion order.
    const first = hits.keys().next().value;
    if (first !== undefined) hits.delete(first);
  }

  function pruneOlderThan(timestamps, cutoff) {
    // In-place filter (mutating the array end-to-start), faster than
    // building a new array on every request. Returns the surviving count.
    let writeIdx = 0;
    for (let i = 0; i < timestamps.length; i += 1) {
      if (timestamps[i] > cutoff) {
        timestamps[writeIdx] = timestamps[i];
        writeIdx += 1;
      }
    }
    timestamps.length = writeIdx;
    return writeIdx;
  }

  return function rateLimit(req, res, next) {
    const id = key(req);
    const now = Date.now();
    const cutoff = now - windowMs;

    let timestamps = hits.get(id);
    if (!timestamps) {
      timestamps = [];
      hits.set(id, timestamps);
      evictIfFull();
    }

    const remaining = pruneOlderThan(timestamps, cutoff);
    if (remaining >= max) {
      const retryAfterSec = Math.max(1, Math.ceil((timestamps[0] + windowMs - now) / 1000));
      res.set('Retry-After', String(retryAfterSec));
      return res.status(429).json({ error: 'Too many requests' });
    }

    timestamps.push(now);
    return next();
  };
}

module.exports = { createRateLimit, __internals: { clientIp, MAX_TRACKED_IPS } };
