/**
 * Rate Limiter Middleware
 *
 * Provides centralized rate limiting for API endpoints using a configurable
 * memory store with optional Redis support for horizontal scaling.
 *
 * Usage:
 *   const { authLimiter, apiLimiter, uploadLimiter } = require('./middlewares/rateLimiter');
 *   app.use('/api/auth', authLimiter);
 *   app.use('/api', apiLimiter);
 */

const { logger } = require('../utils/logger');

// In-memory store for rate limiting
// For horizontal scaling, replace with Redis store
const rateLimitStore = new Map();

// Cleanup interval for expired entries (every 5 minutes)
const CLEANUP_INTERVAL_MS = 5 * 60 * 1000;

// Periodic cleanup of expired entries
setInterval(() => {
  const now = Date.now();
  let cleaned = 0;

  for (const [key, entry] of rateLimitStore.entries()) {
    if (entry.expiresAt <= now) {
      rateLimitStore.delete(key);
      cleaned++;
    }
  }

  if (cleaned > 0) {
    logger.debug({ cleaned }, 'Rate limit store cleanup');
  }
}, CLEANUP_INTERVAL_MS);

/**
 * Create a rate limiter middleware
 *
 * @param {Object} options - Configuration options
 * @param {string} options.keyPrefix - Prefix for rate limit keys (e.g., 'auth', 'api')
 * @param {number} options.windowMs - Time window in milliseconds (default: 15 minutes)
 * @param {number} options.max - Maximum requests per window (default: 100)
 * @param {string} [options.message] - Custom error message
 * @param {boolean} [options.skipFailedRequests] - Don't count failed requests (status >= 400)
 * @param {Function} [options.keyGenerator] - Custom key generator (req) => string
 * @param {Function} [options.skip] - Skip rate limiting for certain requests (req) => boolean
 * @returns {Function} Express middleware
 */
const createRateLimiter = (options = {}) => {
  const {
    keyPrefix = 'rate-limit',
    windowMs = 15 * 60 * 1000, // 15 minutes
    max = 100,
    message = 'Too many requests. Please try again later.',
    skipFailedRequests = false,
    keyGenerator = null,
    skip = null,
  } = options;

  return (req, res, next) => {
    // Allow skipping rate limiting for certain requests
    if (skip && skip(req)) {
      return next();
    }

    // Skip in development when accessing from localhost (optional)
    if (
      process.env.NODE_ENV === 'development' &&
      (req.ip === '127.0.0.1' || req.ip === '::1')
    ) {
      return next();
    }

    // Generate rate limit key
    const clientKey = keyGenerator
      ? keyGenerator(req)
      : req.ip || req.connection?.remoteAddress || 'unknown';
    const key = `${keyPrefix}:${clientKey}`;
    const now = Date.now();

    // Get or create rate limit entry
    let entry = rateLimitStore.get(key);

    if (!entry || entry.expiresAt <= now) {
      // Create new entry
      entry = {
        count: 0,
        expiresAt: now + windowMs,
        resetTime: new Date(now + windowMs).toISOString(),
      };
    }

    // Add standard rate limit headers
    const remaining = Math.max(0, max - entry.count - 1);
    const retryAfter = Math.ceil((entry.expiresAt - now) / 1000);

    res.setHeader('X-RateLimit-Limit', max);
    res.setHeader('X-RateLimit-Remaining', remaining);
    res.setHeader('X-RateLimit-Reset', entry.resetTime);

    // Check if rate limit exceeded
    if (entry.count >= max) {
      res.setHeader('Retry-After', retryAfter);

      logger.warn(
        {
          ip: clientKey,
          keyPrefix,
          count: entry.count,
          max,
          retryAfter,
        },
        'Rate limit exceeded',
      );

      return res.status(429).json({
        error: 'Too Many Requests',
        message,
        retryAfter,
      });
    }

    // Increment count
    entry.count += 1;
    rateLimitStore.set(key, entry);

    // If skipFailedRequests is enabled, decrement on failed response
    if (skipFailedRequests) {
      const originalSend = res.send.bind(res);
      res.send = function (body) {
        if (res.statusCode >= 400) {
          const currentEntry = rateLimitStore.get(key);
          if (currentEntry && currentEntry.count > 0) {
            currentEntry.count -= 1;
            rateLimitStore.set(key, currentEntry);
          }
        }
        return originalSend(body);
      };
    }

    next();
  };
};

