/**
 * Rate Limiting Middleware
 * Implements token bucket algorithm for API rate limiting
 */
/**
 * Simple in-memory rate limiter using token bucket algorithm
 */
export class RateLimiter {
    buckets = new Map();
    config;
    constructor(config = {}) {
        this.config = {
            requestsPerMinute: parseInt(process.env.RATE_LIMIT_REQUESTS_PER_MINUTE || '100'),
            windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS || '60000'),
            skipSuccessfulRequests: false,
            skipFailedRequests: false,
            ...config,
        };
        // Cleanup old buckets every 5 minutes
        setInterval(() => this.cleanup(), 5 * 60 * 1000);
    }
    /**
     * Check if a request should be allowed
     */
    checkLimit(key) {
        const now = Date.now();
        let bucket = this.buckets.get(key);
        // Create new bucket if doesn't exist
        if (!bucket) {
            bucket = {
                tokens: this.config.requestsPerMinute,
                lastRefill: now,
            };
            this.buckets.set(key, bucket);
        }
        // Refill tokens based on time elapsed
        const timeSinceLastRefill = now - bucket.lastRefill;
        const refillRate = this.config.requestsPerMinute / this.config.windowMs;
        const tokensToAdd = timeSinceLastRefill * refillRate;
        bucket.tokens = Math.min(this.config.requestsPerMinute, bucket.tokens + tokensToAdd);
        bucket.lastRefill = now;
        // Check if request can be allowed
        const hasTokens = bucket.tokens >= 1;
        if (hasTokens) {
            bucket.tokens -= 1;
        }
        const remaining = Math.floor(bucket.tokens);
        const reset = Math.ceil((this.config.requestsPerMinute - bucket.tokens) / refillRate + now);
        const retryAfter = hasTokens ? undefined : Math.ceil((1 - bucket.tokens) / refillRate / 1000);
        return {
            limit: this.config.requestsPerMinute,
            remaining: Math.max(0, remaining),
            reset,
            retryAfter,
        };
    }
    /**
     * Consume a token for a request
     */
    async consume(key) {
        const info = this.checkLimit(key);
        return info.remaining >= 0;
    }
    /**
     * Get rate limit info without consuming a token
     */
    getInfo(key) {
        const bucket = this.buckets.get(key);
        if (!bucket) {
            return {
                limit: this.config.requestsPerMinute,
                remaining: this.config.requestsPerMinute,
                reset: Date.now() + this.config.windowMs,
            };
        }
        return this.checkLimit(key);
    }
    /**
     * Reset rate limit for a key
     */
    reset(key) {
        this.buckets.delete(key);
    }
    /**
     * Cleanup old buckets
     */
    cleanup() {
        const now = Date.now();
        const maxAge = this.config.windowMs * 2;
        for (const [key, bucket] of this.buckets.entries()) {
            if (now - bucket.lastRefill > maxAge) {
                this.buckets.delete(key);
            }
        }
    }
    /**
     * Get current bucket count (for monitoring)
     */
    getBucketCount() {
        return this.buckets.size;
    }
}
/**
 * Create rate limit headers for HTTP response
 */
export function createRateLimitHeaders(info) {
    const headers = {
        'X-RateLimit-Limit': info.limit.toString(),
        'X-RateLimit-Remaining': info.remaining.toString(),
        'X-RateLimit-Reset': info.reset.toString(),
    };
    if (info.retryAfter !== undefined) {
        headers['Retry-After'] = info.retryAfter.toString();
    }
    return headers;
}
/**
 * Default rate limiter instance
 */
export const defaultRateLimiter = new RateLimiter();
//# sourceMappingURL=rate-limiter.js.map