/**
 * In-memory content-addressed cache implementing the CacheProvider interface.
 * LRU eviction at configurable entry limit.
 *
 * Phase 2 adds persistent file-based cache; this in-memory version is
 * sufficient for proving the API and for small workloads.
 */
import { createHash } from 'node:crypto';
import type { CacheProvider } from './types.js';

export interface MemoryCacheConfig {
  /** Maximum number of entries before LRU eviction. Default 1000. */
  maxEntries?: number;
}

export class MemoryCache implements CacheProvider {
  private readonly store = new Map<string, string>();
  private readonly maxEntries: number;

  constructor(config?: MemoryCacheConfig) {
    this.maxEntries = config?.maxEntries ?? 1000;
  }

  async getOrCompute(key: string, compute: () => Promise<string>): Promise<string> {
    const cached = this.store.get(key);
    if (cached !== undefined) {
      // Move to end (most recently used) for LRU
      this.store.delete(key);
      this.store.set(key, cached);
      return cached;
    }

    const value = await compute();
    this.set_sync(key, value);
    return value;
  }

  hash(content: string): string {
    return createHash('sha256').update(content).digest('hex').slice(0, 16);
  }

  async get(key: string): Promise<string | null> {
    const value = this.store.get(key);
    if (value === undefined) return null;
    // Move to end for LRU
    this.store.delete(key);
    this.store.set(key, value);
    return value;
  }

  async set(key: string, value: string): Promise<void> {
    this.set_sync(key, value);
  }

  /** Number of cached entries. */
  get size(): number {
    return this.store.size;
  }

  /** Clear all cached entries. */
  clear(): void {
    this.store.clear();
  }

  private set_sync(key: string, value: string): void {
    // Evict oldest if at capacity
    if (this.store.size >= this.maxEntries) {
      const oldest = this.store.keys().next().value as string;
      this.store.delete(oldest);
    }
    this.store.set(key, value);
  }
}
