export class CacheManager {
    constructor(options = {}) {
        this.cache = new Map();
        this.timers = new Map();
        this.memoryUsage = 0;
        this.options = {
            maxSize: options.maxSize || 1000,
            maxMemory: options.maxMemory || 50 * 1024 * 1024,
            defaultTTL: options.defaultTTL || 5 * 60 * 1000,
            cleanupInterval: options.cleanupInterval || 60 * 1000
        };
        if (this.options.cleanupInterval > 0) {
            this.startCleanupTimer();
        }
    }
    set(key, value, ttl) {
        const size = this.estimateSize(value);
        const entry = {
            value,
            timestamp: Date.now(),
            size
        };
        if (this.cache.size >= this.options.maxSize) {
            this.evictLRU();
        }
        if (this.memoryUsage + size > this.options.maxMemory) {
            this.evictByMemory(size);
        }
        const oldEntry = this.cache.get(key);
        if (oldEntry) {
            this.memoryUsage -= oldEntry.size || 0;
        }
        const oldTimer = this.timers.get(key);
        if (oldTimer !== undefined) {
            clearTimeout(oldTimer);
            this.timers.delete(key);
        }
        this.cache.set(key, entry);
        this.memoryUsage += size;
        if (ttl || this.options.defaultTTL) {
            const timeout = ttl || this.options.defaultTTL;
            const timerId = setTimeout(() => {
                this.timers.delete(key);
                this.delete(key);
            }, timeout);
            this.timers.set(key, timerId);
        }
    }
    get(key) {
        const entry = this.cache.get(key);
        if (!entry)
            return undefined;
        if (this.isExpired(entry)) {
            this.delete(key);
            return undefined;
        }
        entry.timestamp = Date.now();
        return entry.value;
    }
    has(key) {
        const entry = this.cache.get(key);
        if (!entry)
            return false;
        if (this.isExpired(entry)) {
            this.delete(key);
            return false;
        }
        return true;
    }
    delete(key) {
        const timer = this.timers.get(key);
        if (timer !== undefined) {
            clearTimeout(timer);
            this.timers.delete(key);
        }
        const entry = this.cache.get(key);
        if (entry) {
            this.memoryUsage -= entry.size || 0;
            return this.cache.delete(key);
        }
        return false;
    }
    clear() {
        for (const timer of this.timers.values()) {
            clearTimeout(timer);
        }
        this.timers.clear();
        this.cache.clear();
        this.memoryUsage = 0;
    }
    getStats() {
        return {
            size: this.cache.size,
            memoryUsage: this.memoryUsage,
            maxSize: this.options.maxSize,
            maxMemory: this.options.maxMemory
        };
    }
    cleanup() {
        let removed = 0;
        const now = Date.now();
        for (const [key, entry] of this.cache) {
            if (this.isExpired(entry, now)) {
                this.delete(key);
                removed++;
            }
        }
        return removed;
    }
    destroy() {
        this.stopCleanupTimer();
        this.clear();
    }
    isExpired(entry, now) {
        if (!this.options.defaultTTL)
            return false;
        const currentTime = now || Date.now();
        return currentTime - entry.timestamp > this.options.defaultTTL;
    }
    evictLRU() {
        let lruKey;
        let lruTime = Infinity;
        for (const [key, entry] of this.cache) {
            if (entry.timestamp < lruTime) {
                lruTime = entry.timestamp;
                lruKey = key;
            }
        }
        if (lruKey) {
            this.delete(lruKey);
        }
    }
    evictByMemory(requiredSize) {
        const entries = Array.from(this.cache.entries())
            .sort((a, b) => a[1].timestamp - b[1].timestamp);
        let freedMemory = 0;
        for (const [key, entry] of entries) {
            if (freedMemory >= requiredSize)
                break;
            freedMemory += entry.size || 0;
            this.delete(key);
        }
    }
    estimateSize(value) {
        if (typeof value === 'string') {
            return value.length * 2;
        }
        else if (typeof value === 'object' && value !== null) {
            try {
                return JSON.stringify(value).length * 2;
            }
            catch {
                return 1024;
            }
        }
        else {
            return 8;
        }
    }
    startCleanupTimer() {
        this.cleanupTimer = setInterval(() => {
            this.cleanup();
        }, this.options.cleanupInterval);
    }
    stopCleanupTimer() {
        if (this.cleanupTimer) {
            clearInterval(this.cleanupTimer);
            this.cleanupTimer = undefined;
        }
    }
}
//# sourceMappingURL=cache-manager.js.map