import { Request, Response, NextFunction } from 'express';
import { RateLimiterMemory, RateLimiterRedis } from 'rate-limiter-flexible';
import Redis from 'ioredis';

/**
 * Rate limit configuration
 */
export interface RateLimitConfig {
  windowMs: number;  // Time window in milliseconds
  max: number;       // Maximum requests per window
  message?: string;  // Error message
  skipSuccessfulRequests?: boolean;
  skipFailedRequests?: boolean;
}

/**
 * Get or create rate limiter instance
 */
function getRateLimiter(config: RateLimitConfig): RateLimiterMemory | RateLimiterRedis {
  const points = config.max;
  const duration = Math.ceil(config.windowMs / 1000); // Convert to seconds

  // Try to use Redis if available
  if (process.env.REDIS_URL) {
    try {
      const redis = new Redis(process.env.REDIS_URL);
      return new RateLimiterRedis({
        storeClient: redis,
        keyPrefix: 'rl:nlp:',
        points,
        duration,
        blockDuration: 10, // Block for 10 seconds after limit reached
      });
    } catch (error) {
      console.warn('Failed to create Redis rate limiter, falling back to memory:', error);
    }
  }

  // Fallback to in-memory rate limiter
  return new RateLimiterMemory({
    keyPrefix: 'rl:nlp:',
    points,
    duration,
    blockDuration: 10,
  });
}

/**
 * Rate limiting middleware factory
 */
export function rateLimitMiddleware(config: RateLimitConfig) {
  const rateLimiter = getRateLimiter(config);
  const message = config.message || 'Too many requests, please try again later';

  return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      // Use IP address as key, with fallback
      const key = req.ip || req.socket.remoteAddress || 'unknown';
      
      // Try to consume a point
      await rateLimiter.consume(key);
      
      next();
    } catch (rejRes) {
      // Rate limit exceeded
      res.status(429).json({
        success: false,
        error: message,
        retryAfter: Math.round((rejRes as any).msBeforeNext / 1000) || 60,
      });
    }
  };
}

/**
 * Create a rate limiter for specific endpoint groups
 */
export const createRateLimiter = {
  // Standard API rate limit (60 requests per minute)
  standard: () => rateLimitMiddleware({
    windowMs: 60 * 1000,
    max: 60,
    message: 'Too many requests, please try again later',
  }),

  // Strict rate limit for expensive operations (10 requests per minute)
  strict: () => rateLimitMiddleware({
    windowMs: 60 * 1000,
    max: 10,
    message: 'Too many requests to this resource, please try again later',
  }),

  // Relaxed rate limit for read operations (120 requests per minute)
  relaxed: () => rateLimitMiddleware({
    windowMs: 60 * 1000,
    max: 120,
    message: 'Too many requests, please slow down',
  }),

  // Custom rate limiter
  custom: (config: RateLimitConfig) => rateLimitMiddleware(config),
};

export default rateLimitMiddleware;