/**
 * Request timing and memory usage tracking
 * Provides comprehensive monitoring for HTTP requests, AI calls, and MCP operations
 */
import { randomBytes } from 'node:crypto';
import { MAX_COMPLETED_REQUESTS } from '../../constants.js';
import { generateCorrelationId, getLogger } from './index.js';
import { calculateMemoryDelta, formatBytes, trackPerformance, } from './performance.js';
// Get logger instance directly to avoid circular dependencies
const logger = getLogger();
/**
 * Request tracker class for monitoring HTTP requests and operations
 */
export class RequestTracker {
    activeRequests = new Map();
    completedRequests = [];
    maxCompletedRequests;
    correlationId;
    constructor(maxCompletedRequests = MAX_COMPLETED_REQUESTS) {
        this.maxCompletedRequests = maxCompletedRequests;
        this.correlationId = generateCorrelationId();
    }
    /**
     * Start tracking a new request
     */
    startRequest(metadata) {
        const id = this.generateRequestId();
        const request = {
            ...metadata,
            id,
            startTime: Date.now(),
            status: 'pending',
            correlationId: generateCorrelationId(),
            memoryStart: process.memoryUsage(),
        };
        this.activeRequests.set(id, request);
        logger.debug('Request tracking started', {
            requestId: id,
            requestType: metadata.type,
            method: metadata.method,
            endpoint: metadata.url || metadata.endpoint,
            correlationId: request.correlationId,
            source: 'request-tracker',
        });
        return id;
    }
    /**
     * Complete a request successfully
     */
    completeRequest(requestId, responseMetadata) {
        const request = this.activeRequests.get(requestId);
        if (!request) {
            logger.warn('Attempted to complete unknown request', {
                requestId,
                source: 'request-tracker',
            });
            return null;
        }
        const endTime = Date.now();
        const memoryEnd = process.memoryUsage();
        const duration = endTime - request.startTime;
        let memoryDelta;
        if (request.memoryStart) {
            try {
                memoryDelta = calculateMemoryDelta(request.memoryStart, memoryEnd);
            }
            catch {
                // Ignore memory calculation errors
                memoryDelta = undefined;
            }
        }
        const completedRequest = {
            ...request,
            endTime,
            duration,
            status: 'success',
            memoryEnd,
            memoryDelta,
            ...responseMetadata,
        };
        this.activeRequests.delete(requestId);
        this.addToCompleted(completedRequest);
        // Log successful request completion
        logger.info('Request completed successfully', {
            requestId: completedRequest.id,
            requestType: completedRequest.type,
            method: completedRequest.method,
            endpoint: completedRequest.url || completedRequest.endpoint,
            duration: `${duration}ms`,
            statusCode: completedRequest.statusCode,
            memoryDelta: memoryDelta
                ? {
                    heapUsed: formatBytes(memoryDelta.heapUsedDelta || 0),
                    heapTotal: formatBytes(memoryDelta.heapTotalDelta || 0),
                    external: formatBytes(memoryDelta.externalDelta || 0),
                    rss: formatBytes(memoryDelta.rssDelta || 0),
                }
                : undefined,
            correlationId: completedRequest.correlationId,
            source: 'request-tracker',
        });
        return completedRequest;
    }
    /**
     * Mark a request as failed
     */
    failRequest(requestId, error, errorMetadata) {
        const request = this.activeRequests.get(requestId);
        if (!request) {
            logger.warn('Attempted to fail unknown request', {
                requestId,
                source: 'request-tracker',
            });
            return null;
        }
        const endTime = Date.now();
        const memoryEnd = process.memoryUsage();
        const duration = endTime - request.startTime;
        let memoryDelta;
        if (request.memoryStart) {
            try {
                memoryDelta = calculateMemoryDelta(request.memoryStart, memoryEnd);
            }
            catch {
                // Ignore memory calculation errors
                memoryDelta = undefined;
            }
        }
        const errorMessage = error instanceof Error ? error.message : error;
        const errorType = error instanceof Error
            ? error.constructor.name
            : errorMetadata?.errorType || 'Unknown';
        const completedRequest = {
            ...request,
            endTime,
            duration,
            status: 'error',
            memoryEnd,
            memoryDelta,
            errorType,
            errorMessage,
            ...errorMetadata,
        };
        this.activeRequests.delete(requestId);
        this.addToCompleted(completedRequest);
        // Log request failure
        logger.error('Request failed', {
            requestId: completedRequest.id,
            requestType: completedRequest.type,
            method: completedRequest.method,
            endpoint: completedRequest.url || completedRequest.endpoint,
            duration: `${duration}ms`,
            errorType,
            errorMessage,
            statusCode: completedRequest.statusCode,
            memoryDelta: memoryDelta
                ? {
                    heapUsed: formatBytes(memoryDelta.heapUsedDelta || 0),
                    heapTotal: formatBytes(memoryDelta.heapTotalDelta || 0),
                    external: formatBytes(memoryDelta.externalDelta || 0),
                    rss: formatBytes(memoryDelta.rssDelta || 0),
                }
                : undefined,
            correlationId: completedRequest.correlationId,
            source: 'request-tracker',
        });
        return completedRequest;
    }
    /**
     * Mark a request as timed out
     */
    timeoutRequest(requestId, timeoutMs) {
        const request = this.activeRequests.get(requestId);
        if (!request) {
            logger.warn('Attempted to timeout unknown request', {
                requestId,
                source: 'request-tracker',
            });
            return null;
        }
        const endTime = Date.now();
        const memoryEnd = process.memoryUsage();
        const duration = endTime - request.startTime;
        let memoryDelta;
        if (request.memoryStart) {
            try {
                memoryDelta = calculateMemoryDelta(request.memoryStart, memoryEnd);
            }
            catch {
                // Ignore memory calculation errors
                memoryDelta = undefined;
            }
        }
        const completedRequest = {
            ...request,
            endTime,
            duration,
            status: 'timeout',
            memoryEnd,
            memoryDelta,
            errorMessage: `Request timed out after ${timeoutMs}ms (actual duration: ${duration}ms)`,
        };
        this.activeRequests.delete(requestId);
        this.addToCompleted(completedRequest);
        // Log request timeout
        logger.warn('Request timed out', {
            requestId: completedRequest.id,
            requestType: completedRequest.type,
            method: completedRequest.method,
            endpoint: completedRequest.url || completedRequest.endpoint,
            duration: `${duration}ms`,
            timeoutMs: `${timeoutMs}ms`,
            memoryDelta: memoryDelta
                ? {
                    heapUsed: formatBytes(memoryDelta.heapUsedDelta || 0),
                    heapTotal: formatBytes(memoryDelta.heapTotalDelta || 0),
                    external: formatBytes(memoryDelta.externalDelta || 0),
                    rss: formatBytes(memoryDelta.rssDelta || 0),
                }
                : undefined,
            correlationId: completedRequest.correlationId,
            source: 'request-tracker',
        });
        return completedRequest;
    }
    /**
     * Cancel a request
     */
    cancelRequest(requestId, reason) {
        const request = this.activeRequests.get(requestId);
        if (!request) {
            logger.warn('Attempted to cancel unknown request', {
                requestId,
                source: 'request-tracker',
            });
            return null;
        }
        const endTime = Date.now();
        const memoryEnd = process.memoryUsage();
        const duration = endTime - request.startTime;
        let memoryDelta;
        if (request.memoryStart) {
            try {
                memoryDelta = calculateMemoryDelta(request.memoryStart, memoryEnd);
            }
            catch {
                // Ignore memory calculation errors
                memoryDelta = undefined;
            }
        }
        const completedRequest = {
            ...request,
            endTime,
            duration,
            status: 'cancelled',
            memoryEnd,
            memoryDelta,
            errorMessage: reason || 'Request was cancelled',
        };
        this.activeRequests.delete(requestId);
        this.addToCompleted(completedRequest);
        // Log request cancellation
        logger.info('Request cancelled', {
            requestId: completedRequest.id,
            requestType: completedRequest.type,
            method: completedRequest.method,
            endpoint: completedRequest.url || completedRequest.endpoint,
            duration: `${duration}ms`,
            reason,
            memoryDelta: memoryDelta
                ? {
                    heapUsed: formatBytes(memoryDelta.heapUsedDelta || 0),
                    heapTotal: formatBytes(memoryDelta.heapTotalDelta || 0),
                    external: formatBytes(memoryDelta.externalDelta || 0),
                    rss: formatBytes(memoryDelta.rssDelta || 0),
                }
                : undefined,
            correlationId: completedRequest.correlationId,
            source: 'request-tracker',
        });
        return completedRequest;
    }
    /**
     * Get request statistics
     */
    getStats() {
        const now = Date.now();
        const oneHourAgo = now - 60 * 60 * 1000;
        const oneDayAgo = now - 24 * 60 * 60 * 1000;
        const requestsInLastHour = this.completedRequests.filter(r => r.endTime !== undefined && r.endTime > oneHourAgo).length;
        const requestsInLastDay = this.completedRequests.filter(r => r.endTime !== undefined && r.endTime > oneDayAgo).length;
        const requestsByType = {};
        const requestsByStatus = {};
        const endpointStats = {};
        let totalDuration = 0;
        let minDuration = Infinity;
        let maxDuration = 0;
        const memoryDeltaSum = { heapUsed: 0, heapTotal: 0, external: 0, rss: 0 };
        let memoryDeltaCount = 0;
        for (const request of this.completedRequests) {
            // Count by type
            requestsByType[request.type] = (requestsByType[request.type] || 0) + 1;
            // Count by status
            const status = request.status || 'unknown';
            requestsByStatus[status] = (requestsByStatus[status] || 0) + 1;
            // Duration statistics
            if (request.duration) {
                totalDuration += request.duration;
                minDuration = Math.min(minDuration, request.duration);
                maxDuration = Math.max(maxDuration, request.duration);
            }
            // Memory statistics
            if (request.memoryDelta) {
                memoryDeltaSum.heapUsed += request.memoryDelta.heapUsedDelta;
                memoryDeltaSum.heapTotal += request.memoryDelta.heapTotalDelta;
                memoryDeltaSum.external += request.memoryDelta.externalDelta;
                memoryDeltaSum.rss += request.memoryDelta.rssDelta;
                memoryDeltaCount++;
            }
            // Endpoint statistics
            const endpoint = request.url || request.endpoint || request.toolName || 'unknown';
            if (!endpointStats[endpoint]) {
                endpointStats[endpoint] = { count: 0, totalDuration: 0, errors: 0 };
            }
            endpointStats[endpoint].count++;
            if (request.duration) {
                endpointStats[endpoint].totalDuration += request.duration;
            }
            if (request.status === 'error') {
                endpointStats[endpoint].errors++;
            }
        }
        // Calculate average duration and memory delta
        const averageDuration = this.completedRequests.length > 0
            ? totalDuration / this.completedRequests.length
            : 0;
        const averageMemoryDelta = memoryDeltaCount > 0
            ? {
                heapUsed: memoryDeltaSum.heapUsed / memoryDeltaCount,
                heapTotal: memoryDeltaSum.heapTotal / memoryDeltaCount,
                external: memoryDeltaSum.external / memoryDeltaCount,
                rss: memoryDeltaSum.rss / memoryDeltaCount,
            }
            : { heapUsed: 0, heapTotal: 0, external: 0, rss: 0 };
        // Find busiest, slowest, and most error-prone endpoints
        let busiestEndpoint;
        let slowestEndpoint;
        let mostErrorProneEndpoint;
        let maxRequests = 0;
        let maxAvgDuration = 0;
        let maxErrorRate = 0;
        for (const [endpoint, stats] of Object.entries(endpointStats)) {
            if (stats.count > maxRequests) {
                maxRequests = stats.count;
                busiestEndpoint = endpoint;
            }
            const avgDuration = stats.count > 0 ? stats.totalDuration / stats.count : 0;
            if (avgDuration > maxAvgDuration) {
                maxAvgDuration = avgDuration;
                slowestEndpoint = endpoint;
            }
            const errorRate = stats.count > 0 ? stats.errors / stats.count : 0;
            if (errorRate > maxErrorRate) {
                maxErrorRate = errorRate;
                mostErrorProneEndpoint = endpoint;
            }
        }
        const totalRequests = this.completedRequests.length;
        const errorRate = requestsByStatus.error
            ? requestsByStatus.error / totalRequests
            : 0;
        const timeoutRate = requestsByStatus.timeout
            ? requestsByStatus.timeout / totalRequests
            : 0;
        return {
            totalRequests,
            requestsByType,
            requestsByStatus,
            averageDuration,
            minDuration: minDuration === Infinity ? 0 : minDuration,
            maxDuration,
            totalDuration,
            averageMemoryDelta,
            errorRate,
            timeoutRate,
            requestsInLastHour,
            requestsInLastDay,
            busiestHour: new Date().getHours(),
            busiestEndpoint,
            slowestEndpoint,
            mostErrorProneEndpoint,
            timestamp: new Date().toISOString(),
        };
    }
    /**
     * Get active requests
     */
    getActiveRequests() {
        return Array.from(this.activeRequests.values());
    }
    /**
     * Get recently completed requests
     */
    getRecentRequests(limit = 50) {
        return this.completedRequests
            .sort((a, b) => (b.endTime || 0) - (a.endTime || 0))
            .slice(0, limit);
    }
    /**
     * Clear all tracking data
     */
    clear() {
        this.activeRequests.clear();
        this.completedRequests = [];
    }
    generateRequestId() {
        return `req_${Date.now()}_${randomBytes(8).toString('hex')}`;
    }
    addToCompleted(request) {
        this.completedRequests.push(request);
        // Keep only the last N requests
        if (this.completedRequests.length > this.maxCompletedRequests) {
            this.completedRequests = this.completedRequests.slice(-this.maxCompletedRequests);
        }
    }
}
/**
 * Global request tracker instance
 */
