/**
 * Unblock opportunities analysis.
 * @task T4784
 */
import { getAccessor } from '../store/data-accessor.js';
/** Build a reverse dependency map. */
function buildReverseDependencyMap(tasks) {
    const reverseMap = new Map();
    for (const task of tasks) {
        if (task.depends) {
            for (const dep of task.depends) {
                const existing = reverseMap.get(dep) || [];
                existing.push(task.id);
                reverseMap.set(dep, existing);
            }
        }
    }
    return reverseMap;
}
/** Count all transitive dependents. */
function countTransitiveDependents(taskId, reverseMap, visited = new Set()) {
    if (visited.has(taskId))
        return [];
    visited.add(taskId);
    const directDependents = reverseMap.get(taskId) || [];
    const allDependents = [...directDependents];
    for (const dep of directDependents) {
        const transitive = countTransitiveDependents(dep, reverseMap, visited);
        allDependents.push(...transitive);
    }
    return allDependents;
}
/** Analyze dependency graph for unblocking opportunities. */
export async function getUnblockOpportunities(cwd, accessor) {
    const acc = accessor ?? (await getAccessor(cwd));
    const { tasks } = await acc.queryTasks({});
    const taskMap = new Map(tasks.map((t) => [t.id, t]));
    const completedIds = new Set(tasks.filter((t) => t.status === 'done' || t.status === 'cancelled').map((t) => t.id));
    const nonDoneTasks = tasks.filter((t) => t.status !== 'done' && t.status !== 'cancelled');
    const reverseMap = buildReverseDependencyMap(tasks);
    // 1. High-impact completions
    const highImpact = [];
    for (const task of nonDoneTasks) {
        const allDependents = countTransitiveDependents(task.id, reverseMap, new Set());
        const uniqueDependents = [...new Set(allDependents)];
        if (uniqueDependents.length > 0) {
            highImpact.push({
                taskId: task.id,
                title: task.title,
                wouldUnblock: uniqueDependents.length,
                dependents: uniqueDependents,
            });
        }
    }
    highImpact.sort((a, b) => b.wouldUnblock - a.wouldUnblock);
    // 2. Single-blocker tasks
    const singleBlocker = [];
    for (const task of tasks) {
        if (!task.depends || task.depends.length === 0)
            continue;
        const incompleteDeps = task.depends.filter((depId) => !completedIds.has(depId));
        if (incompleteDeps.length === 1) {
            const blockerId = incompleteDeps[0];
            const blockerTask = taskMap.get(blockerId);
            singleBlocker.push({
                taskId: task.id,
                title: task.title,
                remainingBlocker: {
                    id: blockerId,
                    title: blockerTask?.title || blockerId,
                },
            });
        }
    }
    // 3. Common blockers
    const blockerCounts = new Map();
    for (const task of tasks) {
        if (!task.depends)
            continue;
        for (const depId of task.depends) {
            if (!completedIds.has(depId)) {
                const existing = blockerCounts.get(depId) || [];
                existing.push(task.id);
                blockerCounts.set(depId, existing);
            }
        }
    }
    const commonBlockers = [];
    for (const [blockerId, blockedTasks] of blockerCounts) {
        if (blockedTasks.length > 1) {
            const blockerTask = taskMap.get(blockerId);
            commonBlockers.push({
                taskId: blockerId,
                title: blockerTask?.title || blockerId,
                blocksCount: blockedTasks.length,
                blockedTasks,
            });
        }
    }
    commonBlockers.sort((a, b) => b.blocksCount - a.blocksCount);
    return { highImpact, singleBlocker, commonBlockers };
}
//# sourceMappingURL=unblock.js.map