/**
 * detectCycle — Pre-mortem #5 mitigation.
 *
 * Given a task graph keyed by `depends_on` edges, determines whether
 * adding a proposed edge `newEdge.from → newEdge.to` would create a cycle.
 * Returns the cycle path (as an array of task ids starting at the re-visited
 * node) or `null` when the graph remains acyclic.
 *
 * The graph is read-only. `tasks` maps id → depends_on[] (after parsing).
 */
/**
 * Return the first cycle path that would exist if `newEdge` were added,
 * or `null` if adding the edge keeps the graph acyclic.
 */
export function detectCycle(dependsOnByTaskId, newEdge) {
    // Build a working adjacency map that includes the hypothetical edge.
    const adj = new Map();
    for (const [id, deps] of dependsOnByTaskId.entries()) {
        adj.set(id, [...deps]);
    }
    const existing = adj.get(newEdge.from) ?? [];
    if (!existing.includes(newEdge.to)) {
        adj.set(newEdge.from, [...existing, newEdge.to]);
    }
    // DFS from `newEdge.to` — if we can reach `newEdge.from`, the hypothetical
    // edge closes a cycle.
    const stack = [{ node: newEdge.to, path: [newEdge.to] }];
    const visited = new Set();
    while (stack.length > 0) {
        const { node, path } = stack.pop();
        if (node === newEdge.from) {
            // Found back to origin → close the loop for readability.
            return [...path, newEdge.from];
        }
        if (visited.has(node))
            continue;
        visited.add(node);
        const nexts = adj.get(node) ?? [];
        for (const next of nexts) {
            stack.push({ node: next, path: [...path, next] });
        }
    }
    return null;
}
/**
 * Return the transitive set of task ids that depend (directly or indirectly)
 * on `targetId`. Used by `task list --blocked-by`.
 */
export function collectBlockedBy(dependsOnByTaskId, targetId) {
    // Build reverse adjacency: edge `A depends_on B` means A is blocked by B.
    const reverse = new Map();
    for (const [id, deps] of dependsOnByTaskId.entries()) {
        for (const dep of deps) {
            const arr = reverse.get(dep) ?? [];
            arr.push(id);
            reverse.set(dep, arr);
        }
    }
    const visited = new Set();
    const out = [];
    const queue = [...(reverse.get(targetId) ?? [])];
    while (queue.length > 0) {
        const current = queue.shift();
        if (visited.has(current))
            continue;
        visited.add(current);
        out.push(current);
        for (const next of reverse.get(current) ?? []) {
            if (!visited.has(next))
                queue.push(next);
        }
    }
    return out;
}
//# sourceMappingURL=cycle.js.map