export function compareWizardTasks(a, b) {
    if (a.sortOrder !== b.sortOrder)
        return a.sortOrder - b.sortOrder;
    return a.createdAt.localeCompare(b.createdAt);
}
function buildBaseDependencyOrder(tasks, dependencies) {
    const baseOrder = [...tasks].sort(compareWizardTasks);
    if (baseOrder.length <= 1) {
        return baseOrder;
    }
    const taskById = new Map(baseOrder.map((task) => [task.taskId, task]));
    const taskIds = new Set(taskById.keys());
    const inDegree = new Map();
    const outgoing = new Map();
    const seenEdges = new Set();
    for (const task of baseOrder) {
        inDegree.set(task.taskId, 0);
        outgoing.set(task.taskId, []);
    }
    for (const dependency of dependencies) {
        if (dependency.dependencyType !== "BlockedBy")
            continue;
        if (!taskIds.has(dependency.taskId) || !taskIds.has(dependency.dependsOnTaskId)) {
            continue;
        }
        if (dependency.taskId === dependency.dependsOnTaskId)
            continue;
        const edgeKey = `${dependency.dependsOnTaskId}->${dependency.taskId}`;
        if (seenEdges.has(edgeKey))
            continue;
        seenEdges.add(edgeKey);
        outgoing.get(dependency.dependsOnTaskId)?.push(dependency.taskId);
        inDegree.set(dependency.taskId, (inDegree.get(dependency.taskId) ?? 0) + 1);
    }
    const ordered = [];
    const ready = baseOrder.filter((task) => (inDegree.get(task.taskId) ?? 0) === 0);
    while (ready.length > 0) {
        ready.sort(compareWizardTasks);
        const next = ready.shift();
        if (!next)
            break;
        ordered.push(next);
        for (const dependentTaskId of outgoing.get(next.taskId) ?? []) {
            const nextInDegree = (inDegree.get(dependentTaskId) ?? 0) - 1;
            inDegree.set(dependentTaskId, nextInDegree);
            if (nextInDegree === 0) {
                const dependentTask = taskById.get(dependentTaskId);
                if (dependentTask) {
                    ready.push(dependentTask);
                }
            }
        }
    }
    if (ordered.length === baseOrder.length) {
        return ordered;
    }
    // Defensive fallback for incomplete/corrupt dependency graphs.
    const orderedTaskIds = new Set(ordered.map((task) => task.taskId));
    return [
        ...ordered,
        ...baseOrder.filter((task) => !orderedTaskIds.has(task.taskId)),
    ];
}
export function orderTasksByBlockedDependenciesWithDepth(tasks, dependencies) {
    const baseOrder = buildBaseDependencyOrder(tasks, dependencies);
    if (baseOrder.length === 0) {
        return [];
    }
    const taskIds = new Set(baseOrder.map((task) => task.taskId));
    const baseIndexByTaskId = new Map(baseOrder.map((task, index) => [task.taskId, index]));
    const childrenByParentId = new Map();
    for (const task of baseOrder) {
        const parentTaskId = task.parentTaskId &&
            task.parentTaskId !== task.taskId &&
            taskIds.has(task.parentTaskId)
            ? task.parentTaskId
            : null;
        const siblings = childrenByParentId.get(parentTaskId) ?? [];
        siblings.push(task);
        childrenByParentId.set(parentTaskId, siblings);
    }
    const blockedDependencies = dependencies.filter((dependency) => dependency.dependencyType === "BlockedBy" &&
        dependency.taskId !== dependency.dependsOnTaskId &&
        taskIds.has(dependency.taskId) &&
        taskIds.has(dependency.dependsOnTaskId));
    const subtreeTaskIdsCache = new Map();
    const subtreeTaskIdsInProgress = new Set();
    const getSubtreeTaskIds = (taskId) => {
        const cached = subtreeTaskIdsCache.get(taskId);
        if (cached) {
            return cached;
        }
        if (subtreeTaskIdsInProgress.has(taskId)) {
            return new Set([taskId]);
        }
        subtreeTaskIdsInProgress.add(taskId);
        const subtreeTaskIds = new Set([taskId]);
        for (const child of childrenByParentId.get(taskId) ?? []) {
            for (const childTaskId of getSubtreeTaskIds(child.taskId)) {
                subtreeTaskIds.add(childTaskId);
            }
        }
        subtreeTaskIdsInProgress.delete(taskId);
        subtreeTaskIdsCache.set(taskId, subtreeTaskIds);
        return subtreeTaskIds;
    };
    const compareByBaseOrder = (a, b) => (baseIndexByTaskId.get(a) ?? Number.MAX_SAFE_INTEGER) -
        (baseIndexByTaskId.get(b) ?? Number.MAX_SAFE_INTEGER);
    const orderSiblingBranches = (parentTaskId) => {
        const siblings = childrenByParentId.get(parentTaskId) ?? [];
        if (siblings.length <= 1) {
            return siblings;
        }
        const siblingIds = new Set(siblings.map((task) => task.taskId));
        const branchOwnerByTaskId = new Map();
        for (const sibling of siblings) {
            for (const subtreeTaskId of getSubtreeTaskIds(sibling.taskId)) {
                branchOwnerByTaskId.set(subtreeTaskId, sibling.taskId);
            }
        }
        const inDegree = new Map();
        const outgoing = new Map();
        const seenEdges = new Set();
        for (const sibling of siblings) {
            inDegree.set(sibling.taskId, 0);
            outgoing.set(sibling.taskId, []);
        }
        for (const dependency of blockedDependencies) {
            const blockedBranchId = branchOwnerByTaskId.get(dependency.taskId);
            const blockerBranchId = branchOwnerByTaskId.get(dependency.dependsOnTaskId);
            if (!blockedBranchId ||
                !blockerBranchId ||
                blockedBranchId === blockerBranchId ||
                !siblingIds.has(blockedBranchId) ||
                !siblingIds.has(blockerBranchId)) {
                continue;
            }
            const edgeKey = `${blockerBranchId}->${blockedBranchId}`;
            if (seenEdges.has(edgeKey))
                continue;
            seenEdges.add(edgeKey);
            outgoing.get(blockerBranchId)?.push(blockedBranchId);
            inDegree.set(blockedBranchId, (inDegree.get(blockedBranchId) ?? 0) + 1);
        }
        const ready = siblings
            .filter((task) => (inDegree.get(task.taskId) ?? 0) === 0)
            .map((task) => task.taskId);
        const orderedBranchIds = [];
        while (ready.length > 0) {
            ready.sort(compareByBaseOrder);
            const nextBranchId = ready.shift();
            if (!nextBranchId)
                break;
            orderedBranchIds.push(nextBranchId);
            for (const dependentBranchId of outgoing.get(nextBranchId) ?? []) {
                const nextInDegree = (inDegree.get(dependentBranchId) ?? 0) - 1;
                inDegree.set(dependentBranchId, nextInDegree);
                if (nextInDegree === 0) {
                    ready.push(dependentBranchId);
                }
            }
        }
        if (orderedBranchIds.length !== siblings.length) {
            return [...siblings].sort((a, b) => compareByBaseOrder(a.taskId, b.taskId));
        }
        const siblingById = new Map(siblings.map((task) => [task.taskId, task]));
        return orderedBranchIds
            .map((taskId) => siblingById.get(taskId))
            .filter((task) => !!task);
    };
    const orderedEntries = [];
    const seenTaskIds = new Set();
    const appendBranch = (task, depth, lineage) => {
        if (seenTaskIds.has(task.taskId)) {
            return;
        }
        seenTaskIds.add(task.taskId);
        orderedEntries.push({ task, depth });
        if (lineage.has(task.taskId)) {
            return;
        }
        const nextLineage = new Set(lineage);
        nextLineage.add(task.taskId);
        for (const child of orderSiblingBranches(task.taskId)) {
            appendBranch(child, depth + 1, nextLineage);
        }
    };
    for (const rootTask of orderSiblingBranches(null)) {
        appendBranch(rootTask, 0, new Set());
    }
    for (const task of baseOrder) {
        if (!seenTaskIds.has(task.taskId)) {
            appendBranch(task, 0, new Set());
        }
    }
    return orderedEntries;
}
export function orderTasksByBlockedDependencies(tasks, dependencies) {
    return orderTasksByBlockedDependenciesWithDepth(tasks, dependencies).map(({ task }) => task);
}
//# sourceMappingURL=taskDependencyOrdering.js.map