/**
 * Einfacher In-Memory-Rate-Limiter fuer oeffentliche API-Routen.
 * Speichert pro IP-Adresse die Anzahl der Requests in einem Zeitfenster.
 *
 * Hinweis: Das funktioniert fuer einen einzelnen Node-Prozess. Hinter einem
 * Load Balancer mit mehreren Instanzen braeuchte man Redis oder einen
 * aehnlichen zentralen Store. Fuer den aktuellen PM2-Single-Node-Betrieb ist
 * dieser Mechanismus ausreichend.
 */

interface Bucket {
  count: number;
  resetAt: number;
}

const store = new Map<string, Bucket>();

function getClientIp(req: { headers: { get(name: string): string | null }; ip?: string | null }): string {
  const forwarded = req.headers.get('x-forwarded-for');
  if (forwarded) {
    // Apache (mod_proxy, ProxyAddHeaders On) haengt die echte Client-IP ANS
    // ENDE der X-Forwarded-For-Liste an. Die erste Position kann der Client
    // frei setzen und damit spoofen - deshalb wird bewusst der LETZTE,
    // vom Proxy hinzugefuegte Eintrag verwendet.
    const parts = forwarded
      .split(',')
      .map((p) => p.trim())
      .filter(Boolean);
    if (parts.length > 0) return parts[parts.length - 1];
  }
  return req.ip ?? 'unknown';
}

function cleanup(now: number) {
  for (const [key, bucket] of store.entries()) {
    if (bucket.resetAt < now) {
      store.delete(key);
    }
  }
}

/**
 * Prueft, ob der Request fuer die gegebene Aktion noch erlaubt ist.
 * Rueckgabe: { allowed: true } oder { allowed: false, retryAfter: Sekunden }.
 */
export function checkRateLimit(
  req: { headers: { get(name: string): string | null }; ip?: string | null },
  action: string,
  maxRequests: number,
  windowSeconds: number,
): { allowed: true } | { allowed: false; retryAfter: number } {
  const now = Date.now();
  // Alle 5 Minuten abgelaufene Eintraege entfernen, damit die Map nicht endlos waechst.
  if (Math.random() < 0.01) cleanup(now);

  const ip = getClientIp(req);
  const key = `${ip}:${action}`;
  const bucket = store.get(key);

  if (!bucket || bucket.resetAt < now) {
    store.set(key, { count: 1, resetAt: now + windowSeconds * 1000 });
    return { allowed: true };
  }

  if (bucket.count >= maxRequests) {
    const retryAfter = Math.ceil((bucket.resetAt - now) / 1000);
    return { allowed: false, retryAfter };
  }

  bucket.count += 1;
  return { allowed: true };
}
