#!/usr/bin/env node
import { EventEmitter } from 'events';
import { execSync } from 'child_process';
import os from 'os';
import { LoggerService } from '../services/core/logger.service.js';
/**
 * ResourceMonitor provides comprehensive monitoring of system resources
 * to detect potential issues before they cause crashes.
 *
 * Features:
 * - Memory usage monitoring with leak detection
 * - CPU usage tracking
 * - Event loop lag measurement
 * - File descriptor monitoring
 * - System health metrics
 * - Predictive alerting
 *
 * @example
 * ```typescript
 * const monitor = new ResourceMonitor({
 *   memoryMB: 500,
 *   cpuPercent: 80,
 *   eventLoopLagMs: 100,
 *   openFiles: 1000
 * });
 *
 * monitor.on('warning', (metrics) => {
 *   // Handle resource warning
 * });
 *
 * monitor.start();
 * ```
 */
export class ResourceMonitor extends EventEmitter {
    thresholds;
    monitoringInterval = null;
    eventLoopStartTime = 0;
    lastCpuUsage = process.cpuUsage();
    lastCpuCheck = Date.now();
    memoryHistory = [];
    cpuHistory = [];
    isMonitoring = false;
    logger;
    DEFAULT_THRESHOLDS = {
        memoryMB: 500, // 500MB memory warning
        cpuPercent: 80, // 80% CPU warning
        eventLoopLagMs: 100, // 100ms event loop lag warning
        openFiles: 1000 // 1000 open file descriptors warning
    };
    constructor(thresholds) {
        super();
        this.logger = LoggerService.getInstance().createComponentLogger('ResourceMonitor');
        this.thresholds = { ...this.DEFAULT_THRESHOLDS, ...thresholds };
        this.logger.info('Resource Monitor initialized', { thresholds: this.thresholds });
    }
    /**
     * Start resource monitoring
     *
     * @param intervalMs - Monitoring interval in milliseconds (default: 30000)
     */
    start(intervalMs = 30000) {
        if (this.isMonitoring) {
            this.logger.warn('Resource monitoring is already running');
            return;
        }
        this.logger.info('Starting resource monitoring', { intervalMs });
        this.isMonitoring = true;
        // Initial measurement
        this.measureResources();
        // Set up monitoring interval
        this.monitoringInterval = setInterval(() => {
            this.measureResources();
        }, intervalMs);
        // Set up event loop lag monitoring
        this.startEventLoopMonitoring();
    }
    /**
     * Stop resource monitoring
     */
    stop() {
        if (!this.isMonitoring) {
            return;
        }
        this.logger.info('Stopping resource monitoring');
        this.isMonitoring = false;
        if (this.monitoringInterval) {
            clearInterval(this.monitoringInterval);
            this.monitoringInterval = null;
        }
    }
    /**
     * Get current resource metrics
     *
     * @returns Current resource usage metrics
     */
    async getCurrentMetrics() {
        const memoryUsage = process.memoryUsage();
        const cpuUsage = this.calculateCpuUsage();
        const systemInfo = this.getSystemInfo();
        return {
            memoryUsageMB: Math.round(memoryUsage.heapUsed / 1024 / 1024),
            cpuUsagePercent: cpuUsage,
            eventLoopLagMs: await this.measureEventLoopLag(),
            openFiles: await this.getOpenFileCount(),
            uptimeSeconds: os.uptime(),
            freeMemoryPercent: (os.freemem() / os.totalmem()) * 100,
            loadAverage: os.loadavg(),
            processUptimeSeconds: process.uptime()
        };
    }
    /**
     * Measure and analyze current resource usage
     */
    async measureResources() {
        try {
            const metrics = await this.getCurrentMetrics();
            // Update history
            this.memoryHistory.push(metrics.memoryUsageMB);
            this.cpuHistory.push(metrics.cpuUsagePercent);
            // Keep only last 10 measurements
            if (this.memoryHistory.length > 10) {
                this.memoryHistory.shift();
            }
            if (this.cpuHistory.length > 10) {
                this.cpuHistory.shift();
            }
            // Log current metrics
            this.logMetrics(metrics);
            // Check thresholds and emit warnings
            this.checkThresholds(metrics);
            // Analyze trends
            this.analyzeTrends(metrics);
            // Emit metrics event
            this.emit('metrics', metrics);
        }
        catch (error) {
            this.logger.error('Error measuring resources', { error: error instanceof Error ? error.message : String(error) });
            this.emit('error', error);
        }
    }
    /**
     * Log current metrics to console
     *
     * @param metrics - Current resource metrics
     */
    logMetrics(metrics) {
        this.logger.debug('Resource Metrics', {
            memoryMB: metrics.memoryUsageMB,
            cpuPercent: parseFloat(metrics.cpuUsagePercent.toFixed(1)),
            eventLoopLagMs: parseFloat(metrics.eventLoopLagMs.toFixed(1)),
            openFiles: metrics.openFiles,
            freeMemoryPercent: parseFloat(metrics.freeMemoryPercent.toFixed(1)),
            loadAverage: metrics.loadAverage.map(l => parseFloat(l.toFixed(2))),
        });
    }
    /**
     * Check if metrics exceed thresholds and emit warnings
     *
     * @param metrics - Current resource metrics
     */
    checkThresholds(metrics) {
        const warnings = [];
        if (metrics.memoryUsageMB > this.thresholds.memoryMB) {
            warnings.push(`High memory usage: ${metrics.memoryUsageMB}MB > ${this.thresholds.memoryMB}MB`);
        }
        if (metrics.cpuUsagePercent > this.thresholds.cpuPercent) {
            warnings.push(`High CPU usage: ${metrics.cpuUsagePercent.toFixed(1)}% > ${this.thresholds.cpuPercent}%`);
        }
        if (metrics.eventLoopLagMs > this.thresholds.eventLoopLagMs) {
            warnings.push(`High event loop lag: ${metrics.eventLoopLagMs.toFixed(1)}ms > ${this.thresholds.eventLoopLagMs}ms`);
        }
        if (metrics.openFiles > this.thresholds.openFiles) {
            warnings.push(`High open file count: ${metrics.openFiles} > ${this.thresholds.openFiles}`);
        }
        if (metrics.freeMemoryPercent < 10) {
            warnings.push(`Low system memory: ${metrics.freeMemoryPercent.toFixed(1)}% free`);
        }
        if (warnings.length > 0) {
            this.logger.warn('Resource warnings detected', { warnings });
            this.emit('warning', {
                warnings,
                metrics,
                timestamp: new Date().toISOString()
            });
        }
    }
    /**
     * Analyze resource usage trends
     *
     * @param metrics - Current resource metrics
     */
    analyzeTrends(metrics) {
        // Memory leak detection
        if (this.memoryHistory.length >= 5) {
            const avgGrowth = this.calculateAverageGrowth(this.memoryHistory);
            if (avgGrowth > 10) { // Growing by more than 10MB per measurement
                this.logger.warn('Potential memory leak detected', {
                    averageGrowthMB: parseFloat(avgGrowth.toFixed(1)),
                    currentMemoryMB: metrics.memoryUsageMB,
                });
                this.emit('memory_leak_warning', {
                    averageGrowth: avgGrowth,
                    currentMemory: metrics.memoryUsageMB,
                    history: this.memoryHistory
                });
            }
        }
        // CPU spike detection
        if (this.cpuHistory.length >= 3) {
            const recentCpu = this.cpuHistory.slice(-3);
            const avgCpu = recentCpu.reduce((a, b) => a + b, 0) / recentCpu.length;
            if (avgCpu > this.thresholds.cpuPercent) {
                this.logger.warn('Sustained high CPU usage', {
                    averageCpuPercent: parseFloat(avgCpu.toFixed(1)),
                    measurements: 3,
                });
                this.emit('cpu_spike_warning', {
                    averageCpu: avgCpu,
                    recentReadings: recentCpu
                });
            }
        }
    }
    /**
     * Calculate average growth rate from a series of measurements
     *
     * @param measurements - Array of measurements
     * @returns Average growth rate per measurement
     */
    calculateAverageGrowth(measurements) {
        if (measurements.length < 2)
            return 0;
        let totalGrowth = 0;
        for (let i = 1; i < measurements.length; i++) {
            totalGrowth += measurements[i] - measurements[i - 1];
        }
        return totalGrowth / (measurements.length - 1);
    }
    /**
     * Calculate CPU usage percentage
     *
     * @returns CPU usage percentage
     */
    calculateCpuUsage() {
        const currentUsage = process.cpuUsage();
        const currentTime = Date.now();
        const userDiff = currentUsage.user - this.lastCpuUsage.user;
        const systemDiff = currentUsage.system - this.lastCpuUsage.system;
        const timeDiff = (currentTime - this.lastCpuCheck) * 1000; // Convert to microseconds
        this.lastCpuUsage = currentUsage;
        this.lastCpuCheck = currentTime;
        if (timeDiff === 0)
            return 0;
        return ((userDiff + systemDiff) / timeDiff) * 100;
    }
    /**
     * Measure event loop lag
     *
     * @returns Promise that resolves to event loop lag in milliseconds
     */
    measureEventLoopLag() {
        return new Promise((resolve) => {
            const start = process.hrtime.bigint();
            setImmediate(() => {
                const end = process.hrtime.bigint();
                const lag = Number(end - start) / 1_000_000; // Convert to milliseconds
                resolve(lag);
            });
        });
    }
    /**
     * Get the number of open file descriptors
     *
     * @returns Number of open file descriptors
     */
    async getOpenFileCount() {
        try {
            if (process.platform === 'linux' || process.platform === 'darwin') {
                const result = execSync(`lsof -p ${process.pid} | wc -l`, { encoding: 'utf8' });
                return parseInt(result.trim(), 10) || 0;
            }
            else {
                // Fallback for other platforms
                return 0;
            }
        }
        catch (error) {
            // Return 0 if we can't measure (e.g., lsof not available)
            return 0;
        }
    }
    /**
     * Get system information
     *
     * @returns System information object
     */
    getSystemInfo() {
        return {
            platform: process.platform,
            arch: process.arch,
            nodeVersion: process.version,
            totalMemory: os.totalmem(),
            freeMemory: os.freemem(),
            cpuCount: os.cpus().length,
            uptime: os.uptime()
        };
    }
    /**
     * Start monitoring event loop lag
     */
    startEventLoopMonitoring() {
        // Measure event loop lag every 5 seconds
        setInterval(async () => {
            if (!this.isMonitoring)
                return;
            const lag = await this.measureEventLoopLag();
            if (lag > this.thresholds.eventLoopLagMs) {
                this.logger.warn('High event loop lag', { lagMs: parseFloat(lag.toFixed(1)) });
                this.emit('event_loop_lag', { lag });
            }
        }, 5000);
    }
    /**
     * Generate a comprehensive health report
     *
     * @returns Promise that resolves to a health report
     */
    async generateHealthReport() {
        const metrics = await this.getCurrentMetrics();
        const recommendations = [];
        let status = 'healthy';
        // Assess overall health
        if (metrics.memoryUsageMB > this.thresholds.memoryMB * 1.5) {
            status = 'critical';
            recommendations.push('Memory usage is critically high - consider restarting');
        }
        else if (metrics.memoryUsageMB > this.thresholds.memoryMB) {
            status = 'warning';
            recommendations.push('Memory usage is high - monitor for leaks');
        }
        if (metrics.cpuUsagePercent > this.thresholds.cpuPercent) {
            status = status === 'critical' ? 'critical' : 'warning';
            recommendations.push('CPU usage is high - check for blocking operations');
        }
        if (metrics.eventLoopLagMs > this.thresholds.eventLoopLagMs) {
            recommendations.push('Event loop lag detected - review async operations');
        }
        if (metrics.freeMemoryPercent < 5) {
            status = 'critical';
            recommendations.push('System memory critically low');
        }
        // Analyze trends
        const memoryTrend = this.memoryHistory.length >= 3 ?
            this.calculateAverageGrowth(this.memoryHistory.slice(-3)) > 0 ? 'increasing' : 'stable' :
            'insufficient_data';
        const cpuTrend = this.cpuHistory.length >= 3 ?
            this.calculateAverageGrowth(this.cpuHistory.slice(-3)) > 0 ? 'increasing' : 'stable' :
            'insufficient_data';
        return {
            status,
            metrics,
            recommendations,
            trends: {
                memoryTrend,
                cpuTrend
            }
        };
    }
    /**
     * Update monitoring thresholds
     *
     * @param newThresholds - New threshold values
     */
    updateThresholds(newThresholds) {
        this.thresholds = { ...this.thresholds, ...newThresholds };
        this.logger.info('Updated resource monitoring thresholds', { thresholds: this.thresholds });
    }
    /**
     * Get current thresholds
     *
     * @returns Current monitoring thresholds
     */
    getThresholds() {
        return { ...this.thresholds };
    }
}
// If this script is run directly, start monitoring
const _isMainModule = process.argv[1]?.endsWith('resource-monitor.ts') || process.argv[1]?.endsWith('resource-monitor.js');
if (_isMainModule) {
    const logger = LoggerService.getInstance().createComponentLogger('ResourceMonitor');
    const monitor = new ResourceMonitor();
    monitor.on('warning', (data) => {
        logger.warn('Resource Warning', { data });
    });
    monitor.on('memory_leak_warning', (data) => {
        logger.warn('Memory Leak Warning', { data });
    });
    monitor.on('cpu_spike_warning', (data) => {
        logger.warn('CPU Spike Warning', { data });
    });
    monitor.start();
    // Generate health report every 5 minutes
    setInterval(async () => {
        const report = await monitor.generateHealthReport();
        logger.info('Health Report', { report });
    }, 300000);
    // Graceful shutdown
    process.on('SIGINT', () => {
        logger.info('Shutting down resource monitor');
        monitor.stop();
        process.exit(0);
    });
}
//# sourceMappingURL=resource-monitor.js.map