/**
 * ARCH-2 (Session 7e): LEGACY rate limiter. The canonical implementation
 * lives in src/utils/rateLimiter.js (factory pattern, paired with
 * createCircuitBreaker). New code should import from there.
 *
 * Current callers of this legacy module (remove this file when the list
 * empties):
 *   - src/services/UnifiedOddsProvider.js
 *   - src/services/adapters/ESPNAdapter.js
 *   - src/services/adapters/PinnacleAdapter.js
 *
 * Token bucket rate limiter, per upstream key.
 *
 * Each upstream (ESPN, Pinnacle, DK, etc.) gets its own bucket so a runaway
 * caller on one source doesn't starve the others. Buckets refill on demand,
 * so we don't pay for setInterval timers.
 *
 * Usage:
 *   const limiter = require('./rateLimiter');
 *   await limiter.take('espn', 1);   // throws if the wait would exceed maxWaitMs
 */

const DEFAULT_LIMIT = { capacity: 10, refillPerSec: 5 };

// Tunable per source. Numbers chosen to stay well under each upstream's
// observed rate limits.
const LIMITS = Object.freeze({
  espn:        { capacity: 30, refillPerSec: 10 },
  pinnacle:    { capacity: 10, refillPerSec: 3 },
  draftkings:  { capacity: 10, refillPerSec: 2 },
  fanduel:     { capacity: 10, refillPerSec: 2 },
  betmgm:      { capacity: 10, refillPerSec: 2 },
  caesars:     { capacity: 10, refillPerSec: 2 },
  prizepicks:  { capacity: 10, refillPerSec: 2 },
  covers:      { capacity: 5,  refillPerSec: 1 },
  rotowire:    { capacity: 5,  refillPerSec: 1 },
  weather:     { capacity: 20, refillPerSec: 5 },
  injuries:    { capacity: 20, refillPerSec: 5 },
  'nba-stats': { capacity: 8,  refillPerSec: 1 },  // stats.nba.com is strict
  pybaseball:  { capacity: 4,  refillPerSec: 0.5 }, // Statcast tolerates roughly 1 req / 2s
});

const buckets = new Map();

function getBucket(key) {
  let b = buckets.get(key);
  if (!b) {
    const cfg = LIMITS[key] || DEFAULT_LIMIT;
    b = {
      tokens: cfg.capacity,
      capacity: cfg.capacity,
      refillPerSec: cfg.refillPerSec,
      lastRefillMs: Date.now(),
    };
    buckets.set(key, b);
  }
  return b;
}

function refill(b) {
  const now = Date.now();
  const elapsedSec = (now - b.lastRefillMs) / 1000;
  if (elapsedSec <= 0) return;
  b.tokens = Math.min(b.capacity, b.tokens + elapsedSec * b.refillPerSec);
  b.lastRefillMs = now;
}

/**
 * Wait for `cost` tokens on the named bucket. Resolves once tokens are
 * consumed. Rejects if the projected wait exceeds maxWaitMs (default 5s).
 */
async function take(key, cost = 1, maxWaitMs = 5_000) {
  const b = getBucket(key);
  while (true) {
    refill(b);
    if (b.tokens >= cost) {
      b.tokens -= cost;
      return;
    }
    const needed = cost - b.tokens;
    const waitMs = Math.ceil((needed / b.refillPerSec) * 1000);
    if (waitMs > maxWaitMs) {
      const err = new Error(`rate limit wait exceeded for ${key}`);
      err.code = 'RATE_LIMIT_TIMEOUT';
      err.retryAfterMs = waitMs;
      throw err;
    }
    await new Promise((r) => setTimeout(r, Math.min(waitMs, 250)));
  }
}

function snapshot() {
  const out = {};
  for (const [k, b] of buckets.entries()) {
    refill(b);
    out[k] = { tokens: Math.floor(b.tokens), capacity: b.capacity, refillPerSec: b.refillPerSec };
  }
  return out;
}

module.exports = { take, snapshot, LIMITS };
