/**
 * Distributed Caching for Teams (Item 104)
 * Shared response cache across team members
 */
import { EventEmitter } from 'events';
import crypto from 'crypto';
export class DistributedCache extends EventEmitter {
    cache = new Map();
    config;
    hits = 0;
    misses = 0;
    syncTimer = null;
    constructor(config = {}) {
        super();
        this.config = {
            maxSize: 100 * 1024 * 1024, // 100MB
            ttl: 3600000, // 1 hour
            syncInterval: 60000, // 1 minute
            nodes: [],
            ...config,
        };
    }
    generateKey(input) {
        return crypto.createHash('sha256').update(input).digest('hex').slice(0, 16);
    }
    set(key, value, userId) {
        const hashedKey = this.generateKey(key);
        const size = Buffer.byteLength(value, 'utf8');
        // Check size limit
        if (size > this.config.maxSize / 10) {
            return false; // Single entry too large
        }
        // Evict if needed
        this.evictIfNeeded(size);
        const entry = {
            key: hashedKey,
            value,
            metadata: {
                createdBy: userId,
                createdAt: new Date(),
                expiresAt: new Date(Date.now() + this.config.ttl),
                hits: 0,
                size,
            },
        };
        this.cache.set(hashedKey, entry);
        this.emit('set', { key: hashedKey, size });
        return true;
    }
    get(key) {
        const hashedKey = this.generateKey(key);
        const entry = this.cache.get(hashedKey);
        if (!entry) {
            this.misses++;
            return null;
        }
        if (new Date() > entry.metadata.expiresAt) {
            this.cache.delete(hashedKey);
            this.misses++;
            return null;
        }
        entry.metadata.hits++;
        this.hits++;
        this.emit('hit', { key: hashedKey });
        return entry.value;
    }
    has(key) {
        const hashedKey = this.generateKey(key);
        const entry = this.cache.get(hashedKey);
        return entry !== undefined && new Date() <= entry.metadata.expiresAt;
    }
    delete(key) {
        const hashedKey = this.generateKey(key);
        return this.cache.delete(hashedKey);
    }
    evictIfNeeded(newSize) {
        let totalSize = this.getTotalSize();
        while (totalSize + newSize > this.config.maxSize && this.cache.size > 0) {
            // Evict least recently hit entries
            let oldestEntry = null;
            for (const entry of this.cache.entries()) {
                if (!oldestEntry || entry[1].metadata.hits < oldestEntry[1].metadata.hits) {
                    oldestEntry = entry;
                }
            }
            if (oldestEntry) {
                this.cache.delete(oldestEntry[0]);
                totalSize -= oldestEntry[1].metadata.size;
            }
        }
    }
    getTotalSize() {
        let total = 0;
        for (const entry of this.cache.values()) {
            total += entry.metadata.size;
        }
        return total;
    }
    getStats() {
        const total = this.hits + this.misses;
        return {
            totalEntries: this.cache.size,
            totalSize: this.getTotalSize(),
            hitRate: total > 0 ? this.hits / total : 0,
            missRate: total > 0 ? this.misses / total : 0,
        };
    }
    clear() {
        this.cache.clear();
        this.hits = 0;
        this.misses = 0;
        this.emit('cleared');
    }
    startSync() {
        this.syncTimer = setInterval(() => {
            this.cleanup();
            this.emit('sync', this.getStats());
        }, this.config.syncInterval);
    }
    stopSync() {
        if (this.syncTimer) {
            clearInterval(this.syncTimer);
            this.syncTimer = null;
        }
    }
    cleanup() {
        const now = new Date();
        for (const [key, entry] of this.cache) {
            if (now > entry.metadata.expiresAt) {
                this.cache.delete(key);
            }
        }
    }
    dispose() {
        this.stopSync();
        this.clear();
        this.removeAllListeners();
    }
}
let instance = null;
export function getDistributedCache(config) {
    if (!instance)
        instance = new DistributedCache(config);
    return instance;
}
export default DistributedCache;
//# sourceMappingURL=distributed-cache.js.map