const rateLimit = require("express-rate-limit");
const logger = require("../utils/logger");

// General rate limiter for Maps API endpoints
const createMapsRateLimiter = (windowMs, max, message) => {
  return rateLimit({
    windowMs, // time window in milliseconds
    max, // limit each IP/user to max requests per windowMs
    message: {
      error: message,
      retryAfter: Math.ceil(windowMs / 1000), // seconds
    },
    standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
    legacyHeaders: false, // Disable the `X-RateLimit-*` headers
    // Use user ID if available, otherwise fall back to IPv6-safe IP handling
    keyGenerator: (req) => {
      if (req.user?.id) {
        return `user:${req.user.id}`;
      }
      // Use the built-in IP key generator which properly handles IPv6
      return rateLimit.defaultKeyGenerator(req);
    },
  });
};

// Specific rate limiters for different endpoints
const rateLimiters = {
  // Places Autocomplete - allow more requests since users type frequently
  placesAutocomplete: createMapsRateLimiter(
    60 * 1000, // 1 minute window
    30, // 30 requests per minute per user/IP
    "Too many autocomplete requests. Please slow down."
  ),

  // Place Details - moderate limit since each selection triggers this
  placeDetails: createMapsRateLimiter(
    60 * 1000, // 1 minute window
    20, // 20 requests per minute per user/IP
    "Too many place detail requests. Please slow down."
  ),

  // Geocoding - lower limit since this is typically used less frequently
  geocoding: createMapsRateLimiter(
    60 * 1000, // 1 minute window
    10, // 10 requests per minute per user/IP
    "Too many geocoding requests. Please slow down."
  ),
};

// Enhanced rate limiter with user-specific limits
const createUserBasedRateLimiter = (windowMs, max, message) => {
  const store = new Map(); // Simple in-memory store (use Redis in production)

  return (req, res, next) => {
    const key = req.user?.id
      ? `user:${req.user.id}`
      : rateLimit.defaultKeyGenerator(req);
    const now = Date.now();
    const windowStart = now - windowMs;

    // Clean up old entries
    for (const [k, data] of store.entries()) {
      if (data.windowStart < windowStart) {
        store.delete(k);
      }
    }

    // Get or create user's request data
    let userData = store.get(key);
    if (!userData || userData.windowStart < windowStart) {
      userData = {
        count: 0,
        windowStart: now,
        resetTime: now + windowMs,
      };
    }

    // Check if limit exceeded
    if (userData.count >= max) {
      return res.status(429).json({
        error: message,
        retryAfter: Math.ceil((userData.resetTime - now) / 1000),
      });
    }

    // Increment counter and store
    userData.count++;
    store.set(key, userData);

    // Add headers
    res.set({
      "RateLimit-Limit": max,
      "RateLimit-Remaining": Math.max(0, max - userData.count),
      "RateLimit-Reset": new Date(userData.resetTime).toISOString(),
    });

    next();
  };
};

// Burst protection for expensive operations
const burstProtection = createUserBasedRateLimiter(
  10 * 1000, // 10 seconds
  5, // 5 requests per 10 seconds
  "Too many requests in a short period. Please slow down."
);

// Upload presign rate limiter - 30 requests per minute
const uploadPresignLimiter = createUserBasedRateLimiter(
  60 * 1000, // 1 minute window
  30, // 30 presign requests per minute per user
  "Too many upload requests. Please slow down."
);

// Helper to create a rate limit handler that logs the event
const createRateLimitHandler = (limiterName) => (req, res, next, options) => {
  const reqLogger = logger.withRequestId(req.id);
  reqLogger.warn('Rate limit exceeded', {
    limiter: limiterName,
    ip: req.ip,
    userId: req.user?.id || 'anonymous',
    method: req.method,
    url: req.url,
    userAgent: req.get('User-Agent'),
    message: options.message?.error || 'Rate limit exceeded'
  });
  res.status(options.statusCode).json(options.message);
};

// Authentication rate limiters
const authRateLimiters = {
  // Login rate limiter - stricter to prevent brute force
  login: rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 5, // 5 login attempts per 15 minutes
    message: {
      error: "Too many login attempts. Please try again in 15 minutes.",
      retryAfter: 900, // seconds
    },
    standardHeaders: true,
    legacyHeaders: false,
    skipSuccessfulRequests: true, // Don't count successful logins
    handler: createRateLimitHandler('login'),
  }),

  // Registration rate limiter
  register: rateLimit({
    windowMs: 60 * 60 * 1000, // 1 hour
    max: 3, // 3 registration attempts per hour
    message: {
      error: "Too many registration attempts. Please try again later.",
      retryAfter: 3600,
    },
    standardHeaders: true,
    legacyHeaders: false,
    handler: createRateLimitHandler('register'),
  }),

  // Password reset rate limiter
  passwordReset: rateLimit({
    windowMs: 60 * 60 * 1000, // 1 hour
    max: 3, // 3 password reset requests per hour
    message: {
      error: "Too many password reset requests. Please try again later.",
      retryAfter: 3600,
    },
    standardHeaders: true,
    legacyHeaders: false,
    handler: createRateLimitHandler('passwordReset'),
  }),

  // Alpha code validation rate limiter
  alphaCodeValidation: rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 5, // 5 code validation attempts per 15 minutes
    message: {
      error: "Too many attempts. Please try again later.",
      retryAfter: 900,
    },
    standardHeaders: true,
    legacyHeaders: false,
    handler: createRateLimitHandler('alphaCodeValidation'),
  }),

  // Email verification rate limiter - protect against brute force on 6-digit codes
  emailVerification: rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 10, // 10 verification attempts per 15 minutes per IP
    message: {
      error: "Too many verification attempts. Please try again later.",
      retryAfter: 900,
    },
    standardHeaders: true,
    legacyHeaders: false,
    handler: createRateLimitHandler('emailVerification'),
  }),

  // General API rate limiter
  general: rateLimit({
    windowMs: 60 * 1000, // 1 minute
    max: 100, // 100 requests per minute
    message: {
      error: "Too many requests. Please slow down.",
      retryAfter: 60,
    },
    standardHeaders: true,
    legacyHeaders: false,
    handler: createRateLimitHandler('general'),
  }),
};

module.exports = {
  // Individual rate limiters
  placesAutocomplete: rateLimiters.placesAutocomplete,
  placeDetails: rateLimiters.placeDetails,
  geocoding: rateLimiters.geocoding,

  // Auth rate limiters
  loginLimiter: authRateLimiters.login,
  registerLimiter: authRateLimiters.register,
  passwordResetLimiter: authRateLimiters.passwordReset,
  alphaCodeValidationLimiter: authRateLimiters.alphaCodeValidation,
  emailVerificationLimiter: authRateLimiters.emailVerification,
  generalLimiter: authRateLimiters.general,

  // Burst protection
  burstProtection,

  // Upload rate limiter
  uploadPresignLimiter,

  // Utility functions
  createMapsRateLimiter,
  createUserBasedRateLimiter,
};
