/**
 * Fixed-window rate limiting (§36).
 *
 * In-process by design for this build: the platform runs as a single Next.js instance and
 * an in-memory counter has no operational dependencies. `RateLimitStore` is the seam —
 * pointing it at Redis is the only change needed to make limits correct across replicas,
 * and REDIS_URL is already carried through the environment for exactly that.
 */

import 'server-only'

export interface RateLimitResult {
  allowed: boolean
  remaining: number
  resetAt: number
  limit: number
}

export interface RateLimitStore {
  hit(key: string, windowMs: number): Promise<{ count: number; resetAt: number }>
}

interface Bucket {
  count: number
  resetAt: number
}

class MemoryStore implements RateLimitStore {
  private buckets = new Map<string, Bucket>()
  private lastSweep = Date.now()

  async hit(key: string, windowMs: number) {
    const now = Date.now()

    // Amortised cleanup — without it the map grows with every distinct client.
    if (now - this.lastSweep > 60_000) {
      for (const [k, bucket] of this.buckets) {
        if (bucket.resetAt <= now) this.buckets.delete(k)
      }
      this.lastSweep = now
    }

    const existing = this.buckets.get(key)
    if (!existing || existing.resetAt <= now) {
      const bucket = { count: 1, resetAt: now + windowMs }
      this.buckets.set(key, bucket)
      return bucket
    }
    existing.count += 1
    return existing
  }
}

const globalForStore = globalThis as unknown as { nabdhRateLimitStore?: RateLimitStore }
const store: RateLimitStore = globalForStore.nabdhRateLimitStore ?? new MemoryStore()
globalForStore.nabdhRateLimitStore = store

export async function rateLimit(
  key: string,
  limit: number,
  windowMs: number,
): Promise<RateLimitResult> {
  const { count, resetAt } = await store.hit(key, windowMs)
  return {
    allowed: count <= limit,
    remaining: Math.max(0, limit - count),
    resetAt,
    limit,
  }
}

export function rateLimitHeaders(result: RateLimitResult): Record<string, string> {
  return {
    'X-RateLimit-Limit': String(result.limit),
    'X-RateLimit-Remaining': String(result.remaining),
    'X-RateLimit-Reset': String(Math.ceil(result.resetAt / 1000)),
  }
}
