/**
 * Adaptive Memory Management with Garbage Collection Optimization
 * Enterprise-grade memory management for high-performance text processing
 */
import { getLogger } from '../core/logger.js';
const logger = getLogger('adaptive-memory');
/**
 * Advanced Memory Manager with Predictive Allocation and Adaptive GC
 * Provides enterprise-grade memory optimization for text processing workloads
 */
export class AdaptiveMemoryManager {
    constructor() {
        this.memoryPools = new Map();
        this.allocationHistory = [];
        this.gcMetrics = [];
        this.memoryPressureCallbacks = new Set();
        // Performance optimization flags
        this.isEnabled = true;
        this.predictiveAllocation = true;
        this.adaptiveGC = true;
        this.compressionEnabled = true;
        // Memory thresholds and limits
        this.maxHeapSize = 512 * 1024 * 1024; // 512MB
        this.pressureThreshold = 0.8; // 80% usage triggers pressure mode
        this.gcTriggerThreshold = 0.9; // 90% usage triggers GC
        this.poolMaxAge = 5 * 60 * 1000; // 5 minutes
        // Advanced memory tracking
        this.weakRefs = new Set();
        this.compressionRegistry = new Map();
        this.allocationPatterns = new Map();
        this.initializeMemoryMonitoring();
        this.setupGCOptimization();
    }
    static getInstance() {
        if (!AdaptiveMemoryManager.instance) {
            AdaptiveMemoryManager.instance = new AdaptiveMemoryManager();
        }
        return AdaptiveMemoryManager.instance;
    }
    /**
     * Initialize advanced memory monitoring with predictive analysis
     */
    initializeMemoryMonitoring() {
        // Monitor allocation patterns for prediction
        setInterval(() => {
            this.analyzeAllocationPatterns();
            this.predictivePreallocation();
        }, 10000); // Every 10 seconds
        // Memory pressure detection
        setInterval(() => {
            const metrics = this.getMemoryMetrics();
            if (metrics.pressure > this.pressureThreshold) {
                this.handleMemoryPressure();
            }
        }, 1000); // Every second
        // Periodic cleanup of stale objects
        setInterval(() => {
            this.cleanupStaleObjects();
        }, 30000); // Every 30 seconds
        // Advanced GC optimization
        if (this.adaptiveGC) {
            this.setupAdaptiveGC();
        }
    }
    /**
     * Setup garbage collection optimization with adaptive tuning
     */
    setupGCOptimization() {
        // Monitor GC events if available
        if (typeof global.gc === 'function') {
            const originalGC = global.gc;
            global.gc = (...args) => {
                const startTime = performance.now();
                const beforeHeap = this.estimateHeapUsage();
                const result = originalGC.apply(global, args);
                const duration = performance.now() - startTime;
                const afterHeap = this.estimateHeapUsage();
                const freedBytes = beforeHeap - afterHeap;
                this.gcMetrics.push({
                    timestamp: Date.now(),
                    duration,
                    freedBytes,
                });
                // Keep only recent metrics
                if (this.gcMetrics.length > 100) {
                    this.gcMetrics.splice(0, this.gcMetrics.length - 100);
                }
                return result;
            };
        }
    }
    /**
     * Create or get optimized memory pool for specific object types
     */
    createPool(name, factory, cleanup = () => { }, validator = () => true, maxSize = 100) {
        this.memoryPools.set(name, {
            objects: [],
            maxSize,
            totalAllocated: 0,
            factory: factory,
            cleanup: cleanup,
            validator: validator,
        });
        logger.debug(`Created memory pool: ${name} (max: ${maxSize})`);
    }
    /**
     * Acquire object from pool with intelligent allocation tracking
     */
    acquire(poolName) {
        const pool = this.memoryPools.get(poolName);
        if (!pool) {
            logger.warn(`Memory pool not found: ${poolName}`);
            return null;
        }
        // Try to reuse existing object
        for (let i = 0; i < pool.objects.length; i++) {
            const obj = pool.objects[i];
            if (pool.validator(obj.data)) {
                // Remove from pool and update metrics
                pool.objects.splice(i, 1);
                obj.lastUsed = Date.now();
                obj.useCount++;
                this.trackAllocation(poolName, obj.size);
                return obj.data;
            }
        }
        // Create new object if pool allows
        if (pool.totalAllocated < pool.maxSize) {
            const newObj = pool.factory();
            const objSize = this.estimateObjectSize(newObj);
            pool.totalAllocated++;
            this.trackAllocation(poolName, objSize);
            return newObj;
        }
        logger.warn(`Memory pool exhausted: ${poolName}`);
        return null;
    }
    /**
     * Return object to pool with intelligent lifecycle management
     */
    release(poolName, obj) {
        const pool = this.memoryPools.get(poolName);
        if (!pool) {
            logger.warn(`Memory pool not found: ${poolName}`);
            return;
        }
        // Clean object for reuse
        pool.cleanup(obj);
        // Add back to pool if not full
        if (pool.objects.length < pool.maxSize) {
            const objSize = this.estimateObjectSize(obj);
            pool.objects.push({
                id: this.generateObjectId(),
                size: objSize,
                lastUsed: Date.now(),
                useCount: 0,
                data: obj,
            });
        }
    }
    /**
     * Intelligent string compression for large text objects
     */
    compressString(key, text) {
        if (!this.compressionEnabled || text.length < 1000) {
            return text;
        }
        try {
            // Simple compression using gzip-like approach
            const compressed = this.simpleCompress(text);
            const ratio = compressed.length / text.length;
            if (ratio < 0.7) {
                // Only store if >30% compression
                this.compressionRegistry.set(key, {
                    original: text,
                    compressed,
                    ratio,
                });
                logger.debug(`Compressed string ${key}: ${(ratio * 100).toFixed(1)}% of original`);
                return key; // Return key as placeholder
            }
        }
        catch (error) {
            logger.warn('String compression failed', { error, key });
        }
        return text;
    }
    /**
     * Decompress string from compression registry
     */
    decompressString(keyOrText) {
        const compressed = this.compressionRegistry.get(keyOrText);
        if (compressed) {
            return this.simpleDecompress(compressed.compressed);
        }
        return keyOrText; // Return as-is if not compressed
    }
    /**
     * Analyze allocation patterns for predictive optimization
     */
    analyzeAllocationPatterns() {
        const now = Date.now();
        const recentAllocations = this.allocationHistory.filter((alloc) => now - alloc.timestamp < 60000 // Last minute
        );
        // Group by type and analyze patterns
        const patterns = new Map();
        for (const alloc of recentAllocations) {
            if (!patterns.has(alloc.type)) {
                patterns.set(alloc.type, { sizes: [], timestamps: [] });
            }
            const pattern = patterns.get(alloc.type);
            pattern.sizes.push(alloc.size);
            pattern.timestamps.push(alloc.timestamp);
        }
        // Update allocation patterns for prediction
        for (const [type, data] of patterns) {
            const avgSize = data.sizes.reduce((a, b) => a + b, 0) / data.sizes.length;
            const frequency = data.timestamps.length;
            this.allocationPatterns.set(type, {
                count: frequency,
                avgSize,
                frequency: frequency / 60, // per second
            });
        }
    }
    /**
     * Predictive pre-allocation based on patterns
     */
    predictivePreallocation() {
        if (!this.predictiveAllocation)
            return;
        for (const [type, pattern] of this.allocationPatterns) {
            // Predict if we'll need more objects of this type
            const predictedNeed = Math.ceil(pattern.frequency * 10); // Next 10 seconds
            const pool = this.memoryPools.get(type);
            if (pool && pool.objects.length < predictedNeed) {
                const needed = Math.min(predictedNeed - pool.objects.length, 5); // Max 5 at once
                for (let i = 0; i < needed; i++) {
                    const obj = pool.factory();
                    const objSize = this.estimateObjectSize(obj);
                    pool.objects.push({
                        id: this.generateObjectId(),
                        size: objSize,
                        lastUsed: Date.now(),
                        useCount: 0,
                        data: obj,
                    });
                }
                logger.debug(`Pre-allocated ${needed} objects for pool: ${type}`);
            }
        }
    }
    /**
     * Handle memory pressure with intelligent optimization
     */
    handleMemoryPressure() {
        logger.warn('Memory pressure detected, optimizing...');
        // 1. Trigger immediate cleanup
        this.cleanupStaleObjects();
        // 2. Force compression of large strings
        this.forceCompressionOptimization();
        // 3. Reduce pool sizes temporarily
        this.reducePools();
        // 4. Trigger GC if available
        if (typeof global.gc === 'function') {
            global.gc();
        }
        // 5. Notify pressure callbacks
        for (const callback of this.memoryPressureCallbacks) {
            try {
                callback();
            }
            catch (error) {
                logger.warn('Memory pressure callback failed', { error });
            }
        }
    }
    /**
     * Setup adaptive GC with machine learning optimization
     */
    setupAdaptiveGC() {
        // Analyze GC patterns and optimize timing
        setInterval(() => {
            if (this.gcMetrics.length < 5)
                return;
            const recentGCs = this.gcMetrics.slice(-10);
            const avgDuration = recentGCs.reduce((sum, gc) => sum + gc.duration, 0) / recentGCs.length;
            const totalFreed = recentGCs.reduce((sum, gc) => sum + gc.freedBytes, 0);
            // Adaptive GC trigger based on efficiency
            const efficiency = totalFreed / (avgDuration * recentGCs.length);
            if (efficiency < 1000) {
                // Less than 1KB freed per ms
                // Reduce GC frequency
                this.adaptiveGC = false;
                setTimeout(() => {
                    this.adaptiveGC = true;
                }, 30000);
                logger.debug('Temporarily reduced GC frequency due to low efficiency');
            }
        }, 60000); // Every minute
    }
    /**
     * Clean up stale objects and optimize memory usage
     */
    cleanupStaleObjects() {
        const now = Date.now();
        let totalCleaned = 0;
        for (const [poolName, pool] of this.memoryPools) {
            const before = pool.objects.length;
            // Remove objects older than maxAge
            pool.objects = pool.objects.filter((obj) => {
                const age = now - obj.lastUsed;
                if (age > this.poolMaxAge) {
                    pool.cleanup(obj.data);
                    totalCleaned++;
                    return false;
                }
                return true;
            });
            const cleaned = before - pool.objects.length;
            if (cleaned > 0) {
                logger.debug(`Cleaned ${cleaned} stale objects from pool: ${poolName}`);
            }
        }
        // Clean up weak references
        const validRefs = new Set();
        for (const ref of this.weakRefs) {
            if (ref.deref()) {
                validRefs.add(ref);
            }
        }
        this.weakRefs = validRefs;
        if (totalCleaned > 0) {
            logger.info(`Memory cleanup completed: ${totalCleaned} objects cleaned`);
        }
    }
    /**
     * Force compression optimization during pressure
     */
    forceCompressionOptimization() {
        // Compress any large uncompressed strings
        const compressionCandidates = [];
        // Find large objects in pools
        for (const pool of this.memoryPools.values()) {
            for (const obj of pool.objects) {
                if (typeof obj.data === 'string' && obj.data.length > 500) {
                    compressionCandidates.push(obj.data);
                }
            }
        }
        for (const text of compressionCandidates) {
            const key = this.generateObjectId();
            this.compressString(key, text);
        }
        logger.debug(`Force-compressed ${compressionCandidates.length} strings`);
    }
    /**
     * Temporarily reduce pool sizes during pressure
     */
    reducePools() {
        for (const [poolName, pool] of this.memoryPools) {
            const targetSize = Math.floor(pool.objects.length * 0.7); // Reduce by 30%
            if (pool.objects.length > targetSize) {
                // Remove least recently used objects
                pool.objects.sort((a, b) => a.lastUsed - b.lastUsed);
                const removed = pool.objects.splice(0, pool.objects.length - targetSize);
                for (const obj of removed) {
                    pool.cleanup(obj.data);
                }
                logger.debug(`Reduced pool ${poolName} from ${pool.objects.length + removed.length} to ${pool.objects.length}`);
            }
        }
    }
    /**
     * Register callback for memory pressure events
     */
    onMemoryPressure(callback) {
        this.memoryPressureCallbacks.add(callback);
    }
    /**
     * Get comprehensive memory metrics
     */
    getMemoryMetrics() {
        const heapUsage = this.estimateHeapUsage();
        const _totalPoolSize = Array.from(this.memoryPools.values()).reduce((sum, pool) => sum + pool.objects.length, 0);
        return {
            allocated: heapUsage,
            used: heapUsage,
            fragmentation: this.calculateFragmentation(),
            gcCount: this.gcMetrics.length,
            gcTime: this.gcMetrics.reduce((sum, gc) => sum + gc.duration, 0),
            pressure: heapUsage / this.maxHeapSize,
        };
    }
    /**
     * Get detailed performance analytics
     */
    getAnalytics() {
        // Pool analytics
        const pools = {};
        for (const [name, pool] of this.memoryPools) {
            const efficiency = pool.objects.reduce((sum, obj) => sum + obj.useCount, 0) /
                Math.max(pool.objects.length, 1);
            pools[name] = {
                size: pool.objects.length,
                allocated: pool.totalAllocated,
                efficiency,
            };
        }
        // Compression analytics
        const compressionStats = Array.from(this.compressionRegistry.values());
        const totalSaved = compressionStats.reduce((sum, stat) => {
            return sum + (stat.original.length - stat.compressed.length);
        }, 0);
        const avgRatio = compressionStats.reduce((sum, stat) => sum + stat.ratio, 0) /
            Math.max(compressionStats.length, 1);
        // GC analytics
        const recentGCs = this.gcMetrics.slice(-20);
        const avgGCDuration = recentGCs.reduce((sum, gc) => sum + gc.duration, 0) / Math.max(recentGCs.length, 1);
        const gcEfficiency = recentGCs.reduce((sum, gc) => sum + gc.freedBytes, 0) /
            Math.max(avgGCDuration * recentGCs.length, 1);
        // Pattern analytics
        const patterns = {};
        for (const [type, pattern] of this.allocationPatterns) {
            patterns[type] = {
                frequency: pattern.frequency,
                avgSize: pattern.avgSize,
            };
        }
        return {
            pools,
            compression: {
                totalSaved,
                ratio: avgRatio,
                count: compressionStats.length,
            },
            gc: {
                avgDuration: avgGCDuration,
                efficiency: gcEfficiency,
                frequency: recentGCs.length / 60, // per minute
            },
            patterns,
        };
    }
    // Utility methods
    estimateHeapUsage() {
        if (typeof process !== 'undefined' && process.memoryUsage) {
            return process.memoryUsage().heapUsed;
        }
        // Fallback estimation
        let totalSize = 0;
        for (const pool of this.memoryPools.values()) {
            totalSize += pool.objects.reduce((sum, obj) => sum + obj.size, 0);
        }
        return totalSize;
    }
    estimateObjectSize(obj) {
        if (typeof obj === 'string') {
            return obj.length * 2; // UTF-16 encoding
        }
        if (obj instanceof ArrayBuffer) {
            return obj.byteLength;
        }
        if (obj && typeof obj === 'object') {
            return JSON.stringify(obj).length * 2;
        }
        return 64; // Default estimate
    }
    calculateFragmentation() {
        // Simplified fragmentation calculation
        const totalAllocated = Array.from(this.memoryPools.values()).reduce((sum, pool) => sum + pool.totalAllocated, 0);
        const totalUsed = Array.from(this.memoryPools.values()).reduce((sum, pool) => sum + pool.objects.length, 0);
        return totalUsed > 0 ? 1 - totalUsed / totalAllocated : 0;
    }
    trackAllocation(type, size) {
        this.allocationHistory.push({
            size,
            timestamp: Date.now(),
            type,
        });
        // Keep history manageable
        if (this.allocationHistory.length > 1000) {
            this.allocationHistory.splice(0, this.allocationHistory.length - 1000);
        }
    }
    generateObjectId() {
        return `obj_${crypto.randomUUID()}`;
    }
    simpleCompress(text) {
        // Simplified compression using RLE-like approach
        const encoder = new TextEncoder();
        const bytes = encoder.encode(text);
        const compressed = [];
        for (let i = 0; i < bytes.length; i++) {
            let count = 1;
            while (i + count < bytes.length && bytes[i] === bytes[i + count] && count < 255) {
                count++;
            }
            if (count > 2) {
                compressed.push(255, count, bytes[i]); // Escape sequence
                i += count - 1;
            }
            else {
                compressed.push(bytes[i]);
            }
        }
        return new Uint8Array(compressed);
    }
    simpleDecompress(compressed) {
        const decompressed = [];
        for (let i = 0; i < compressed.length; i++) {
            if (compressed[i] === 255 && i + 2 < compressed.length) {
                const count = compressed[i + 1];
                const byte = compressed[i + 2];
                for (let j = 0; j < count; j++) {
                    decompressed.push(byte);
                }
                i += 2;
            }
            else {
                decompressed.push(compressed[i]);
            }
        }
        const decoder = new TextDecoder();
        return decoder.decode(new Uint8Array(decompressed));
    }
}
// Export singleton instance
export const memoryManager = AdaptiveMemoryManager.getInstance();
//# sourceMappingURL=adaptive-memory.js.map