/**
 * In-memory Rate Limiter for MCP Tool Calls
 *
 * Uses a sliding window counter to enforce per-category rate limits.
 * Prevents DoS from malfunctioning or malicious clients flooding
 * the server with concurrent requests.
 *
 * Categories:
 *   - search:      50 calls/window  (queries)
 *   - ingestion:   10 calls/window  (heavy GPU work)
 *   - destructive:  5 calls/window  (deletes)
 *   - default:    100 calls/window  (everything else)
 *
 * Configurable via environment variables:
 *   - OCR_RATE_LIMIT_ENABLED   (default: "true")
 *   - OCR_RATE_LIMIT_WINDOW_MS (default: 60000)
 *
 * CRITICAL: NEVER use console.log() — stdout is JSON-RPC protocol.
 *
 * @module services/rate-limiter
 */
/** Default limits per category (calls per window) */
const CATEGORY_LIMITS = {
    search: { maxCalls: 50 },
    ingestion: { maxCalls: 10 },
    destructive: { maxCalls: 5 },
    default: { maxCalls: 100 },
};
/** Tool name -> category mapping */
const TOOL_CATEGORY_MAP = {
    // Search tools
    ocr_search: 'search',
    ocr_search_cross_db: 'search',
    ocr_rag_context: 'search',
    // Ingestion tools (heavy GPU operations)
    ocr_ingest_files: 'ingestion',
    ocr_ingest_directory: 'ingestion',
    ocr_process_pending: 'ingestion',
    api_upload: 'ingestion',
    // Destructive tools
    ocr_db_delete: 'destructive',
    ocr_document_delete: 'destructive',
    ocr_tag_delete: 'destructive',
};
/** In-memory sliding window counters keyed by category */
const windowCounters = new Map();
/**
 * Get the rate limit window duration in milliseconds.
 * Reads from OCR_RATE_LIMIT_WINDOW_MS env var, defaults to 60000 (1 minute).
 */
function getWindowMs() {
    const envVal = process.env.OCR_RATE_LIMIT_WINDOW_MS;
    if (envVal) {
        const parsed = parseInt(envVal, 10);
        if (!isNaN(parsed) && parsed > 0) {
            return parsed;
        }
    }
    return 60_000;
}
/**
 * Check whether rate limiting is enabled.
 * Reads from OCR_RATE_LIMIT_ENABLED env var, defaults to true.
 */
function isRateLimitEnabled() {
    const envVal = process.env.OCR_RATE_LIMIT_ENABLED;
    if (envVal === undefined || envVal === '')
        return true;
    return envVal.toLowerCase() !== 'false' && envVal !== '0';
}
/**
 * Resolve the rate-limit category for a given tool name.
 */
export function getToolCategory(toolName) {
    return TOOL_CATEGORY_MAP[toolName] ?? 'default';
}
/**
 * Check rate limit for a given category.
 * Throws an Error immediately if the limit is exceeded (fail-fast).
 *
 * @param category - The rate limit category to check
 * @throws Error when the rate limit for the category is exceeded
 */
export function checkRateLimit(category) {
    if (!isRateLimitEnabled())
        return;
    const limit = CATEGORY_LIMITS[category] ?? CATEGORY_LIMITS['default'];
    const now = Date.now();
    const windowMs = getWindowMs();
    let state = windowCounters.get(category);
    // Reset window if expired or first call
    if (!state || now - state.windowStart >= windowMs) {
        state = { count: 0, windowStart: now };
        windowCounters.set(category, state);
    }
    // Check before incrementing — fail fast
    if (state.count >= limit.maxCalls) {
        const windowSec = Math.round(windowMs / 1000);
        throw new Error(`Rate limit exceeded for ${category} operations. ` +
            `Maximum ${limit.maxCalls} calls per ${windowSec}s window. ` +
            `Please wait and try again.`);
    }
    state.count++;
}
//# sourceMappingURL=rate-limiter.js.map