export const globalRequestTracker = new RequestTracker();
/**
 * Decorator to automatically track function calls as requests
 */
export function trackRequest(fn, options) {
    return trackPerformance(async (...args) => {
        const requestId = globalRequestTracker.startRequest({
            type: options.type,
            method: options.method,
            endpoint: options.endpoint,
            provider: options.provider,
            model: options.model,
            tags: options.tags,
        });
        try {
            const result = await fn(...args);
            globalRequestTracker.completeRequest(requestId, {
                customData: {
                    arguments: options.trackRequestSize
                        ? {
                            count: args.length,
                            types: args.map(arg => typeof arg),
                            size: JSON.stringify(args).length,
                        }
                        : undefined,
                },
            });
            return result;
        }
        catch (error) {
            globalRequestTracker.failRequest(requestId, error, {
                errorType: error instanceof Error ? error.constructor.name : 'Unknown',
            });
            throw error;
        }
    }, `${options.type}-request-${options.endpoint || 'unknown'}`, {
        thresholds: options.thresholds,
        trackMemory: options.trackMemory !== false,
        trackArgs: false,
    });
}
/**
 * HTTP request tracking utilities
 */
export const httpTracker = {
    /**
     * Track an HTTP GET request
     */
    get: (url, fn, options) => trackRequest(fn, {
        type: 'http',
        method: 'GET',
        endpoint: url,
        ...options,
    }),
    /**
     * Track an HTTP POST request
     */
    post: (url, fn, options) => trackRequest(fn, {
        type: 'http',
        method: 'POST',
        endpoint: url,
        ...options,
    }),
    /**
     * Track an HTTP PUT request
     */
    put: (url, fn, options) => trackRequest(fn, {
        type: 'http',
        method: 'PUT',
        endpoint: url,
        ...options,
    }),
    /**
     * Track an HTTP DELETE request
     */
    delete: (url, fn, options) => trackRequest(fn, {
        type: 'http',
        method: 'DELETE',
        endpoint: url,
        ...options,
    }),
};
/**
 * AI request tracking utilities
 */
