/**
 * In-memory rate limiter utility — VF-02 security fix.
 *
 * Uses a Map keyed by IP address. Each entry tracks request timestamps
 * within a sliding window. Stale entries are cleaned up on each check.
 *
 * Defaults:
 *   GET  — 60 requests per 60-second window
 *   POST/PUT/DELETE — 20 requests per 60-second window
 *
 * Returns a 429 Response when the limit is exceeded, or null when allowed.
 */

import { NextRequest } from 'next/server';

interface RateLimitEntry {
  timestamps: number[];
}

// Module-level store — persists for the lifetime of the serverless instance
const store = new Map<string, RateLimitEntry>();

const WINDOW_MS = 60_000; // 60-second sliding window

export const RATE_LIMIT_GET = 60;   // requests per window for GET
export const RATE_LIMIT_WRITE = 20; // requests per window for POST/PUT/DELETE

/**
 * Extract client IP from the request headers.
 * Prefers Vercel's x-real-ip, falls back to x-forwarded-for, then '127.0.0.1'.
 */
function getClientIp(req: NextRequest): string {
  return (
    req.headers.get('x-real-ip') ||
    req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ||
    '127.0.0.1'
  );
}

/**
 * Clean up entries older than the sliding window to prevent unbounded growth.
 */
function cleanup(entry: RateLimitEntry): void {
  const cutoff = Date.now() - WINDOW_MS;
  entry.timestamps = entry.timestamps.filter(ts => ts > cutoff);
}

/**
 * Check rate limit for a request.
 *
 * @param req      The incoming Next.js request
 * @param limit    Maximum requests allowed in the window (default: RATE_LIMIT_WRITE)
 * @returns        null if allowed, or a 429 Response object if exceeded
 */
export function checkRateLimit(
  req: NextRequest,
  limit: number = RATE_LIMIT_WRITE,
): Response | null {
  const ip = getClientIp(req);
  const key = `${ip}:${limit}`;
  const now = Date.now();

  if (!store.has(key)) {
    store.set(key, { timestamps: [] });
  }

  const entry = store.get(key)!;
  cleanup(entry);

  const remaining = Math.max(0, limit - entry.timestamps.length);
  const resetAt = Math.ceil((now + WINDOW_MS) / 1000); // Unix epoch seconds

  if (entry.timestamps.length >= limit) {
    return new Response(
      JSON.stringify({
        error: 'Too many requests. Please slow down and retry after the window resets.',
        retryAfter: resetAt,
      }),
      {
        status: 429,
        headers: {
          'Content-Type': 'application/json',
          'X-RateLimit-Limit': String(limit),
          'X-RateLimit-Remaining': '0',
          'X-RateLimit-Reset': String(resetAt),
          'Retry-After': String(Math.ceil(WINDOW_MS / 1000)),
        },
      },
    );
  }

  entry.timestamps.push(now);

  // Attach rate-limit info headers to the caller so they can merge them into their own response
  // (Headers object is mutable but NextResponse doesn't easily accept pre-built headers from here,
  // so the check just returns null and callers use the addRateLimitHeaders helper below.)
  return null;
}

/**
 * Build rate-limit informational headers to include in successful responses.
 */
export function getRateLimitHeaders(
  req: NextRequest,
  limit: number = RATE_LIMIT_WRITE,
): Record<string, string> {
  const ip = getClientIp(req);
  const key = `${ip}:${limit}`;
  const entry = store.get(key);
  const now = Date.now();
  const used = entry ? entry.timestamps.filter(ts => ts > now - WINDOW_MS).length : 0;
  const remaining = Math.max(0, limit - used);
  const resetAt = Math.ceil((now + WINDOW_MS) / 1000);

  return {
    'X-RateLimit-Limit': String(limit),
    'X-RateLimit-Remaining': String(remaining),
    'X-RateLimit-Reset': String(resetAt),
  };
}
