/**
 * 🚦 VIGTHORIA REQUEST QUEUE & RATE LIMITING SYSTEM
 * Advanced request management for optimal performance and resource utilization
 */

const EventEmitter = require('events');

class RequestQueueSystem extends EventEmitter {
    constructor() {
        super();
        
        // Queue Management
        this.queues = new Map(); // Different queues for different request types
        this.activeRequests = new Map();
        this.completedRequests = new Map();
        
        // Rate Limiting
        this.rateLimiters = new Map();
        this.globalRateLimit = new RateLimiter({ maxRequests: 100, windowMs: 60000 });
        
        // Performance Monitoring
        this.metrics = {
            totalRequests: 0,
            completedRequests: 0,
            failedRequests: 0,
            averageWaitTime: 0,
            averageProcessingTime: 0,
            queueSizes: {},
            throughput: 0
        };
        
        this.initializeQueues();
        this.startMetricsCollection();
    }

    initializeQueues() {
        // AI Request Queue (High Priority)
        this.createQueue('ai_requests', {
            maxConcurrent: 5,
            maxSize: 50,
            priority: 1,
            timeout: 30000,
            retryAttempts: 3,
            retryDelay: 2000
        });

        // File Operation Queue (Medium Priority)
        this.createQueue('file_operations', {
            maxConcurrent: 10,
            maxSize: 100,
            priority: 2,
            timeout: 15000,
            retryAttempts: 2,
            retryDelay: 1000
        });

        // Sub-Agent Queue (Medium Priority)
        this.createQueue('sub_agents', {
            maxConcurrent: 8,
            maxSize: 20,
            priority: 2,
            timeout: 60000,
            retryAttempts: 1,
            retryDelay: 5000
        });

        // Background Tasks Queue (Low Priority)
        this.createQueue('background_tasks', {
            maxConcurrent: 3,
            maxSize: 200,
            priority: 3,
            timeout: 120000,
            retryAttempts: 5,
            retryDelay: 10000
        });

        // Health Check Queue (Highest Priority)
        this.createQueue('health_checks', {
            maxConcurrent: 2,
            maxSize: 10,
            priority: 0,
            timeout: 5000,
            retryAttempts: 1,
            retryDelay: 1000
        });

        console.log(`🚦 Request queue system initialized with ${this.queues.size} queues`);
    }

    createQueue(queueName, config) {
        const queue = {
            name: queueName,
            config: config,
            requests: [],
            processing: new Set(),
            paused: false,
            created: Date.now()
        };

        this.queues.set(queueName, queue);
        
        // Create rate limiter for this queue
        this.rateLimiters.set(queueName, new RateLimiter({
            maxRequests: config.maxConcurrent * 10,
            windowMs: 60000,
            queueName: queueName
        }));

        // Start processing for this queue
        this.startQueueProcessor(queueName);
    }

    async enqueueRequest(queueName, requestData, options = {}) {
        const queue = this.queues.get(queueName);
        if (!queue) {
            throw new Error(`Queue '${queueName}' not found`);
        }

        // Check global rate limit
        if (!this.globalRateLimit.checkLimit('global')) {
            throw new Error('Global rate limit exceeded. Please try again later.');
        }

        // Check queue-specific rate limit
        const rateLimiter = this.rateLimiters.get(queueName);
        if (!rateLimiter.checkLimit(options.userId || 'anonymous')) {
            throw new Error(`Rate limit exceeded for queue '${queueName}'. Please try again later.`);
        }

        // Check queue size limit
        if (queue.requests.length >= queue.config.maxSize) {
            throw new Error(`Queue '${queueName}' is full. Please try again later.`);
        }

        const request = {
            id: this.generateRequestId(),
            queueName: queueName,
            data: requestData,
            options: options,
            priority: options.priority || queue.config.priority,
            attempts: 0,
            maxAttempts: queue.config.retryAttempts + 1,
            createdAt: Date.now(),
            enqueuedAt: Date.now(),
            timeout: options.timeout || queue.config.timeout,
            resolve: null,
            reject: null,
            promise: null
        };

        // Create promise for the request
        request.promise = new Promise((resolve, reject) => {
            request.resolve = resolve;
            request.reject = reject;
        });

        // Add to queue (maintain priority order)
        this.insertByPriority(queue, request);

        this.metrics.totalRequests++;
        this.updateQueueMetrics();

        this.emit('requestEnqueued', {
            requestId: request.id,
            queueName: queueName,
            queueSize: queue.requests.length
        });

        console.log(`📝 Request ${request.id} enqueued to ${queueName} (Queue size: ${queue.requests.length})`);

        return request.promise;
    }

