/**
 * Task hierarchy operations - parent/child tree traversal and validation.
 * Ported from lib/tasks/hierarchy.sh
 *
 * @epic T4454
 * @task T4529
 */
/**
 * Get direct children of a task.
 */
export function getChildren(taskId, tasks) {
    return tasks.filter((t) => t.parentId === taskId);
}
/**
 * Get direct child IDs.
 */
export function getChildIds(taskId, tasks) {
    return getChildren(taskId, tasks).map((t) => t.id);
}
/**
 * Get all descendants of a task (recursive).
 */
export function getDescendants(taskId, tasks) {
    const result = [];
    const queue = [taskId];
    const visited = new Set();
    while (queue.length > 0) {
        const current = queue.shift();
        if (visited.has(current))
            continue;
        visited.add(current);
        const children = getChildren(current, tasks);
        for (const child of children) {
            result.push(child);
            queue.push(child.id);
        }
    }
    return result;
}
/**
 * Get all descendant IDs (flat list).
 */
export function getDescendantIds(taskId, tasks) {
    return getDescendants(taskId, tasks).map((t) => t.id);
}
/**
 * Get the parent chain (ancestors) from a task up to the root.
 * Returns ordered from immediate parent to root.
 */
export function getParentChain(taskId, tasks) {
    const chain = [];
    const taskMap = new Map(tasks.map((t) => [t.id, t]));
    let current = taskMap.get(taskId);
    const visited = new Set();
    while (current?.parentId) {
        if (visited.has(current.parentId))
            break; // circular reference guard
        visited.add(current.parentId);
        const parent = taskMap.get(current.parentId);
        if (!parent)
            break;
        chain.push(parent);
        current = parent;
    }
    return chain;
}
/**
 * Get the parent chain as IDs.
 */
export function getParentChainIds(taskId, tasks) {
    return getParentChain(taskId, tasks).map((t) => t.id);
}
/**
 * Calculate depth of a task in the hierarchy (0-based).
 * Root tasks have depth 0, their children depth 1, etc.
 */
export function getDepth(taskId, tasks) {
    return getParentChain(taskId, tasks).length;
}
/**
 * Get the root ancestor of a task.
 */
export function getRootAncestor(taskId, tasks) {
    const chain = getParentChain(taskId, tasks);
    return chain.length > 0 ? chain[chain.length - 1] : null;
}
/**
 * Check if a task is an ancestor of another.
 */
export function isAncestorOf(ancestorId, descendantId, tasks) {
    const chain = getParentChainIds(descendantId, tasks);
    return chain.includes(ancestorId);
}
/**
 * Check if a task is a descendant of another.
 */
export function isDescendantOf(descendantId, ancestorId, tasks) {
    return isAncestorOf(ancestorId, descendantId, tasks);
}
/**
 * Get sibling tasks (same parent).
 */
export function getSiblings(taskId, tasks) {
    const task = tasks.find((t) => t.id === taskId);
    if (!task)
        return [];
    if (task.parentId) {
        return tasks.filter((t) => t.parentId === task.parentId && t.id !== taskId);
    }
    // Root-level siblings: tasks with no parent, excluding self
    return tasks.filter((t) => !t.parentId && t.id !== taskId);
}
export function validateHierarchy(parentId, tasks, policy) {
    if (!parentId) {
        return { valid: true };
    }
    const parent = tasks.find((t) => t.id === parentId);
    if (!parent) {
        return {
            valid: false,
            error: { code: 'E_PARENT_NOT_FOUND', message: `Parent task ${parentId} not found` },
        };
    }
    // Check depth
    const parentDepth = getDepth(parentId, tasks);
    if (parentDepth + 1 >= (policy?.maxDepth ?? 3)) {
        return {
            valid: false,
            error: {
                code: 'E_DEPTH_EXCEEDED',
                message: `Maximum nesting depth ${policy?.maxDepth ?? 3} would be exceeded`,
            },
        };
    }
    // Check sibling limit
    const existingChildren = getChildren(parentId, tasks);
    const maxSiblings = policy?.maxSiblings ?? 0;
    if (maxSiblings > 0 && existingChildren.length >= maxSiblings) {
        return {
            valid: false,
            error: {
                code: 'E_SIBLING_LIMIT',
                message: `Parent ${parentId} already has ${maxSiblings} children (max)`,
            },
        };
    }
    return { valid: true };
}
/**
 * Detect circular reference if parentId were set.
 */
export function wouldCreateCircle(taskId, newParentId, tasks) {
    if (taskId === newParentId)
        return true;
    const descendants = getDescendantIds(taskId, tasks);
    return descendants.includes(newParentId);
}
export function buildTree(tasks) {
    const childrenMap = new Map();
    for (const task of tasks) {
        const parentKey = task.parentId ?? null;
        if (!childrenMap.has(parentKey)) {
            childrenMap.set(parentKey, []);
        }
        childrenMap.get(parentKey).push(task);
    }
    function buildNode(task) {
        const children = (childrenMap.get(task.id) ?? []).map(buildNode);
        return { task, children };
    }
    const roots = childrenMap.get(null) ?? [];
    return roots.map(buildNode);
}
/**
 * Flatten a tree back to a list (depth-first).
 */
export function flattenTree(nodes) {
    const result = [];
    for (const node of nodes) {
        result.push(node.task);
        result.push(...flattenTree(node.children));
    }
    return result;
}
//# sourceMappingURL=hierarchy.js.map