// @crash-shield-ignore-file — config/cache reader for Planu-controlled JSON; writer is this codebase, shape guaranteed by build/seed.
// Cache layer for Dynamic Knowledge Engine
// Reads/writes data/knowledge/cache.json with per-category TTL
import { CATEGORY_TTL, } from '../../types/index.js';
import { readFile, writeFile, mkdir } from 'node:fs/promises';
import { join, dirname } from 'node:path';
const CACHE_DIR = 'data/knowledge';
const CACHE_FILE = 'cache.json';
const MAX_ENTRIES = 200;
const PRUNE_THRESHOLD_DAYS = 30;
function getCachePath() {
    return join(process.cwd(), CACHE_DIR, CACHE_FILE);
}
function createEmptyStore() {
    return { version: 1, entries: [], lastPruned: new Date().toISOString() };
}
function hashContext(context) {
    // Simple hash: sorted JSON string -> basic hash
    const str = JSON.stringify(context, Object.keys(context).sort());
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
        const char = str.charCodeAt(i);
        hash = (hash << 5) - hash + char;
        hash |= 0;
    }
    return Math.abs(hash).toString(36);
}
export async function readCache() {
    try {
        const raw = await readFile(getCachePath(), 'utf-8');
        return JSON.parse(raw);
    }
    catch {
        return createEmptyStore();
    }
}
export async function writeCache(store) {
    const path = getCachePath();
    await mkdir(dirname(path), { recursive: true });
    await writeFile(path, JSON.stringify(store, null, 2), 'utf-8');
}
export function getCacheKey(category, context) {
    return `${category}:${hashContext(context)}`;
}
export function findCachedEntry(store, category, context) {
    const contextHash = hashContext(context);
    const entry = store.entries.find((e) => e.category === category && e.context === contextHash);
    if (!entry) {
        return null;
    }
    // Check TTL
    if (new Date(entry.expiresAt) < new Date()) {
        return null;
    }
    return entry;
}
export async function setCacheEntry(category, context, items) {
    const store = await readCache();
    const contextHash = hashContext(context);
    const ttlHours = CATEGORY_TTL[category];
    const now = new Date();
    const expiresAt = new Date(now.getTime() + ttlHours * 60 * 60 * 1000);
    // Remove existing entry for same category+context
    store.entries = store.entries.filter((e) => !(e.category === category && e.context === contextHash));
    store.entries.push({
        category,
        context: contextHash,
        items,
        fetchedAt: now.toISOString(),
        expiresAt: expiresAt.toISOString(),
        ttl: ttlHours,
    });
    // Prune old entries
    const pruned = pruneCache(store);
    await writeCache(pruned);
}
export function pruneCache(store) {
    const now = new Date();
    const threshold = new Date(now.getTime() - PRUNE_THRESHOLD_DAYS * 24 * 60 * 60 * 1000);
    // Remove expired entries older than threshold
    let entries = store.entries.filter((e) => new Date(e.fetchedAt) > threshold);
    // Cap at MAX_ENTRIES (keep newest)
    if (entries.length > MAX_ENTRIES) {
        entries = entries
            .slice()
            .sort((a, b) => new Date(b.fetchedAt).getTime() - new Date(a.fetchedAt).getTime())
            .slice(0, MAX_ENTRIES);
    }
    return { ...store, entries, lastPruned: now.toISOString() };
}
//# sourceMappingURL=knowledge-cache.js.map