/**
 * Per-tier rate limiter for /api/scan and other write endpoints.
 *
 * Bucket model:
 *   - free:    5 scans/minute, daily cap of 5 (the daily cap is enforced
 *              elsewhere in the route handler)
 *   - analyst: 30 scans/minute, unlimited daily
 *   - desk:    60 scans/minute, unlimited daily
 *
 * Storage:
 *   - In-memory ring buffer per key.
 *   - Process-local — good for single-instance Vercel deployments. For
 *     multi-instance later, swap to upstash/redis without changing the
 *     external interface.
 */

import { NextResponse, type NextRequest } from 'next/server';

type Tier = 'free' | 'analyst' | 'desk';

const LIMITS: Record<Tier, number> = {
  free: 5,
  analyst: 30,
  desk: 60,
};

const WINDOW_MS = 60_000;

interface Bucket {
  hits: number[]; // ms timestamps
}

const buckets = new Map<string, Bucket>();

export function rateLimitCheck(key: string, tier: Tier): { ok: true } | { ok: false; retryAfter: number } {
  const limit = LIMITS[tier] ?? LIMITS.free;
  const now = Date.now();
  const bucket = buckets.get(key) ?? { hits: [] };

  // Drop hits older than the window
  while (bucket.hits.length && now - bucket.hits[0] > WINDOW_MS) {
    bucket.hits.shift();
  }

  if (bucket.hits.length >= limit) {
    const oldest = bucket.hits[0];
    const retryAfter = Math.max(1, Math.ceil((WINDOW_MS - (now - oldest)) / 1000));
    buckets.set(key, bucket);
    return { ok: false, retryAfter };
  }

  bucket.hits.push(now);
  buckets.set(key, bucket);

  // Opportunistic GC — keep the map small in long-running processes
  if (buckets.size > 5000 && Math.random() < 0.01) {
    pruneStale();
  }

  return { ok: true };
}

export function rateLimitResponse(retryAfter: number) {
  return NextResponse.json(
    {
      error: "Slow down — you're reading faster than the model can think. Try again in a minute.",
      retryAfter,
    },
    { status: 429, headers: { 'Retry-After': String(retryAfter) } },
  );
}

/**
 * Build a stable key from the incoming request. Prefers the auth bearer
 * (per-user) and falls back to the forwarded IP for anonymous traffic.
 */
export function rateLimitKey(req: NextRequest): string {
  const auth = req.headers.get('authorization');
  if (auth) return `user:${auth.slice(-32)}`;
  const fwd = req.headers.get('x-forwarded-for') || '';
  const ip = fwd.split(',')[0].trim() || 'anon';
  return `ip:${ip}`;
}

function pruneStale() {
  const cutoff = Date.now() - WINDOW_MS * 2;
  for (const [key, bucket] of buckets.entries()) {
    if (!bucket.hits.length || bucket.hits[bucket.hits.length - 1] < cutoff) {
      buckets.delete(key);
    }
  }
}
