const ROOT_TREE_NODE = 0;

export const addItemToDict = (item, dict) => {
    const {id} = item;

    if (dict[id]) {
        throw new Error(`Item with id ${id} already in dict`);
    }

    return {
        ...dict,
        [id]: item
    };
};

export const addItemToTree = (item, tree, dict, root = ROOT_TREE_NODE) => {
    const {id, parentId = root} = item;

    if (!id || (!parentId && parentId !== root)) {
        throw new Error(`Got invalid item data: ${item}`);
    }

    const parent = dict[parentId];

    if (parent && parent.parentId === id) {
        throw new Error(`Cyclic structure detected: item ${id} references ${parent.id}`);
    }

    let treeNode = tree[parentId] || [];

    return {
        ...tree,
        [parentId]: [...treeNode, item]
    };
};

export const getItemByIdFromDict = (id, dict) => {
    return dict[id];
};

export const getItemSiblingsFromTree = (item, tree, root = ROOT_TREE_NODE) => {
    if (!item) {
        return;
    }

    const parentId = item.parentId || root;

    return tree[parentId];
};

export const getItemsChildrenFromTree = (item, tree) => {
    if (!item) {
        return;
    }

    const {id} = item;

    return tree[id];
};

export const getRootNodeFromTree = (tree, root = ROOT_TREE_NODE) => {
    return tree[root];
};

export const resolveItemPath = (item, dict, path = []) => {
    if (!item) {
        return;
    }

    const {id, parentId} = item;

    if (!id || !(id in dict)) {
        return;
    }

    const parent = dict[parentId];
    const nextPath = [id, ...path];

    if (!parent) {
        return nextPath;
    }

    return resolveItemPath(parent, dict, nextPath);
};

export const resolveItemsMaxBranchLengthInTree = (item, tree, resolvedLength = 0) => {
    if (!item) {
        return resolvedLength;
    }

    const children = getItemsChildrenFromTree(item, tree);

    if (children && children.length > 0) {
        const candidates = children.map(child =>
            resolveItemsMaxBranchLengthInTree(child, tree, resolvedLength + 1)
        );

        return Math.max(...candidates);
    }

    return resolvedLength;
};
