/**
 * Unit tests for DiffCache class.
 *
 * Tests TTL expiry, prefix invalidation, and env-var TTL configuration.
 */

import { describe, it, expect, vi, afterEach } from 'vitest';
import { DiffCache } from './diff-cache.js';

afterEach(() => {
  vi.restoreAllMocks();
});

describe('DiffCache', () => {
  it('returns undefined for a key that was never set', () => {
    const cache = new DiffCache<string>(5000);
    expect(cache.get('nonexistent')).toBeUndefined();
  });

  it('returns value when entry has not expired', () => {
    vi.spyOn(Date, 'now').mockReturnValue(1000);
    const cache = new DiffCache<string>(5000);
    cache.set('key', 'value');
    vi.spyOn(Date, 'now').mockReturnValue(5999); // 4999ms elapsed, TTL=5000
    expect(cache.get('key')).toBe('value');
  });

  it('returns undefined and deletes the entry when TTL has elapsed', () => {
    vi.spyOn(Date, 'now').mockReturnValue(1000);
    const cache = new DiffCache<string>(5000);
    cache.set('key', 'value');
    vi.spyOn(Date, 'now').mockReturnValue(6001); // 5001ms elapsed, TTL=5000
    expect(cache.get('key')).toBeUndefined();
    // Verify the key is no longer stored (second get also returns undefined)
    vi.spyOn(Date, 'now').mockReturnValue(6001);
    expect(cache.get('key')).toBeUndefined();
  });

  it('overwrites an existing entry and resets its TTL', () => {
    vi.spyOn(Date, 'now').mockReturnValue(1000);
    const cache = new DiffCache<string>(5000);
    cache.set('key', 'first');
    vi.spyOn(Date, 'now').mockReturnValue(4000); // overwrite before expiry
    cache.set('key', 'second');
    vi.spyOn(Date, 'now').mockReturnValue(8999); // 4999ms after overwrite, TTL=5000
    expect(cache.get('key')).toBe('second');
    vi.spyOn(Date, 'now').mockReturnValue(9001); // 5001ms after overwrite
    expect(cache.get('key')).toBeUndefined();
  });

  it('invalidateByPrefix removes all matching keys and preserves others', () => {
    const cache = new DiffCache<string>(60_000);
    cache.set('phase-1:abc', 'a');
    cache.set('phase-1:abc:file.ts', 'b');
    cache.set('phase-2:xyz', 'c');
    cache.invalidateByPrefix('phase-1:');
    expect(cache.get('phase-1:abc')).toBeUndefined();
    expect(cache.get('phase-1:abc:file.ts')).toBeUndefined();
    expect(cache.get('phase-2:xyz')).toBe('c');
  });

  it('singleton instances use REVIEW_DIFF_CACHE_TTL_MS env var for TTL', async () => {
    vi.resetModules();
    vi.stubEnv('REVIEW_DIFF_CACHE_TTL_MS', '1000');
    const { phaseMetaCache } = await import('./diff-cache.js');

    vi.spyOn(Date, 'now').mockReturnValue(0);
    phaseMetaCache.set('key', {} as any);
    vi.spyOn(Date, 'now').mockReturnValue(999);
    expect(phaseMetaCache.get('key')).toBeDefined();
    vi.spyOn(Date, 'now').mockReturnValue(1001);
    expect(phaseMetaCache.get('key')).toBeUndefined();

    vi.unstubAllEnvs();
  });
});