export const aiTracker = {
    /**
     * Track an AI chat request
     */
    chat: (provider, model, fn, options) => trackRequest(fn, {
        type: 'ai',
        provider,
        model,
        endpoint: 'chat',
        ...options,
    }),
    /**
     * Track an AI completion request
     */
    completion: (provider, model, fn, options) => trackRequest(fn, {
        type: 'ai',
        provider,
        model,
        endpoint: 'completion',
        ...options,
    }),
    /**
     * Track an AI embedding request
     */
    embedding: (provider, model, fn, options) => trackRequest(fn, {
        type: 'ai',
        provider,
        model,
        endpoint: 'embedding',
        ...options,
    }),
};
/**
 * MCP request tracking utilities
 */
export const mcpTracker = {
    /**
     * Track an MCP tool execution
     */
    tool: (_serverName, toolName, fn, options) => trackRequest(fn, {
        type: 'mcp',
        toolName,
        endpoint: `tool:${toolName}`,
        ...options,
    }),
    /**
     * Track an MCP server connection
     */
    connect: (_serverName, fn, options) => trackRequest(fn, {
        type: 'mcp',
        endpoint: 'connect',
        ...options,
    }),
};
// RequestTracker is only used internally
// export default RequestTracker;
//# sourceMappingURL=request-tracker.js.map