    insertByPriority(queue, request) {
        // Insert request maintaining priority order (lower priority number = higher priority)
        let inserted = false;
        for (let i = 0; i < queue.requests.length; i++) {
            if (request.priority < queue.requests[i].priority) {
                queue.requests.splice(i, 0, request);
                inserted = true;
                break;
            }
        }
        
        if (!inserted) {
            queue.requests.push(request);
        }
    }

    startQueueProcessor(queueName) {
        const processQueue = async () => {
            const queue = this.queues.get(queueName);
            if (!queue || queue.paused) {
                setTimeout(processQueue, 100);
                return;
            }

            // Check if we can process more requests
            if (queue.processing.size >= queue.config.maxConcurrent) {
                setTimeout(processQueue, 100);
                return;
            }

            // Get next request
            const request = queue.requests.shift();
            if (!request) {
                setTimeout(processQueue, 100);
                return;
            }

            // Add to processing set
            queue.processing.add(request.id);
            this.activeRequests.set(request.id, request);

            // Process the request
            this.processRequest(request).finally(() => {
                queue.processing.delete(request.id);
                this.activeRequests.delete(request.id);
                
                // Continue processing
                setImmediate(processQueue);
            });

            // Continue processing immediately if there's capacity
            if (queue.processing.size < queue.config.maxConcurrent && queue.requests.length > 0) {
                setImmediate(processQueue);
            } else {
                setTimeout(processQueue, 10);
            }
        };

        // Start the processor
        processQueue();
    }

    async processRequest(request) {
        const startTime = Date.now();
        request.startedAt = startTime;
        request.attempts++;

        console.log(`⚡ Processing request ${request.id} (Attempt ${request.attempts}/${request.maxAttempts})`);

        this.emit('requestStarted', {
            requestId: request.id,
            queueName: request.queueName,
            attempt: request.attempts
        });

        try {
            // Set up timeout
            const timeoutPromise = new Promise((_, reject) => {
                setTimeout(() => {
                    reject(new Error(`Request timeout after ${request.timeout}ms`));
                }, request.timeout);
            });

            // Process the actual request
            const resultPromise = this.executeRequest(request);

            // Race between processing and timeout
            const result = await Promise.race([resultPromise, timeoutPromise]);

            // Success
            const processingTime = Date.now() - startTime;
            const waitTime = startTime - request.enqueuedAt;

            this.recordSuccess(request, result, processingTime, waitTime);
            request.resolve(result);

            console.log(`✅ Request ${request.id} completed in ${processingTime}ms (waited ${waitTime}ms)`);

        } catch (error) {
            console.error(`❌ Request ${request.id} failed (Attempt ${request.attempts}):`, error.message);

            // Check if we should retry
            if (request.attempts < request.maxAttempts && this.shouldRetry(error)) {
                console.log(`🔄 Retrying request ${request.id} in ${this.getRetryDelay(request)}ms`);
                
                // Re-enqueue for retry after delay
                setTimeout(() => {
                    const queue = this.queues.get(request.queueName);
                    if (queue) {
                        this.insertByPriority(queue, request);
                    }
                }, this.getRetryDelay(request));

                return;
            }

            // Failed permanently
            const processingTime = Date.now() - startTime;
            const waitTime = startTime - request.enqueuedAt;

            this.recordFailure(request, error, processingTime, waitTime);
            request.reject(error);
        }
    }

    async executeRequest(request) {
        // Route request to appropriate handler based on queue type
        const handlers = {
            'ai_requests': this.handleAIRequest.bind(this),
            'file_operations': this.handleFileOperation.bind(this),
            'sub_agents': this.handleSubAgentRequest.bind(this),
            'background_tasks': this.handleBackgroundTask.bind(this),
            'health_checks': this.handleHealthCheck.bind(this)
        };

        const handler = handlers[request.queueName];
        if (!handler) {
            throw new Error(`No handler found for queue: ${request.queueName}`);
        }

        return await handler(request);
    }

    async handleAIRequest(request) {
        const { action, data } = request.data;
        
        switch (action) {
            case 'chat':
                return await this.processAIChat(data);
            case 'generate_code':
                return await this.processCodeGeneration(data);
            case 'analyze_code':
                return await this.processCodeAnalysis(data);
            default:
                throw new Error(`Unknown AI request action: ${action}`);
        }
    }

    async handleFileOperation(request) {
        const { operation, path, content } = request.data;
        
        switch (operation) {
            case 'read':
                return await this.readFile(path);
            case 'write':
                return await this.writeFile(path, content);
            case 'delete':
                return await this.deleteFile(path);
            case 'list':
                return await this.listDirectory(path);
            default:
                throw new Error(`Unknown file operation: ${operation}`);
        }
    }

