import { test, describe, it, beforeEach } from 'node:test';
import assert from 'node:assert';
import { Arbiter } from '../../src/core/Arbiter.js';
import { ChainRule } from '../../src/authorization/rules/ChainRule.js';

describe('Cache Memory Improvements (SimpleLRUCache default, injectable factory)', () => {
  let arbiter;
  let chainRule;

  beforeEach(() => {
    arbiter = new Arbiter({
      directCheckCacheSize: 1000,
      directCheckCacheTTL: 60000
    });
    chainRule = new ChainRule(arbiter);
  });

  it('should use SimpleLRUCache (default cacheFactory) for chain caches', () => {
    // Verify ChainRule uses the default SimpleLRUCache
    assert.ok(chainRule.chainResultCache.constructor.name === 'SimpleLRUCache');
    
    // Verify cache capacity
    assert.strictEqual(chainRule.maxCacheSize, 2000);
  });

  it('should use SimpleLRUCache for direct check cache', () => {
    // Verify Arbiter uses the default SimpleLRUCache
    assert.ok(arbiter.directCheckCache.constructor.name === 'SimpleLRUCache');
    
    // Verify cache capacity
    assert.strictEqual(arbiter.directCheckCacheSize, 1000);
  });

  it('should use SimpleLRUCache for relation manager caches', () => {
    // Cache state lives in RelationCaches (RF-08): reach it via `_caches`.
    assert.ok(arbiter.relationManager._caches.relationLookupCache.constructor.name === 'SimpleLRUCache');
    assert.ok(arbiter.relationManager._caches.valueLookupCache.constructor.name === 'SimpleLRUCache');
    
    // Verify cache capacity
    assert.strictEqual(arbiter.relationManager._caches.maxCacheSize, 10000);
  });

  it('should use SimpleLRUCache for value manager caches', () => {
    // Verify ValueManager uses the default SimpleLRUCache
    assert.ok(arbiter.valueManager.blurredValueCache.constructor.name === 'SimpleLRUCache');
    assert.ok(arbiter.valueManager.distributionCache.constructor.name === 'SimpleLRUCache');
  });

  it('should use SimpleLRUCache for similarity manager cache', () => {
    // SimilarityManager is only constructed when similarityParams are provided
    const simArbiter = new Arbiter({ similarityParams: {} });
    assert.ok(simArbiter.similarityManager.nodeKeyToVector.constructor.name === 'SimpleLRUCache');
  });

  it('should handle cache eviction automatically', () => {
    // Test that the default cache handles eviction automatically
    const cache = chainRule.chainResultCache;
    const initialSize = cache.size();
    
    // Fill cache beyond capacity
    for (let i = 0; i < chainRule.maxCacheSize + 100; i++) {
      cache.set(`key_${i}`, { result: { possibility: 0.5 }, timestamp: Date.now() });
    }
    
    // Cache should not exceed capacity
    assert.ok(cache.size() <= chainRule.maxCacheSize);
    console.log(`Cache size after overflow: ${cache.size()} (max: ${chainRule.maxCacheSize})`);
  });

  it('should track eviction statistics', () => {
    // Test that eviction statistics are tracked
    const cache = chainRule.chainResultCache;
    
    // Fill cache beyond capacity to trigger evictions
    for (let i = 0; i < chainRule.maxCacheSize + 50; i++) {
      cache.set(`key_${i}`, { result: { possibility: 0.5 }, timestamp: Date.now() });
    }
    
    // Check if eviction stats are tracked
    if (chainRule.stats) {
      console.log(`Chain result cache evictions: ${chainRule.stats.evictedResults || 0}`);
      console.log(`Chain path cache evictions: ${chainRule.stats.evictedPaths || 0}`);
    }
  });

  it('should provide better memory management than Map', () => {
    // Compare memory usage patterns
    const mapCache = new Map();
    const hyperbolicCache = chainRule.chainResultCache;
    
    // Fill both caches
    for (let i = 0; i < 1000; i++) {
      const value = { result: { possibility: 0.5 }, timestamp: Date.now() };
      mapCache.set(`key_${i}`, value);
      hyperbolicCache.set(`key_${i}`, value);
    }
    
    // Map grows indefinitely, the default cache is bounded
    assert.ok(mapCache.size === 1000);
    assert.ok(hyperbolicCache.size() <= chainRule.maxCacheSize);
    
    console.log(`Map size: ${mapCache.size}`);
    console.log(`SimpleLRUCache size: ${hyperbolicCache.size()}`);
  });

  it('should handle TTL without manual cleanup', () => {
    // Test that TTL is handled without manual cleanup
    const cache = arbiter.directCheckCache;
    
    // Add entries with different timestamps
    const now = Date.now();
    cache.set('key1', { result: { possibility: 0.5 }, timestamp: now });
    cache.set('key2', { result: { possibility: 0.5 }, timestamp: now - 70000 }); // Expired
    
    // SimpleLRUCache keeps both entries; TTL is enforced at read time
    // No manual cleanup needed
    assert.ok(cache.has('key1'));
    assert.ok(cache.has('key2')); // Still in cache, but will be evicted based on frequency
  });
});

console.log('✅ Cache Memory Improvements Test Suite');
console.log('📊 Benefits:');
console.log('  - Automatic eviction based on frequency and recency');
console.log('  - No manual TTL cleanup needed');
console.log('  - Bounded memory usage');
console.log('  - Better cache hit rates through intelligent eviction');
console.log('  - Protection against memory leaks');
