// Simple in-memory rate limiter for IP and email-based requests
// For production with multiple instances, consider using Redis

type RateLimitEntry = {
  count: number;
  resetAt: number; // Unix timestamp in ms
};

const rateLimitStore = new Map<string, RateLimitEntry>();

// Cleanup old entries every 10 minutes to prevent memory leaks
setInterval(() => {
  const now = Date.now();
  for (const [key, entry] of rateLimitStore.entries()) {
    if (entry.resetAt < now) {
      rateLimitStore.delete(key);
    }
  }
}, 10 * 60 * 1000);

export type RateLimitConfig = {
  maxRequests: number;
  windowMs: number; // Time window in milliseconds
};

export type RateLimitResult = {
  allowed: boolean;
  remaining: number;
  resetAt: number;
  retryAfterSeconds?: number;
};

/**
 * Check if a request is allowed based on rate limiting
 * @param key - Unique identifier (IP, email, or combination)
 * @param config - Rate limit configuration
 * @returns RateLimitResult with allow status and metadata
 */
export function checkRateLimit(
  key: string,
  config: RateLimitConfig
): RateLimitResult {
  const now = Date.now();
  const entry = rateLimitStore.get(key);

  // No existing entry or window expired - allow and create new entry
  if (!entry || entry.resetAt < now) {
    rateLimitStore.set(key, {
      count: 1,
      resetAt: now + config.windowMs,
    });

    return {
      allowed: true,
      remaining: config.maxRequests - 1,
      resetAt: now + config.windowMs,
    };
  }

  // Existing entry within window
  if (entry.count >= config.maxRequests) {
    // Rate limit exceeded
    const retryAfterSeconds = Math.ceil((entry.resetAt - now) / 1000);
    return {
      allowed: false,
      remaining: 0,
      resetAt: entry.resetAt,
      retryAfterSeconds,
    };
  }

  // Increment count and allow
  entry.count++;
  rateLimitStore.set(key, entry);

  return {
    allowed: true,
    remaining: config.maxRequests - entry.count,
    resetAt: entry.resetAt,
  };
}

/**
 * Check rate limit for booking creation
 * Applies multiple checks: per IP and per email
 */
export function checkBookingRateLimit(params: {
  ip?: string;
  email: string;
}): RateLimitResult {
  const { ip, email } = params;

  // Config: max 3 bookings per email per hour
  const emailConfig: RateLimitConfig = {
    maxRequests: 3,
    windowMs: 60 * 60 * 1000, // 1 hour
  };

  // Config: max 5 bookings per IP per 10 minutes
  const ipConfig: RateLimitConfig = {
    maxRequests: 5,
    windowMs: 10 * 60 * 1000, // 10 minutes
  };

  // Check email rate limit
  const emailKey = `booking:email:${email.toLowerCase()}`;
  const emailResult = checkRateLimit(emailKey, emailConfig);

  if (!emailResult.allowed) {
    return {
      ...emailResult,
      allowed: false,
    };
  }

  // Check IP rate limit (if IP is available)
  if (ip) {
    const ipKey = `booking:ip:${ip}`;
    const ipResult = checkRateLimit(ipKey, ipConfig);

    if (!ipResult.allowed) {
      return {
        ...ipResult,
        allowed: false,
      };
    }
  }

  // Both checks passed
  return {
    allowed: true,
    remaining: Math.min(emailResult.remaining, ip ? Infinity : emailResult.remaining),
    resetAt: emailResult.resetAt,
  };
}

/**
 * Get client IP from various headers (for proxy/load balancer support)
 */
export function getClientIP(headers: Record<string, string | undefined>): string | undefined {
  // Check common proxy headers
  const forwardedFor = headers['x-forwarded-for'];
  if (forwardedFor) {
    // x-forwarded-for can contain multiple IPs, take the first one
    return forwardedFor.split(',')[0].trim();
  }

  const realIP = headers['x-real-ip'];
  if (realIP) {
    return realIP;
  }

  const cfConnectingIP = headers['cf-connecting-ip']; // Cloudflare
  if (cfConnectingIP) {
    return cfConnectingIP;
  }

  // No IP found
  return undefined;
}


