import { EventEmitter } from "events";
import { logger } from "../monitoring/logger.js";
class ContextCache extends EventEmitter {
  cache = /* @__PURE__ */ new Map();
  accessOrder = [];
  options;
  stats;
  currentSize = 0;
  constructor(options = {}) {
    super();
    this.options = {
      maxSize: options.maxSize || 100 * 1024 * 1024,
      // 100MB default
      maxItems: options.maxItems || 1e4,
      defaultTTL: options.defaultTTL || 36e5,
      // 1 hour default
      enableStats: options.enableStats ?? true,
      onEvict: options.onEvict || (() => {
      })
    };
    this.stats = {
      hits: 0,
      misses: 0,
      evictions: 0,
      size: 0,
      itemCount: 0,
      hitRate: 0,
      avgAccessTime: 0
    };
  }
  /**
   * Get item from cache
   */
  get(key) {
    const startTime = Date.now();
    const entry = this.cache.get(key);
    if (!entry) {
      this.stats.misses++;
      this.updateHitRate();
      return void 0;
    }
    if (entry.ttl && Date.now() - entry.createdAt > entry.ttl) {
      this.delete(key);
      this.stats.misses++;
      this.updateHitRate();
      return void 0;
    }
    entry.hits++;
    entry.lastAccessed = Date.now();
    this.updateAccessOrder(key);
    this.stats.hits++;
    this.updateHitRate();
    this.updateAvgAccessTime(Date.now() - startTime);
    return entry.value;
  }
  /**
   * Set item in cache
   */
  set(key, value, options = {}) {
    const size = options.size || this.estimateSize(value);
    const ttl = options.ttl ?? this.options.defaultTTL;
    if (this.cache.size >= this.options.maxItems || this.currentSize + size > this.options.maxSize) {
      this.evict(size);
    }
    if (this.cache.has(key)) {
      this.delete(key);
    }
    const entry = {
      value,
      size,
      hits: 0,
      createdAt: Date.now(),
      lastAccessed: Date.now(),
      ttl
    };
    this.cache.set(key, entry);
    this.accessOrder.push(key);
    this.currentSize += size;
    this.stats.size = this.currentSize;
    this.stats.itemCount = this.cache.size;
    this.emit("set", key, value);
  }
  /**
   * Delete item from cache
   */
  delete(key) {
    const entry = this.cache.get(key);
    if (!entry) return false;
    this.cache.delete(key);
    this.currentSize -= entry.size;
    this.accessOrder = this.accessOrder.filter((k) => k !== key);
    this.stats.size = this.currentSize;
    this.stats.itemCount = this.cache.size;
    this.emit("delete", key);
    return true;
  }
  /**
   * Clear entire cache
   */
  clear() {
    const oldSize = this.cache.size;
    this.cache.clear();
    this.accessOrder = [];
    this.currentSize = 0;
    this.stats.size = 0;
    this.stats.itemCount = 0;
    this.stats.evictions += oldSize;
    this.emit("clear");
  }
  /**
   * Check if key exists and is valid
   */
  has(key) {
    const entry = this.cache.get(key);
    if (!entry) return false;
    if (entry.ttl && Date.now() - entry.createdAt > entry.ttl) {
      this.delete(key);
      return false;
    }
    return true;
  }
  /**
   * Get cache statistics
   */
  getStats() {
    return { ...this.stats };
  }
  /**
   * Get cache size info
   */
  getSize() {
    return {
      bytes: this.currentSize,
      items: this.cache.size,
      utilization: this.currentSize / this.options.maxSize
    };
  }
  /**
   * Preload multiple items
   */
  preload(entries) {
    const startTime = Date.now();
    for (const entry of entries) {
      this.set(entry.key, entry.value, {
        ttl: entry.ttl,
        size: entry.size
      });
    }
    logger.debug("Cache preload complete", {
      items: entries.length,
      duration: Date.now() - startTime,
      cacheSize: this.currentSize
    });
  }
  /**
   * Get multiple items efficiently
   */
  getMany(keys) {
    const results = /* @__PURE__ */ new Map();
    for (const key of keys) {
      const value = this.get(key);
      if (value !== void 0) {
        results.set(key, value);
      }
    }
    return results;
  }
  /**
   * Warm cache with computed values
   */
  async warmUp(keys, compute, options = {}) {
    const { parallel = true } = options;
    if (parallel) {
      const promises = keys.map(async (key) => {
        if (!this.has(key)) {
          const value = await compute(key);
          this.set(key, value, { ttl: options.ttl });
        }
      });
      await Promise.all(promises);
    } else {
      for (const key of keys) {
        if (!this.has(key)) {
          const value = await compute(key);
          this.set(key, value, { ttl: options.ttl });
        }
      }
    }
  }
  /**
   * Get or compute value
   */
  async getOrCompute(key, compute, options = {}) {
    const cached = this.get(key);
    if (cached !== void 0) {
      return cached;
    }
    const value = await compute();
    this.set(key, value, options);
    return value;
  }
  // Private methods
  evict(requiredSize) {
    const startEvictions = this.stats.evictions;
    while ((this.cache.size >= this.options.maxItems || this.currentSize + requiredSize > this.options.maxSize) && this.accessOrder.length > 0) {
      const keyToEvict = this.accessOrder.shift();
      const entry = this.cache.get(keyToEvict);
      if (entry) {
        this.cache.delete(keyToEvict);
        this.currentSize -= entry.size;
        this.stats.evictions++;
        this.options.onEvict(keyToEvict, entry);
        this.emit("evict", keyToEvict, entry);
      }
    }
    if (this.stats.evictions > startEvictions) {
      logger.debug("Cache eviction", {
        evicted: this.stats.evictions - startEvictions,
        currentSize: this.currentSize,
        requiredSize
      });
    }
  }
  updateAccessOrder(key) {
    const index = this.accessOrder.indexOf(key);
    if (index > -1) {
      this.accessOrder.splice(index, 1);
    }
    this.accessOrder.push(key);
  }
  estimateSize(value) {
    if (typeof value === "string") {
      return value.length * 2;
    }
    if (typeof value === "object" && value !== null) {
      return JSON.stringify(value).length * 2;
    }
    return 8;
  }
  updateHitRate() {
    const total = this.stats.hits + this.stats.misses;
    this.stats.hitRate = total > 0 ? this.stats.hits / total : 0;
  }
  updateAvgAccessTime(time) {
    const alpha = 0.1;
    this.stats.avgAccessTime = this.stats.avgAccessTime * (1 - alpha) + time * alpha;
  }
  /**
   * Cleanup expired entries periodically
   */
  startCleanup(intervalMs = 6e4) {
    return setInterval(() => {
      let cleaned = 0;
      for (const [key, entry] of this.cache.entries()) {
        if (entry.ttl && Date.now() - entry.createdAt > entry.ttl) {
          this.delete(key);
          cleaned++;
        }
      }
      if (cleaned > 0) {
        logger.debug("Cache cleanup", { cleaned, remaining: this.cache.size });
      }
    }, intervalMs);
  }
}
export {
  ContextCache
};
//# sourceMappingURL=context-cache.js.map
