import { Request, Response, NextFunction } from 'express';

/**
 * Simple in-memory rate limiter
 * Tracks requests per API key
 */
class RateLimiter {
  private requests: Map<string, { count: number; resetAt: number }> = new Map();
  private readonly limit: number;
  private readonly windowMs: number;

  constructor(limit: number = 100, windowMs: number = 60 * 60 * 1000) {
    this.limit = limit;
    this.windowMs = windowMs;

    // Cleanup old entries every 5 minutes
    setInterval(() => this.cleanup(), 5 * 60 * 1000);
  }

  check(keyId: string): { allowed: boolean; remaining: number; resetAt: number } {
    const now = Date.now();
    const record = this.requests.get(keyId);

    if (!record || record.resetAt < now) {
      // Create new window
      const resetAt = now + this.windowMs;
      this.requests.set(keyId, { count: 1, resetAt });
      return { allowed: true, remaining: this.limit - 1, resetAt };
    }

    if (record.count >= this.limit) {
      return { allowed: false, remaining: 0, resetAt: record.resetAt };
    }

    // Increment counter
    record.count++;
    return {
      allowed: true,
      remaining: this.limit - record.count,
      resetAt: record.resetAt,
    };
  }

  private cleanup() {
    const now = Date.now();
    for (const [keyId, record] of this.requests.entries()) {
      if (record.resetAt < now) {
        this.requests.delete(keyId);
      }
    }
  }
}

const rateLimiter = new RateLimiter(100, 60 * 60 * 1000); // 100 requests per hour

/**
 * Rate limiting middleware
 * Must be used AFTER validateApiKey middleware
 */
export function rateLimitByApiKey(req: Request, res: Response, next: NextFunction): void {
  if (!req.apiKey) {
    next();
    return;
  }

  const result = rateLimiter.check(req.apiKey.id);

  // Add rate limit headers
  res.setHeader('X-RateLimit-Limit', '100');
  res.setHeader('X-RateLimit-Remaining', result.remaining.toString());
  res.setHeader('X-RateLimit-Reset', new Date(result.resetAt).toISOString());

  if (!result.allowed) {
    const retryAfter = Math.ceil((result.resetAt - Date.now()) / 1000);

    console.warn(
      `[${new Date().toISOString()}] Rate limit exceeded: ${req.apiKey.id} (${req.apiKey.keyType}) - reset: ${new Date(result.resetAt).toISOString()}`,
    );

    res
      .status(429)
      .setHeader('Retry-After', retryAfter.toString())
      .json({
        error: 'Rate limit exceeded',
        message: `Too many requests. Retry after ${retryAfter} seconds`,
        retryAfter,
      });
    return;
  }

  next();
}
