/**
 * Simple Rate Limiter
 * IP-based rate limiting for API endpoints
 */

import { NextRequest } from "next/server";

interface RateLimitEntry {
  count: number;
  resetTime: number;
  blocked: boolean;
}

interface RateLimitConfig {
  windowMs: number; // Time window in milliseconds
  maxRequests: number; // Max requests per window
  blockDurationMs: number; // How long to block after exceeding limit
}

// In-memory store (resets on server restart)
// For production, use Redis or similar
const rateLimitStore = new Map<string, RateLimitEntry>();

// Cleanup interval (every 10 minutes)
const CLEANUP_INTERVAL = 10 * 60 * 1000;

// Default rate limit config
const DEFAULT_CONFIG: RateLimitConfig = {
  windowMs: 60 * 60 * 1000, // 1 hour
  maxRequests: 20, // 20 requests per hour per IP
  blockDurationMs: 30 * 60 * 1000, // 30 minutes block
};

// Stricter config for unauthenticated users
const PUBLIC_CONFIG: RateLimitConfig = {
  windowMs: 60 * 60 * 1000, // 1 hour
  maxRequests: 10, // 10 requests per hour
  blockDurationMs: 60 * 60 * 1000, // 1 hour block
};

/**
 * Cleanup old entries from the rate limit store
 */
function cleanupStore(): void {
  const now = Date.now();
  for (const [key, entry] of rateLimitStore.entries()) {
    if (entry.resetTime < now && !entry.blocked) {
      rateLimitStore.delete(key);
    } else if (
      entry.blocked &&
      entry.resetTime < now - DEFAULT_CONFIG.blockDurationMs
    ) {
      rateLimitStore.delete(key);
    }
  }
}

// Run cleanup periodically
if (typeof setInterval !== "undefined") {
  setInterval(cleanupStore, CLEANUP_INTERVAL);
}

/**
 * Gets client IP from request
 */
export function getClientIp(request: NextRequest): string {
  // Try various headers that might contain the real IP
  const forwarded = request.headers.get("x-forwarded-for");
  if (forwarded) {
    // x-forwarded-for can contain multiple IPs, first is the client
    return forwarded.split(",")[0].trim();
  }

  const realIp = request.headers.get("x-real-ip");
  if (realIp) {
    return realIp;
  }

  // Fallback to a default (in development)
  return "unknown";
}

/**
 * Checks if an IP is rate limited
 */
export function checkRateLimit(
  ip: string,
  config: RateLimitConfig = DEFAULT_CONFIG,
): {
  allowed: boolean;
  remaining: number;
  resetTime: number;
  blocked: boolean;
} {
  const now = Date.now();
  const entry = rateLimitStore.get(ip);

  // If no entry exists, create one
  if (!entry) {
    rateLimitStore.set(ip, {
      count: 1,
      resetTime: now + config.windowMs,
      blocked: false,
    });
    return {
      allowed: true,
      remaining: config.maxRequests - 1,
      resetTime: now + config.windowMs,
      blocked: false,
    };
  }

  // Check if currently blocked
  if (entry.blocked) {
    const blockEndTime = entry.resetTime + config.blockDurationMs;
    if (now < blockEndTime) {
      return {
        allowed: false,
        remaining: 0,
        resetTime: blockEndTime,
        blocked: true,
      };
    }
    // Block expired, reset
    entry.blocked = false;
    entry.count = 0;
    entry.resetTime = now + config.windowMs;
  }

  // Check if window has expired
  if (now > entry.resetTime) {
    entry.count = 1;
    entry.resetTime = now + config.windowMs;
    return {
      allowed: true,
      remaining: config.maxRequests - 1,
      resetTime: entry.resetTime,
      blocked: false,
    };
  }

  // Check if limit exceeded
  if (entry.count >= config.maxRequests) {
    entry.blocked = true;
    return {
      allowed: false,
      remaining: 0,
      resetTime: entry.resetTime + config.blockDurationMs,
      blocked: true,
    };
  }

  // Increment count
  entry.count++;
  return {
    allowed: true,
    remaining: config.maxRequests - entry.count,
    resetTime: entry.resetTime,
    blocked: false,
  };
}

/**
 * Rate limit middleware for API routes
 */
export function withRateLimit(
  request: NextRequest,
  config: RateLimitConfig = PUBLIC_CONFIG,
): { success: true } | { success: false; error: string; retryAfter: number } {
  const ip = getClientIp(request);
  const result = checkRateLimit(ip, config);

  if (!result.allowed) {
    const retryAfter = Math.ceil((result.resetTime - Date.now()) / 1000);
    return {
      success: false,
      error: result.blocked
        ? "Too many requests. Your IP has been temporarily blocked."
        : "Rate limit exceeded. Please try again later.",
      retryAfter,
    };
  }

  return { success: true };
}

/**
 * Gets remaining requests for an IP
 */
export function getRemainingRequests(
  ip: string,
  config: RateLimitConfig = DEFAULT_CONFIG,
): number {
  const entry = rateLimitStore.get(ip);
  if (!entry || Date.now() > entry.resetTime) {
    return config.maxRequests;
  }
  return Math.max(0, config.maxRequests - entry.count);
}

/**
 * Resets rate limit for an IP (admin use)
 */
export function resetRateLimit(ip: string): void {
  rateLimitStore.delete(ip);
}

/**
 * Gets current rate limit stats (admin use)
 */
export function getRateLimitStats(): { totalIps: number; blockedIps: number } {
  let blockedIps = 0;
  for (const entry of rateLimitStore.values()) {
    if (entry.blocked) blockedIps++;
  }
  return {
    totalIps: rateLimitStore.size,
    blockedIps,
  };
}
