import { getCallableNodes, getCallEdges, getFileNodesAll, getImportEdges } from '../../db/index.js';
import { loadNative } from '../../infrastructure/native.js';
import { isTestFile } from '../../infrastructure/test-filter.js';
/**
 * Find cycles using Tarjan's SCC algorithm.
 *
 * Builds a label-based adjacency list directly from DB rows — no intermediate
 * CodeGraph construction. This is O(V + E) with minimal memory overhead.
 */
export function findCycles(db, opts = {}) {
    const fileLevel = opts.fileLevel !== false;
    const noTests = opts.noTests || false;
    const edges = [];
    const seen = new Set();
    if (fileLevel) {
        let nodes = getFileNodesAll(db);
        if (noTests)
            nodes = nodes.filter((n) => !isTestFile(n.file));
        const nodeIds = new Set();
        const idToFile = new Map();
        for (const n of nodes) {
            nodeIds.add(n.id);
            idToFile.set(n.id, n.file);
        }
        for (const e of getImportEdges(db)) {
            if (!nodeIds.has(e.source_id) || !nodeIds.has(e.target_id))
                continue;
            if (e.source_id === e.target_id)
                continue;
            const src = idToFile.get(e.source_id);
            const tgt = idToFile.get(e.target_id);
            const key = `${src}\0${tgt}`;
            if (seen.has(key))
                continue;
            seen.add(key);
            edges.push({ source: src, target: tgt });
        }
    }
    else {
        let nodes = getCallableNodes(db);
        if (noTests)
            nodes = nodes.filter((n) => !isTestFile(n.file));
        const nodeIds = new Set();
        const idToLabel = new Map();
        for (const n of nodes) {
            nodeIds.add(n.id);
            idToLabel.set(n.id, `${n.name}|${n.file}`);
        }
        for (const e of getCallEdges(db)) {
            if (!nodeIds.has(e.source_id) || !nodeIds.has(e.target_id))
                continue;
            if (e.source_id === e.target_id)
                continue;
            const src = idToLabel.get(e.source_id);
            const tgt = idToLabel.get(e.target_id);
            const key = `${src}\0${tgt}`;
            if (seen.has(key))
                continue;
            seen.add(key);
            edges.push({ source: src, target: tgt });
        }
    }
    const native = loadNative();
    if (native) {
        return native.detectCycles(edges);
    }
    return tarjanFromEdges(edges);
}
export function findCyclesJS(edges) {
    return tarjanFromEdges(edges);
}
/**
 * Run Tarjan's SCC on a flat edge list. Returns SCCs with length > 1 (cycles).
 * Uses a simple adjacency-list Map instead of a full CodeGraph.
 */
function tarjanFromEdges(edges) {
    const adj = new Map();
    const allNodes = new Set();
    for (const { source, target } of edges) {
        allNodes.add(source);
        allNodes.add(target);
        let list = adj.get(source);
        if (!list) {
            list = [];
            adj.set(source, list);
        }
        list.push(target);
    }
    let index = 0;
    const stack = [];
    const onStack = new Set();
    const indices = new Map();
    const lowlinks = new Map();
    const sccs = [];
    function strongconnect(v) {
        indices.set(v, index);
        lowlinks.set(v, index);
        index++;
        stack.push(v);
        onStack.add(v);
        const successors = adj.get(v);
        if (successors) {
            for (const w of successors) {
                if (!indices.has(w)) {
                    strongconnect(w);
                    lowlinks.set(v, Math.min(lowlinks.get(v), lowlinks.get(w)));
                }
                else if (onStack.has(w)) {
                    lowlinks.set(v, Math.min(lowlinks.get(v), indices.get(w)));
                }
            }
        }
        if (lowlinks.get(v) === indices.get(v)) {
            const scc = [];
            let w;
            do {
                w = stack.pop();
                onStack.delete(w);
                scc.push(w);
            } while (w !== v);
            if (scc.length > 1)
                sccs.push(scc);
        }
    }
    for (const id of allNodes) {
        if (!indices.has(id))
            strongconnect(id);
    }
    return sccs;
}
export function formatCycles(cycles) {
    if (cycles.length === 0) {
        return 'No circular dependencies detected.';
    }
    const lines = [`Found ${cycles.length} circular dependency cycle(s):\n`];
    for (let i = 0; i < cycles.length; i++) {
        const cycle = cycles[i];
        lines.push(`  Cycle ${i + 1} (${cycle.length} files):`);
        for (const file of cycle) {
            lines.push(`    -> ${file}`);
        }
        lines.push(`    -> ${cycle[0]} (back to start)`);
        lines.push('');
    }
    return lines.join('\n');
}
//# sourceMappingURL=cycles.js.map