/**
 * Rate limiting for expensive operations.
 * Uses token bucket algorithm to prevent API abuse.
 * Ported from Python rate_limiter.py
 */

import { config } from '../config.js';

// ============================================================================
// Types
// ============================================================================

export interface RateLimitStatus {
  allowed: boolean;
  remaining: number;
  resetInSeconds: number;
}

// ============================================================================
// Rate Limiter Class
// ============================================================================

/**
 * Token bucket rate limiter.
 *
 * Usage:
 *   const limiter = new RateLimiter(60, 3600);
 *   if (limiter.isAllowed('user_123')) {
 *     // Allow request
 *   } else {
 *     // Reject (rate limited)
 *   }
 */
export class RateLimiter {
  private maxRequests: number;
  private windowMs: number;
  private requests: Map<string, number[]>;

  /**
   * Initialize rate limiter.
   *
   * @param maxRequests - Maximum requests allowed in window
   * @param windowSeconds - Time window in seconds
   */
  constructor(maxRequests: number, windowSeconds: number) {
    this.maxRequests = maxRequests;
    this.windowMs = windowSeconds * 1000;
    this.requests = new Map();
  }

  /**
   * Check if request is allowed.
   */
  isAllowed(identifier: string): boolean {
    const now = Date.now();
    const cutoff = now - this.windowMs;

    // Get or initialize requests for identifier
    let timestamps = this.requests.get(identifier) || [];

    // Clean old requests
    timestamps = timestamps.filter((time) => time > cutoff);

    // Check limit
    if (timestamps.length >= this.maxRequests) {
      console.log(`Rate limit exceeded for ${identifier}`);
      return false;
    }

    // Record request
    timestamps.push(now);
    this.requests.set(identifier, timestamps);
    return true;
  }

  /**
   * Get remaining requests in current window.
   */
  getRemaining(identifier: string): number {
    const now = Date.now();
    const cutoff = now - this.windowMs;

    // Get and clean requests
    let timestamps = this.requests.get(identifier) || [];
    timestamps = timestamps.filter((time) => time > cutoff);
    this.requests.set(identifier, timestamps);

    return Math.max(0, this.maxRequests - timestamps.length);
  }

  /**
   * Get full rate limit status.
   */
  getStatus(identifier: string): RateLimitStatus {
    const now = Date.now();
    const cutoff = now - this.windowMs;

    // Get and clean requests
    let timestamps = this.requests.get(identifier) || [];
    timestamps = timestamps.filter((time) => time > cutoff);
    this.requests.set(identifier, timestamps);

    const remaining = Math.max(0, this.maxRequests - timestamps.length);
    const allowed = remaining > 0;

    // Calculate reset time
    let resetInSeconds = 0;
    if (timestamps.length > 0 && !allowed) {
      const oldestTimestamp = Math.min(...timestamps);
      resetInSeconds = Math.ceil((oldestTimestamp + this.windowMs - now) / 1000);
    }

    return {
      allowed,
      remaining,
      resetInSeconds,
    };
  }

  /**
   * Reset rate limit for identifier.
   */
  reset(identifier: string): void {
    this.requests.delete(identifier);
  }

  /**
   * Clear all rate limit data.
   */
  clearAll(): void {
    this.requests.clear();
  }

  /**
   * Cleanup old entries to prevent memory leaks.
   * Call this periodically (e.g., every minute).
   */
  cleanup(): void {
    const now = Date.now();
    const cutoff = now - this.windowMs;

    for (const [identifier, timestamps] of this.requests.entries()) {
      const filtered = timestamps.filter((time) => time > cutoff);
      if (filtered.length === 0) {
        this.requests.delete(identifier);
      } else {
        this.requests.set(identifier, filtered);
      }
    }
  }
}

// ============================================================================
// Global Rate Limiters
// ============================================================================

export const openaiLimiter = new RateLimiter(
  config.OPENAI_MAX_REQUESTS_PER_HOUR || 60,
  3600 // 1 hour
);

export const websocketLimiter = new RateLimiter(
  config.WEBSOCKET_MAX_MESSAGES_PER_MINUTE || 30,
  60 // 1 minute
);

// ============================================================================
// Helper Functions
// ============================================================================

/**
 * Check if user can make request of a given type.
 */
export function checkRateLimit(userId: string, type: 'openai' | 'websocket'): boolean {
  if (type === 'openai') {
    return checkOpenAIRateLimit(userId);
  } else {
    return checkWebSocketRateLimit(userId);
  }
}

/**
 * Check if user can make OpenAI request.
 */
export function checkOpenAIRateLimit(userId: string): boolean {
  if (!openaiLimiter.isAllowed(userId)) {
    const remaining = openaiLimiter.getRemaining(userId);
    console.log(`OpenAI rate limit exceeded for ${userId}. Remaining: ${remaining}`);
    return false;
  }
  return true;
}

/**
 * Check if user can send WebSocket message.
 */
export function checkWebSocketRateLimit(userId: string): boolean {
  if (!websocketLimiter.isAllowed(userId)) {
    console.log(`WebSocket rate limit exceeded for ${userId}`);
    return false;
  }
  return true;
}

/**
 * Get OpenAI rate limit status for user.
 */
export function getOpenAIRateLimitStatus(userId: string): RateLimitStatus {
  return openaiLimiter.getStatus(userId);
}

/**
 * Get WebSocket rate limit status for user.
 */
export function getWebSocketRateLimitStatus(userId: string): RateLimitStatus {
  return websocketLimiter.getStatus(userId);
}

// ============================================================================
// Cleanup Timer
// ============================================================================

// Cleanup old entries every minute
setInterval(() => {
  openaiLimiter.cleanup();
  websocketLimiter.cleanup();
}, 60_000);

// ============================================================================
// Export
// ============================================================================

export const rateLimiter = {
  RateLimiter,
  openaiLimiter,
  websocketLimiter,
  checkOpenAIRateLimit,
  checkWebSocketRateLimit,
  getOpenAIRateLimitStatus,
  getWebSocketRateLimitStatus,
};
