/**
 * Unit tests for findBrainFiles TTL cache.
 */

import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { findBrainFiles, invalidateBrainFilesCache } from '../src/utils';

function makeBrain(): string {
    const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'astra-brain-cache-'));
    fs.writeFileSync(path.join(dir, 'a.md'), '# A');
    fs.writeFileSync(path.join(dir, 'b.md'), '# B');
    return dir;
}

describe('findBrainFiles TTL cache', () => {
    let brain: string;

    beforeEach(() => {
        brain = makeBrain();
        invalidateBrainFilesCache();
    });

    afterEach(() => {
        try { fs.rmSync(brain, { recursive: true, force: true }); } catch { /* noop */ }
    });

    test('initial walk lists current files', () => {
        const files = findBrainFiles(brain);
        expect(files.length).toBe(2);
        expect(files.some((f) => f.endsWith('a.md'))).toBe(true);
    });

    test('within TTL: cache returns stale list when files added', () => {
        findBrainFiles(brain); // prime cache
        fs.writeFileSync(path.join(brain, 'c.md'), '# C');
        const files = findBrainFiles(brain);
        // Cache hit returns the previous list
        expect(files.length).toBe(2);
    });

    test('explicit invalidation forces fresh walk', () => {
        findBrainFiles(brain); // prime
        fs.writeFileSync(path.join(brain, 'c.md'), '# C');
        invalidateBrainFilesCache(brain);
        const files = findBrainFiles(brain);
        expect(files.length).toBe(3);
    });

    test('invalidateBrainFilesCache() with no arg clears all entries', () => {
        const a = makeBrain();
        const b = makeBrain();
        try {
            findBrainFiles(a);
            findBrainFiles(b);
            fs.writeFileSync(path.join(a, 'extra.md'), 'x');
            fs.writeFileSync(path.join(b, 'extra.md'), 'x');
            invalidateBrainFilesCache();
            expect(findBrainFiles(a).length).toBe(3);
            expect(findBrainFiles(b).length).toBe(3);
        } finally {
            fs.rmSync(a, { recursive: true, force: true });
            fs.rmSync(b, { recursive: true, force: true });
        }
    });

    test('returned array is a copy — mutations do not poison cache', () => {
        const first = findBrainFiles(brain);
        first.length = 0; // mutate caller's copy
        const second = findBrainFiles(brain);
        expect(second.length).toBe(2);
    });

    test('non-existent directory returns empty list and does not throw', () => {
        const fake = path.join(os.tmpdir(), 'astra-no-such-dir-' + Date.now());
        const files = findBrainFiles(fake);
        expect(files).toEqual([]);
    });
});
