const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const redis = require('redis');

// Redis client setup (optional - for distributed systems)
let redisClient;
try {
  if (process.env.REDIS_URL) {
    redisClient = redis.createClient({
      url: process.env.REDIS_URL,
      socket: {
        reconnectStrategy: (retries) => Math.min(retries * 50, 500)
      }
    });

    redisClient.on('error', (err) => {
      console.error('Redis Client Error:', err);
    });

    redisClient.connect();
  }
} catch (error) {
  console.error('Redis connection error:', error);
}

/**
 * Standard rate limiter factory
 * @param {number} maxRequests - Maximum number of requests
 * @param {number} windowSeconds - Time window in seconds
 * @param {string} message - Custom error message
 */
exports.rateLimiter = (maxRequests = 100, windowSeconds = 60, message = null) => {
  const limiterConfig = {
    windowMs: windowSeconds * 1000,
    max: maxRequests,
    message: message || {
      success: false,
      message: `Too many requests, please try again after ${windowSeconds} seconds`
    },
    standardHeaders: true,
    legacyHeaders: false,
    // Use IP address as key
    keyGenerator: (req) => {
      return req.ip || req.connection.remoteAddress;
    },
    // Custom handler for rate limit exceeded
    handler: (req, res) => {
      res.status(429).json({
        success: false,
        message: `Too many requests from this IP, please try again after ${windowSeconds} seconds`,
        retryAfter: windowSeconds
      });
    },
    // Skip successful requests from counting (optional)
    skipSuccessfulRequests: false,
    // Skip failed requests from counting (optional)
    skipFailedRequests: false
  };

  // Use Redis store if available (for distributed systems)
  if (redisClient && redisClient.isOpen) {
    limiterConfig.store = new RedisStore({
      client: redisClient,
      prefix: 'rate_limit:'
    });
  }

  return rateLimit(limiterConfig);
};

/**
 * Strict rate limiter for authentication routes
 */
exports.authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // 5 requests per window
  message: {
    success: false,
    message: 'Too many login attempts, please try again after 15 minutes'
  },
  standardHeaders: true,
  legacyHeaders: false,
  skipSuccessfulRequests: true, // Don't count successful requests
  keyGenerator: (req) => {
    // Use email or IP for authentication attempts
    return req.body.email || req.ip;
  }
});

/**
 * API rate limiter - more generous limits
 */
exports.apiLimiter = rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 60, // 60 requests per minute
  message: {
    success: false,
    message: 'API rate limit exceeded, please slow down'
  },
  standardHeaders: true,
  legacyHeaders: false
});

/**
 * Create order rate limiter - prevent spam orders
 */
exports.createOrderLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 10, // 10 orders per 15 minutes
  message: {
    success: false,
    message: 'Too many orders created, please wait before creating another order'
  },
  standardHeaders: true,
  legacyHeaders: false,
  keyGenerator: (req) => {
    // Use user ID if authenticated, otherwise IP
    return req.user ? `user_${req.user.id}` : req.ip;
  }
});

/**
 * Search rate limiter
 */
exports.searchLimiter = rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 30, // 30 searches per minute
  message: {
    success: false,
    message: 'Too many search requests, please slow down'
  },
  standardHeaders: true,
  legacyHeaders: false
});

/**
 * Export rate limiter - prevent abuse of export functionality
 */
exports.exportLimiter = rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 5, // 5 exports per minute
  message: {
    success: false,
    message: 'Too many export requests, please wait before exporting again'
  },
  standardHeaders: true,
  legacyHeaders: false,
  keyGenerator: (req) => {
    return req.user ? `user_${req.user.id}` : req.ip;
  }
});

/**
 * File upload rate limiter
 */
exports.uploadLimiter = rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 10, // 10 uploads per minute
  message: {
    success: false,
    message: 'Too many file uploads, please wait before uploading again'
  },
  standardHeaders: true,
  legacyHeaders: false
});

/**
 * Password reset rate limiter
 */