    async handleSubAgentRequest(request) {
        const { agentType, task, context } = request.data;
        
        // Delegate to sub-agent system
        return await this.createAndExecuteSubAgent(agentType, task, context);
    }

    async handleBackgroundTask(request) {
        const { taskType, data } = request.data;
        
        switch (taskType) {
            case 'cleanup':
                return await this.performCleanup(data);
            case 'analytics':
                return await this.processAnalytics(data);
            case 'maintenance':
                return await this.performMaintenance(data);
            default:
                throw new Error(`Unknown background task: ${taskType}`);
        }
    }

    async handleHealthCheck(request) {
        const { service, endpoint } = request.data;
        
        return await this.performHealthCheck(service, endpoint);
    }

    // Placeholder implementations for specific handlers
    async processAIChat(data) {
        // This would integrate with the actual AI orchestrator
        await new Promise(resolve => setTimeout(resolve, Math.random() * 2000 + 1000));
        return { success: true, response: "AI response", model: data.model };
    }

    async processCodeGeneration(data) {
        await new Promise(resolve => setTimeout(resolve, Math.random() * 3000 + 2000));
        return { success: true, code: "generated code", language: data.language };
    }

    async processCodeAnalysis(data) {
        await new Promise(resolve => setTimeout(resolve, Math.random() * 1500 + 500));
        return { success: true, analysis: "code analysis results" };
    }

    async readFile(path) {
        const fs = require('fs').promises;
        return await fs.readFile(path, 'utf8');
    }

    async writeFile(path, content) {
        const fs = require('fs').promises;
        await fs.writeFile(path, content);
        return { success: true, path: path };
    }

    async deleteFile(path) {
        const fs = require('fs').promises;
        await fs.unlink(path);
        return { success: true, path: path };
    }

    async listDirectory(path) {
        const fs = require('fs').promises;
        const files = await fs.readdir(path);
        return { success: true, files: files };
    }

    async createAndExecuteSubAgent(agentType, task, context) {
        // Placeholder for sub-agent creation and execution
        await new Promise(resolve => setTimeout(resolve, Math.random() * 5000 + 3000));
        return { success: true, agentId: 'agent_' + Date.now(), result: "task completed" };
    }

    async performCleanup(data) {
        await new Promise(resolve => setTimeout(resolve, 1000));
        return { success: true, cleaned: data.items };
    }

    async processAnalytics(data) {
        await new Promise(resolve => setTimeout(resolve, 2000));
        return { success: true, processed: data.events };
    }

    async performMaintenance(data) {
        await new Promise(resolve => setTimeout(resolve, 3000));
        return { success: true, maintained: data.components };
    }

    async performHealthCheck(service, endpoint) {
        // This would integrate with the health check system
        await new Promise(resolve => setTimeout(resolve, 500));
        return { success: true, status: 'healthy', service: service };
    }

    shouldRetry(error) {
        // Define retry logic based on error type
        const retryableErrors = [
            'ECONNRESET',
            'ETIMEDOUT',
            'ENOTFOUND',
            'Request timeout',
            'Service temporarily unavailable'
        ];

        return retryableErrors.some(retryableError => 
            error.message.includes(retryableError)
        );
    }

    getRetryDelay(request) {
        const queue = this.queues.get(request.queueName);
        const baseDelay = queue.config.retryDelay;
        
        // Exponential backoff
        return baseDelay * Math.pow(2, request.attempts - 1);
    }

    recordSuccess(request, result, processingTime, waitTime) {
        this.metrics.completedRequests++;
        this.updateAverageMetrics(processingTime, waitTime);
        
        this.completedRequests.set(request.id, {
            request: request,
            result: result,
            success: true,
            processingTime: processingTime,
            waitTime: waitTime,
            completedAt: Date.now()
        });

        this.emit('requestCompleted', {
            requestId: request.id,
            queueName: request.queueName,
            success: true,
            processingTime: processingTime,
            waitTime: waitTime
        });

        // Cleanup old completed requests (keep last 1000)
        if (this.completedRequests.size > 1000) {
            const oldestKeys = Array.from(this.completedRequests.keys()).slice(0, 100);
            oldestKeys.forEach(key => this.completedRequests.delete(key));
        }
    }

    recordFailure(request, error, processingTime, waitTime) {
        this.metrics.failedRequests++;
        this.updateAverageMetrics(processingTime, waitTime);
        
        this.completedRequests.set(request.id, {
            request: request,
            error: error,
            success: false,
            processingTime: processingTime,
            waitTime: waitTime,
            completedAt: Date.now()
        });

        this.emit('requestFailed', {
            requestId: request.id,
            queueName: request.queueName,
            error: error.message,
            processingTime: processingTime,
            waitTime: waitTime,
            attempts: request.attempts
        });
    }