/**
 * Create a rate limiter that uses both IP and user ID as key
 * Useful for authenticated endpoints
 */
const createAuthenticatedRateLimiter = (options = {}) => {
  return createRateLimiter({
    ...options,
    keyGenerator: (req) => {
      const userId = req.currentUser?.id || 'anonymous';
      const ip = req.ip || 'unknown';
      return `${ip}:${userId}`;
    },
  });
};

// Pre-configured limiters for common use cases

/**
 * Auth limiter - Strict limits for authentication endpoints
 * 5 requests per 15 minutes per IP
 */
const authLimiter = createRateLimiter({
  keyPrefix: 'auth',
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 10, // Allow 10 attempts per window
  message: 'Too many authentication attempts. Please try again later.',
  skipFailedRequests: false, // Count failed attempts
});

/**
 * Signup limiter - Very strict limits for registration
 * 5 signups per hour per IP
 */
const signupLimiter = createRateLimiter({
  keyPrefix: 'signup',
  windowMs: 60 * 60 * 1000, // 1 hour
  max: 5,
  message: 'Too many signup attempts. Please try again later.',
});

/**
 * Password reset limiter - Prevent password reset abuse
 * 5 requests per hour per IP
 */
const passwordResetLimiter = createRateLimiter({
  keyPrefix: 'password-reset',
  windowMs: 60 * 60 * 1000, // 1 hour
  max: 5,
  message: 'Too many password reset requests. Please try again later.',
});

/**
 * General API limiter - Standard limits for API endpoints
 * 100 requests per minute per IP
 */
const apiLimiter = createRateLimiter({
  keyPrefix: 'api',
  windowMs: 60 * 1000, // 1 minute
  max: 100,
  message: 'Too many requests. Please slow down.',
  skipFailedRequests: true, // Don't penalize for errors
});

/**
 * Upload limiter - Limits for file uploads
 * 200 uploads per minute per IP (supports batch uploads of 100+ files)
 */
const uploadLimiter = createRateLimiter({
  keyPrefix: 'upload',
  windowMs: 60 * 1000, // 1 minute
  max: 200,
  message: 'Too many file uploads. Please wait before uploading more.',
  skipFailedRequests: true, // Don't penalize for failed uploads
});

/**
 * Download limiter - More permissive limits for file downloads
 * 200 requests per minute per IP (supports asset preloading)
 */
const downloadLimiter = createRateLimiter({
  keyPrefix: 'download',
  windowMs: 60 * 1000, // 1 minute
  max: 200,
  message: 'Too many download requests. Please slow down.',
  skipFailedRequests: true, // Don't penalize for errors
});

/**
 * Search limiter - Prevent search abuse
 * 30 searches per minute per IP
 */
const searchLimiter = createRateLimiter({
  keyPrefix: 'search',
  windowMs: 60 * 1000, // 1 minute
  max: 30,
  message: 'Too many search requests. Please slow down.',
});

/**
 * AI/OpenAI limiter - Strict limits for expensive AI operations
 * 20 requests per minute per IP
 */
const aiLimiter = createRateLimiter({
  keyPrefix: 'ai',
  windowMs: 60 * 1000, // 1 minute
  max: 20,
  message: 'Too many AI requests. Please wait before making more.',
});

module.exports = {
  createRateLimiter,
  createAuthenticatedRateLimiter,
  authLimiter,
  signupLimiter,
  passwordResetLimiter,
  apiLimiter,
  uploadLimiter,
  downloadLimiter,
  searchLimiter,
  aiLimiter,
};