exports.passwordResetLimiter = rateLimit({
  windowMs: 60 * 60 * 1000, // 1 hour
  max: 3, // 3 attempts per hour
  message: {
    success: false,
    message: 'Too many password reset attempts, please try again after 1 hour'
  },
  standardHeaders: true,
  legacyHeaders: false,
  keyGenerator: (req) => {
    return req.body.email || req.ip;
  }
});

/**
 * Email verification rate limiter
 */
exports.verificationLimiter = rateLimit({
  windowMs: 60 * 60 * 1000, // 1 hour
  max: 5, // 5 verification emails per hour
  message: {
    success: false,
    message: 'Too many verification requests, please try again after 1 hour'
  },
  standardHeaders: true,
  legacyHeaders: false,
  keyGenerator: (req) => {
    return req.body.email || req.ip;
  }
});

/**
 * Review submission rate limiter
 */
exports.reviewLimiter = rateLimit({
  windowMs: 24 * 60 * 60 * 1000, // 24 hours
  max: 10, // 10 reviews per day
  message: {
    success: false,
    message: 'Too many reviews submitted today, please try again tomorrow'
  },
  standardHeaders: true,
  legacyHeaders: false,
  keyGenerator: (req) => {
    return req.user ? `user_${req.user.id}` : req.ip;
  }
});

/**
 * Dynamic rate limiter based on user role
 */
exports.dynamicRateLimiter = (limits = {}) => {
  return (req, res, next) => {
    const defaultLimits = {
      admin: { max: 1000, window: 60 },
      manager: { max: 500, window: 60 },
      customer: { max: 100, window: 60 },
      guest: { max: 50, window: 60 }
    };

    const mergedLimits = { ...defaultLimits, ...limits };
    const userRole = req.user ? req.user.role : 'guest';
    const limit = mergedLimits[userRole] || mergedLimits.guest;

    const limiter = rateLimit({
      windowMs: limit.window * 1000,
      max: limit.max,
      message: {
        success: false,
        message: `Rate limit exceeded for ${userRole} role`
      },
      standardHeaders: true,
      legacyHeaders: false,
      keyGenerator: (req) => {
        return req.user ? `user_${req.user.id}_${userRole}` : `guest_${req.ip}`;
      }
    });

    return limiter(req, res, next);
  };
};

/**
 * Endpoint-specific rate limiter
 */
exports.endpointLimiter = (endpoint, maxRequests = 100, windowSeconds = 60) => {
  return rateLimit({
    windowMs: windowSeconds * 1000,
    max: maxRequests,
    message: {
      success: false,
      message: `Too many requests to ${endpoint}, please try again later`
    },
    standardHeaders: true,
    legacyHeaders: false,
    keyGenerator: (req) => {
      const userId = req.user ? req.user.id : 'guest';
      return `${userId}_${endpoint}`;
    }
  });
};

/**
 * Burst protection - allows short bursts but enforces overall limit
 */
exports.burstProtection = (burstMax = 20, sustainedMax = 100, windowSeconds = 60) => {
  const burstLimiter = rateLimit({
    windowMs: 10 * 1000, // 10 second window for burst
    max: burstMax,
    skipSuccessfulRequests: false,
    skipFailedRequests: false
  });

  const sustainedLimiter = rateLimit({
    windowMs: windowSeconds * 1000,
    max: sustainedMax,
    skipSuccessfulRequests: false,
    skipFailedRequests: false
  });

  return (req, res, next) => {
    burstLimiter(req, res, (err) => {
      if (err) return next(err);
      sustainedLimiter(req, res, next);
    });
  };
};

/**
 * IP whitelist bypass
 */
exports.whitelistIPs = (whitelist = []) => {
  return (req, res, next) => {
    const clientIP = req.ip || req.connection.remoteAddress;
    
    if (whitelist.includes(clientIP)) {
      return next();
    }

    // If not whitelisted, apply rate limiting
    return exports.apiLimiter(req, res, next);
  };
};

/**
 * Cleanup function for graceful shutdown
 */
exports.cleanup = async () => {
  if (redisClient && redisClient.isOpen) {
    await redisClient.quit();
    console.log('Redis client disconnected');
  }
};