/**
 * Simple in-memory rate limiting for API endpoints
 * In production, use Redis or a proper rate limiting service
 * 
 * SECURITY NOTES:
 * - This is an in-memory store, so rate limits are per-instance
 * - For multi-instance deployments, use Redis or a shared cache
 * - IP-based rate limiting can be bypassed by users with rotating IPs
 * - Consider adding user ID-based limiting for authenticated routes
 */

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

const rateLimitStore = new Map<string, RateLimitEntry>();
const WINDOW_MS = 15 * 60 * 1000; // 15 minutes
const MAX_REQUESTS = 100; // 100 requests per window (more generous for general use)

// Cleanup interval to prevent memory leaks (H14 fix)
const CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // Run cleanup every 5 minutes
let cleanupInterval: NodeJS.Timeout | null = null;

/**
 * Start the cleanup interval to prevent memory leaks
 * This should be called once when the app starts
 */
export function startRateLimitCleanup(): void {
  if (cleanupInterval) return; // Already running
  
  cleanupInterval = setInterval(() => {
    const now = Date.now();
    let cleaned = 0;
    
    for (const [key, entry] of rateLimitStore.entries()) {
      if (now > entry.resetTime) {
        rateLimitStore.delete(key);
        cleaned++;
      }
    }
    
    if (cleaned > 0) {
      console.log(`[RateLimit] Cleaned up ${cleaned} expired entries`);
    }
  }, CLEANUP_INTERVAL_MS);
  
  // Ensure cleanup doesn't prevent process exit (for serverless)
  if (cleanupInterval.unref) {
    cleanupInterval.unref();
  }
}

/**
 * Stop the cleanup interval (useful for testing)
 */
export function stopRateLimitCleanup(): void {
  if (cleanupInterval) {
    clearInterval(cleanupInterval);
    cleanupInterval = null;
  }
}

/**
 * Get client IP address from request
 * Handles various proxy scenarios and falls back to 'unknown'
 */
export function getClientIP(request: Request): string {
  // Try X-Forwarded-For first (common proxy header)
  const forwarded = request.headers.get('x-forwarded-for');
  if (forwarded) {
    // X-Forwarded-For can be a comma-separated list; use the first (original client)
    return forwarded.split(',')[0].trim();
  }
  
  // Try other common headers
  const realIP = request.headers.get('x-real-ip');
  if (realIP) return realIP;
  
  // Fallback to a default (for serverless/edge environments)
  return 'unknown';
}

/**
 * Check if request is allowed based on rate limit
 * @param identifier - Unique identifier (IP address, user ID, etc.)
 * @param maxRequests - Maximum requests allowed in the window (optional, uses default if not provided)
 * @param windowMs - Time window in milliseconds (optional, uses default if not provided)
 * @returns Object with allowed status and remaining requests
 */
export function checkRateLimit(
  identifier: string,
  maxRequests?: number,
  windowMs?: number
): {
  allowed: boolean;
  remaining: number;
  resetTime: number;
  totalLimit: number;
} {
  const limit = maxRequests ?? MAX_REQUESTS;
  const window = windowMs ?? WINDOW_MS;
  const now = Date.now();
  const entry = rateLimitStore.get(identifier);
  
  if (!entry || now > entry.resetTime) {
    // New window or expired
    rateLimitStore.set(identifier, {
      count: 1,
      resetTime: now + window,
    });
    return {
      allowed: true,
      remaining: limit - 1,
      resetTime: now + window,
      totalLimit: limit,
    };
  }
  
  if (entry.count >= limit) {
    // Rate limit exceeded
    return {
      allowed: false,
      remaining: 0,
      resetTime: entry.resetTime,
      totalLimit: limit,
    };
  }
  
  // Within limit
  entry.count++;
  return {
    allowed: true,
    remaining: limit - entry.count,
    resetTime: entry.resetTime,
    totalLimit: limit,
  };
}

/**
 * Get rate limit headers for response
 */
export function getRateLimitHeaders(
  identifier: string,
  maxRequests?: number,
  windowMs?: number
): Record<string, string> {
  const limit = checkRateLimit(identifier, maxRequests, windowMs);
  return {
    'X-RateLimit-Limit': limit.totalLimit.toString(),
    'X-RateLimit-Remaining': Math.max(0, limit.remaining).toString(),
    'X-RateLimit-Reset': new Date(limit.resetTime).toISOString(),
  };
}

/**
 * Create a rate limit response with proper headers
 */
export function createRateLimitResponse(
  identifier: string,
  retryAfterSeconds?: number
): Response {
  const headers = new Headers({
    'Content-Type': 'application/json',
    'X-RateLimit-Limit': MAX_REQUESTS.toString(),
    'X-RateLimit-Remaining': '0',
  });
  
  if (retryAfterSeconds) {
    headers.set('Retry-After', retryAfterSeconds.toString());
  }
  
  return new Response(
    JSON.stringify({
      error: 'Rate limit exceeded. Please try again later.',
      retryAfter: retryAfterSeconds,
    }),
    {
      status: 429,
      headers,
    }
  );
}

// Auto-start cleanup when module loads
startRateLimitCleanup();

// Export constants for use in route handlers
export { MAX_REQUESTS, WINDOW_MS };
