// Task 6: In-memory cache with TTL + Rate Limiting
const responseCache = new Map();

function cacheGet(key) {
  const entry = responseCache.get(key);
  if (!entry) return null;
  if (Date.now() > entry.expires) {
    responseCache.delete(key);
    return null;
  }
  return entry.data;
}

function cacheSet(key, data, ttlMs) {
  responseCache.set(key, { data, expires: Date.now() + ttlMs });
  // Evict old entries periodically (keep cache under 500 entries)
  if (responseCache.size > 500) {
    const now = Date.now();
    for (const [k, v] of responseCache) {
      if (now > v.expires) responseCache.delete(k);
    }
  }
}

const CACHE_TTL_LOOKUP = 5 * 60 * 1000;   // 5 minutes
const CACHE_TTL_LOOKUP_DEGRADED = 90 * 1000; // matches the existing 90s guard for suspicious /api/validate zero-counts
const CACHE_TTL_DEFAULT = 5 * 60 * 1000;  // 5 minutes

// RDAP Cache — prevents 429 flooding on LACNIC/AFRINIC/APNIC/ARIN
const rdapCache = new Map(); // key: asn string, value: { data, ts }
const RDAP_CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours

function rdapCacheGet(asn) {
  const e = rdapCache.get(String(asn));
  if (e && (Date.now() - e.ts) < RDAP_CACHE_TTL) return e.data;
  return undefined; // undefined = not cached, null = cached miss
}
function rdapCacheSet(asn, data) {
  if (rdapCache.size > 5000) {
    rdapCache.delete(rdapCache.keys().next().value);
  }
  rdapCache.set(String(asn), { data, ts: Date.now() });
}

// WHOIS Cache — 24h TTL, prevents repeated RDAP hammering
const whoisCache = new Map(); // key: asn string, value: { data, ts, ttl }
const WHOIS_CACHE_TTL = 24 * 60 * 60 * 1000;
// A "not found" result after all 5 RIR sources failed is far more often a shared
// network blip (DNS, outbound connectivity, simultaneous rate-limit) than a real
// ASN existing nowhere across RIPE+APNIC+ARIN+LACNIC+AFRINIC -- fetchJSON can't
// tell timeout/network-error apart from a genuine 404, so cache that specific
// conclusion much more briefly than a confirmed, parsed WHOIS record.
const WHOIS_NOT_FOUND_CACHE_TTL = 10 * 60 * 1000;
function whoisCacheGet(asn) {
  const e = whoisCache.get(String(asn));
  if (e && (Date.now() - e.ts) < (e.ttl || WHOIS_CACHE_TTL)) return e.data;
  return undefined;
}
function whoisCacheSet(asn, data, ttl) {
  if (whoisCache.size > 5000) whoisCache.delete(whoisCache.keys().next().value);
  whoisCache.set(String(asn), { data, ts: Date.now(), ttl: ttl || WHOIS_CACHE_TTL });
}

// Quick-IX Cache — 1h TTL, for Peering Recommendations
const quickIxCache = new Map(); // key: asn string, value: { data, ts }
const QUICK_IX_CACHE_TTL = 60 * 60 * 1000;
function quickIxCacheGet(asn) {
  const e = quickIxCache.get(String(asn));
  if (e && (Date.now() - e.ts) < QUICK_IX_CACHE_TTL) return e.data;
  return undefined;
}
function quickIxCacheSet(asn, data) {
  if (quickIxCache.size > 2000) quickIxCache.delete(quickIxCache.keys().next().value);
  quickIxCache.set(String(asn), { data, ts: Date.now() });
}

// bgproutes + ASPA result caches — 15min TTL, prevent re-hitting slow APIs
const bgproutesResultCache = new Map();
const aspaResultCache = new Map();
const validateResultCache = new Map();
const RESULT_CACHE_TTL = 15 * 60 * 1000; // 15 minutes
function resultCacheGet(map, key) {
  const e = map.get(String(key));
  const ttl = e && e.ttl ? e.ttl : RESULT_CACHE_TTL;
  if (e && (Date.now() - e.ts) < ttl) return e.data;
  return undefined;
}
function resultCacheSet(map, key, data, ttlMs) {
  if (map.size > 2000) map.delete(map.keys().next().value);
  map.set(String(key), { data, ts: Date.now(), ttl: ttlMs || RESULT_CACHE_TTL });
}

module.exports = {
  responseCache,
  cacheGet,
  cacheSet,
  RESULT_CACHE_TTL,
  CACHE_TTL_LOOKUP,
  CACHE_TTL_LOOKUP_DEGRADED,
  CACHE_TTL_DEFAULT,
  rdapCacheGet,
  rdapCacheSet,
  whoisCacheGet,
  whoisCacheSet,
  WHOIS_NOT_FOUND_CACHE_TTL,
  quickIxCacheGet,
  quickIxCacheSet,
  bgproutesResultCache,
  aspaResultCache,
  validateResultCache,
  resultCacheGet,
  resultCacheSet,
};
