/**
 * Velocity guardrail.
 *
 * Detects rapid repeated hits from the same IP hash within a short window.
 * A single IP submitting >N touch requests in T minutes is a velocity burst,
 * consistent with bot traffic or affiliate link spam.
 *
 * Implementation uses an in-memory Map keyed by ipHash (rotated daily via the
 * HMAC IP hash). In production on CF Workers, each isolate has its own Map;
 * this provides per-isolate rate limiting which is sufficient for burst
 * detection (a bot will consistently hit the same isolate within a request
 * burst). For cross-isolate rate limiting, the touch endpoint can be extended
 * to use Durable Objects — deferred to a future task.
 *
 * Thresholds (defaults):
 *   - MAX_HITS_PER_WINDOW: 10 touches per IP per window
 *   - WINDOW_MS: 60_000 (1 minute)
 */

import type { GuardResult } from './brand-keyword.js'

const MAX_HITS_PER_WINDOW = 10
const WINDOW_MS = 60_000
const MAX_MAP_SIZE = 50_000

type WindowEntry = { count: number; windowStart: number }
const hitMap = new Map<string, WindowEntry>()

// Lazy eviction: when map exceeds MAX_MAP_SIZE, sweep expired entries.
// Bounds memory growth under flood — single-isolate DoS protection.
// Cross-isolate global rate limiting requires Durable Objects (deferred).
function evictExpired(nowMs: number): void {
  for (const [k, v] of hitMap) {
    if (nowMs - v.windowStart > WINDOW_MS) {
      hitMap.delete(k)
    }
  }
  // If still over cap after expired sweep (sustained burst from many IPs),
  // hard-drop oldest insertion-order entries until under cap.
  if (hitMap.size > MAX_MAP_SIZE) {
    const overflow = hitMap.size - MAX_MAP_SIZE
    let n = 0
    for (const k of hitMap.keys()) {
      if (n++ >= overflow) break
      hitMap.delete(k)
    }
  }
}

/**
 * Evaluate velocity burst.
 *
 * @param ipHash  - HMAC-derived daily-keyed IP hash (16-char hex).
 * @param nowMs   - Current timestamp in ms (injectable for testing).
 */
export function evaluateVelocity(
  ipHash: string,
  nowMs: number = Date.now(),
): GuardResult {
  if (!ipHash) {
    return { isSuspicious: false, codes: [], setCookie: true }
  }

  if (hitMap.size > MAX_MAP_SIZE) {
    evictExpired(nowMs)
  }

  const existing = hitMap.get(ipHash)

  if (!existing || nowMs - existing.windowStart > WINDOW_MS) {
    // New window.
    hitMap.set(ipHash, { count: 1, windowStart: nowMs })
    return { isSuspicious: false, codes: [], setCookie: true }
  }

  existing.count += 1

  if (existing.count > MAX_HITS_PER_WINDOW) {
    return {
      isSuspicious: true,
      codes: ['VELOCITY_BURST'],
      setCookie: false,
    }
  }

  return { isSuspicious: false, codes: [], setCookie: true }
}

/** Reset for testing only. */
export function _resetVelocityMap(): void {
  hitMap.clear()
}