    updateAverageMetrics(processingTime, waitTime) {
        const totalCompleted = this.metrics.completedRequests + this.metrics.failedRequests;
        
        this.metrics.averageProcessingTime = 
            (this.metrics.averageProcessingTime * (totalCompleted - 1) + processingTime) / totalCompleted;
        
        this.metrics.averageWaitTime = 
            (this.metrics.averageWaitTime * (totalCompleted - 1) + waitTime) / totalCompleted;
    }

    updateQueueMetrics() {
        for (const [queueName, queue] of this.queues.entries()) {
            this.metrics.queueSizes[queueName] = {
                waiting: queue.requests.length,
                processing: queue.processing.size,
                total: queue.requests.length + queue.processing.size
            };
        }
    }

    startMetricsCollection() {
        setInterval(() => {
            this.updateQueueMetrics();
            this.calculateThroughput();
        }, 5000);
    }

    calculateThroughput() {
        const now = Date.now();
        const fiveMinutesAgo = now - (5 * 60 * 1000);
        
        let recentCompletions = 0;
        for (const completion of this.completedRequests.values()) {
            if (completion.completedAt > fiveMinutesAgo) {
                recentCompletions++;
            }
        }
        
        // Requests per minute
        this.metrics.throughput = (recentCompletions / 5);
    }

    generateRequestId() {
        return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    }

    // Queue Management Methods
    pauseQueue(queueName) {
        const queue = this.queues.get(queueName);
        if (queue) {
            queue.paused = true;
            console.log(`⏸️ Queue '${queueName}' paused`);
        }
    }

    resumeQueue(queueName) {
        const queue = this.queues.get(queueName);
        if (queue) {
            queue.paused = false;
            console.log(`▶️ Queue '${queueName}' resumed`);
        }
    }

    clearQueue(queueName) {
        const queue = this.queues.get(queueName);
        if (queue) {
            const clearedCount = queue.requests.length;
            queue.requests.forEach(request => {
                request.reject(new Error('Queue cleared'));
            });
            queue.requests = [];
            console.log(`🗑️ Cleared ${clearedCount} requests from queue '${queueName}'`);
        }
    }

    getQueueStatus(queueName) {
        const queue = this.queues.get(queueName);
        if (!queue) return null;

        return {
            name: queueName,
            waiting: queue.requests.length,
            processing: queue.processing.size,
            paused: queue.paused,
            config: queue.config,
            oldestWaiting: queue.requests[0]?.enqueuedAt || null
        };
    }

    getAllQueueStatuses() {
        const statuses = {};
        for (const queueName of this.queues.keys()) {
            statuses[queueName] = this.getQueueStatus(queueName);
        }
        return statuses;
    }

    getMetrics() {
        return {
            ...this.metrics,
            queues: this.getAllQueueStatuses(),
            activeRequests: this.activeRequests.size,
            rateLimits: this.getRateLimitStatus()
        };
    }

    getRateLimitStatus() {
        const status = {};
        status.global = this.globalRateLimit.getStatus();
        
        for (const [queueName, rateLimiter] of this.rateLimiters.entries()) {
            status[queueName] = rateLimiter.getStatus();
        }
        
        return status;
    }
}

/**
 * 🚧 RATE LIMITER IMPLEMENTATION
 * Token bucket algorithm for smooth rate limiting
 */
class RateLimiter {
    constructor(options) {
        this.maxRequests = options.maxRequests || 60;
        this.windowMs = options.windowMs || 60000;
        this.queueName = options.queueName || 'global';
        
        this.requests = new Map(); // userId -> request times
        this.cleanupInterval = setInterval(() => this.cleanup(), this.windowMs);
    }

    checkLimit(userId) {
        const now = Date.now();
        const userRequests = this.requests.get(userId) || [];
        
        // Remove old requests outside the window
        const validRequests = userRequests.filter(time => (now - time) < this.windowMs);
        
        if (validRequests.length >= this.maxRequests) {
            return false;
        }
        
        // Add current request
        validRequests.push(now);
        this.requests.set(userId, validRequests);
        
        return true;
    }

    cleanup() {
        const now = Date.now();
        for (const [userId, requests] of this.requests.entries()) {
            const validRequests = requests.filter(time => (now - time) < this.windowMs);
            if (validRequests.length === 0) {
                this.requests.delete(userId);
            } else {
                this.requests.set(userId, validRequests);
            }
        }
    }

    getStatus() {
        return {
            maxRequests: this.maxRequests,
            windowMs: this.windowMs,
            activeUsers: this.requests.size,
            queueName: this.queueName
        };
    }

    destroy() {
        if (this.cleanupInterval) {
            clearInterval(this.cleanupInterval);
        }
    }
}

module.exports = { RequestQueueSystem, RateLimiter };
