/**
 * Graph cache - memoized dependency graph operations.
 * Ported from lib/tasks/graph-cache.sh
 *
 * @epic T4454
 * @task T4529
 */
import { getDependentIds } from './dependency-check.js';
import { computeDependencyWaves } from './graph-ops.js';
import { getChildIds, getDescendantIds } from './hierarchy.js';
/**
 * Graph cache for expensive dependency calculations.
 * Automatically invalidates when tasks change.
 */
export class GraphCache {
    descendantsCache = new Map();
    childrenCache = new Map();
    dependentsCache = new Map();
    wavesCache = null;
    taskChecksum = '';
    ttlMs;
    constructor(options) {
        this.ttlMs = options?.ttlMs ?? 30_000; // 30 second default TTL
    }
    /**
     * Compute a simple checksum from task data to detect changes.
     */
    computeChecksum(tasks) {
        // Use task count + IDs + statuses + deps as a quick checksum
        const parts = tasks.map((t) => `${t.id}:${t.status}:${t.parentId ?? ''}:${(t.depends ?? []).join(',')}`);
        return parts.sort().join('|');
    }
    /**
     * Check if cache is still valid for given tasks.
     */
    isValid(tasks) {
        return this.computeChecksum(tasks) === this.taskChecksum;
    }
    /**
     * Check if a cache entry has expired.
     */
    isExpired(entry) {
        return Date.now() - entry.timestamp > this.ttlMs;
    }
    /**
     * Invalidate all caches.
     */
    invalidate() {
        this.descendantsCache.clear();
        this.childrenCache.clear();
        this.dependentsCache.clear();
        this.wavesCache = null;
        this.taskChecksum = '';
    }
    /**
     * Ensure cache is fresh for the given task set.
     */
    ensureFresh(tasks) {
        if (!this.isValid(tasks)) {
            this.invalidate();
            this.taskChecksum = this.computeChecksum(tasks);
        }
    }
    /**
     * Get descendants of a task (cached).
     */
    getDescendants(taskId, tasks) {
        this.ensureFresh(tasks);
        const cached = this.descendantsCache.get(taskId);
        if (cached && !this.isExpired(cached)) {
            return cached.value;
        }
        const result = getDescendantIds(taskId, tasks);
        this.descendantsCache.set(taskId, { value: result, timestamp: Date.now() });
        return result;
    }
    /**
     * Get children of a task (cached).
     */
    getChildren(taskId, tasks) {
        this.ensureFresh(tasks);
        const cached = this.childrenCache.get(taskId);
        if (cached && !this.isExpired(cached)) {
            return cached.value;
        }
        const result = getChildIds(taskId, tasks);
        this.childrenCache.set(taskId, { value: result, timestamp: Date.now() });
        return result;
    }
    /**
     * Get dependents of a task (cached).
     */
    getDependents(taskId, tasks) {
        this.ensureFresh(tasks);
        const cached = this.dependentsCache.get(taskId);
        if (cached && !this.isExpired(cached)) {
            return cached.value;
        }
        const result = getDependentIds(taskId, tasks);
        this.dependentsCache.set(taskId, { value: result, timestamp: Date.now() });
        return result;
    }
    /**
     * Get dependency waves (cached).
     */
    getWaves(tasks) {
        this.ensureFresh(tasks);
        if (this.wavesCache && !this.isExpired(this.wavesCache)) {
            return this.wavesCache.value;
        }
        const result = computeDependencyWaves(tasks);
        this.wavesCache = { value: result, timestamp: Date.now() };
        return result;
    }
    /**
     * Get cache statistics.
     */
    getStats() {
        return {
            descendantsSize: this.descendantsCache.size,
            childrenSize: this.childrenCache.size,
            dependentsSize: this.dependentsCache.size,
            hasWaves: this.wavesCache !== null,
        };
    }
}
//# sourceMappingURL=graph-cache.js.map