/**
 * Rate Limiting Middleware
 * Agent 4: Production Deployment
 *
 * Provides rate limiting for API routes to prevent abuse.
 */

import type { NextApiRequest, NextApiResponse, NextApiHandler } from 'next';
import { env } from '../config';
import { logger } from '../logging';

interface RateLimitConfig {
  windowMs: number;       // Time window in milliseconds
  maxRequests: number;    // Max requests per window
  keyGenerator?: (req: NextApiRequest) => string;
  skipSuccessfulRequests?: boolean;
  skipFailedRequests?: boolean;
  message?: string;
}

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

// In-memory store for rate limiting
// In production, use Redis for distributed rate limiting
const store = new Map<string, RateLimitEntry>();

// Cleanup old entries periodically
setInterval(() => {
  const now = Date.now();
  for (const [key, entry] of store.entries()) {
    if (entry.resetTime < now) {
      store.delete(key);
    }
  }
}, 60000); // Cleanup every minute

const DEFAULT_CONFIG: RateLimitConfig = {
  windowMs: env.rateLimitWindowMs,
  maxRequests: env.rateLimitMaxRequests,
  message: 'Too many requests, please try again later.',
};

/**
 * Default key generator - uses IP address
 */
function defaultKeyGenerator(req: NextApiRequest): string {
  const forwarded = req.headers['x-forwarded-for'];
  if (typeof forwarded === 'string') {
    return forwarded.split(',')[0].trim();
  }
  if (Array.isArray(forwarded)) {
    return forwarded[0];
  }
  return req.socket?.remoteAddress || 'unknown';
}

/**
 * Check if request is rate limited
 */
function checkRateLimit(
  key: string,
  config: RateLimitConfig
): { limited: boolean; remaining: number; resetTime: number } {
  const now = Date.now();
  let entry = store.get(key);

  // Create new entry if doesn't exist or expired
  if (!entry || entry.resetTime < now) {
    entry = {
      count: 0,
      resetTime: now + config.windowMs,
    };
    store.set(key, entry);
  }

  // Increment count
  entry.count += 1;

  const remaining = Math.max(0, config.maxRequests - entry.count);
  const limited = entry.count > config.maxRequests;

  return { limited, remaining, resetTime: entry.resetTime };
}

/**
 * Rate limiting middleware
 */
export function withRateLimit(
  handler: NextApiHandler,
  config: Partial<RateLimitConfig> = {}
): NextApiHandler {
  const mergedConfig: RateLimitConfig = { ...DEFAULT_CONFIG, ...config };
  const keyGenerator = mergedConfig.keyGenerator || defaultKeyGenerator;

  return async (req: NextApiRequest, res: NextApiResponse) => {
    const key = keyGenerator(req);
    const { limited, remaining, resetTime } = checkRateLimit(key, mergedConfig);

    // Set rate limit headers
    res.setHeader('X-RateLimit-Limit', mergedConfig.maxRequests);
    res.setHeader('X-RateLimit-Remaining', remaining);
    res.setHeader('X-RateLimit-Reset', Math.ceil(resetTime / 1000));

    if (limited) {
      logger.warn('Rate limit exceeded', {
        ip: key,
        path: req.url,
        method: req.method,
      });

      res.setHeader('Retry-After', Math.ceil((resetTime - Date.now()) / 1000));
      return res.status(429).json({
        error: 'Too Many Requests',
        message: mergedConfig.message,
        retryAfter: Math.ceil((resetTime - Date.now()) / 1000),
      });
    }

    return handler(req, res);
  };
}

/**
 * Create a rate limiter with custom settings
 */
export function createRateLimiter(config: Partial<RateLimitConfig>) {
  return (handler: NextApiHandler) => withRateLimit(handler, config);
}

/**
 * Stricter rate limiter for authentication endpoints
 */
export const authRateLimiter = createRateLimiter({
  windowMs: 15 * 60 * 1000, // 15 minutes
  maxRequests: 5,           // 5 attempts per window
  message: 'Too many authentication attempts. Please try again in 15 minutes.',
});

/**
 * API rate limiter for general endpoints
 */
export const apiRateLimiter = createRateLimiter({
  windowMs: 60 * 1000,      // 1 minute
  maxRequests: 60,          // 60 requests per minute
});

// Export types
export type { RateLimitConfig, RateLimitEntry };
