/**
 * RateLimiter - Token Bucket en memoria por usuario.
 * - Límite global por remitente (ID normalizado).
 * - Capacidad y tasa configurables por entorno:
 *   - RATE_LIMIT_PER_MIN: tokens que se recargan por minuto (default 15)
 *   - RATE_LIMIT_BURST: capacidad del bucket (default = RATE_LIMIT_PER_MIN)
 * - Desactivación en tests: la integración en server evita usar el limiter con NODE_ENV='test'.
 */
export class RateLimiter {
  private static readonly buckets = new Map<string, { tokens: number; last: number }>();
  private static readonly lastNotifyAt = new Map<string, number>();
  private static readonly NOTIFY_COOLDOWN_MS = 60_000; // 1 minuto

  private static get ratePerMin(): number {
    const v = Number(process.env.RATE_LIMIT_PER_MIN);
    return Number.isFinite(v) && v > 0 ? v : 15;
  }

  private static get capacity(): number {
    const burst = Number(process.env.RATE_LIMIT_BURST);
    if (Number.isFinite(burst) && burst > 0) return burst;
    return this.ratePerMin; // por defecto, burst = rate
  }

  private static get refillPerMs(): number {
    return this.ratePerMin / 60_000; // tokens por ms
  }

  static checkAndConsume(userId: string, now: number = Date.now()): boolean {
    if (!userId) return true; // por seguridad, no bloquear IDs vacíos

    let bucket = this.buckets.get(userId);
    if (!bucket) {
      bucket = { tokens: this.capacity, last: now };
      this.buckets.set(userId, bucket);
    }

    // Refill lineal en función del tiempo transcurrido
    const elapsed = Math.max(0, now - bucket.last);
    if (elapsed > 0) {
      bucket.tokens = Math.min(this.capacity, bucket.tokens + elapsed * this.refillPerMs);
      bucket.last = now;
    }

    if (bucket.tokens >= 1) {
      bucket.tokens -= 1;
      return true;
    }
    return false;
  }

  static shouldNotify(userId: string, now: number = Date.now()): boolean {
    const last = this.lastNotifyAt.get(userId);
    // Si nunca se ha notificado, permitir notificar inmediatamente
    if (last === undefined) {
      this.lastNotifyAt.set(userId, now);
      return true;
    }
    if (now - last >= this.NOTIFY_COOLDOWN_MS) {
      this.lastNotifyAt.set(userId, now);
      return true;
    }
    return false;
  }

  // Solo para tests
  static reset(): void {
    this.buckets.clear();
    this.lastNotifyAt.clear();
  }
}
