import { TREE_NODE_KEY } from '../constants/tree';

export const generateNodeKey = (parentKey, currentNodeId) => {
  return `${parentKey ? parentKey + '_' : ''}${currentNodeId}`;
};

export const createTreeNode = (nodeId, nodeKey, depth, hasChildNodes) => {
  return {
    [TREE_NODE_KEY.ID]: nodeId,
    [TREE_NODE_KEY.KEY]: nodeKey,
    [TREE_NODE_KEY.DEPTH]: depth,
    [TREE_NODE_KEY.HAS_CHILD_NODES]: hasChildNodes,
  };
};

/**
 * for get row by node key from 'tree_node_key_row_id_map' directly
 * @param {array} tree
 * @returns tree_node_key_row_id_map
 * tree_node_key_row_id_map: { [node_key]: _id, ... }
 */
export const generateKeyTreeNodeRowIdMap = (tree) => {
  let tree_node_key_row_id_map = {};
  tree.forEach((node) => {
    tree_node_key_row_id_map[node[TREE_NODE_KEY.KEY]] = node[TREE_NODE_KEY.ID];
  });
  return tree_node_key_row_id_map;
};

export const getValidKeyTreeNodeFoldedMap = (keyTreeNodeFoldedMap, treeNodeKeyRecordIdMap) => {
  if (!keyTreeNodeFoldedMap) return {};

  let validKeyTreeNodeFoldedMap = {};
  Object.keys(keyTreeNodeFoldedMap).forEach((nodeKey) => {
    if (treeNodeKeyRecordIdMap[nodeKey]) {
      // just keep the folded status of exist nodes
      validKeyTreeNodeFoldedMap[nodeKey] = keyTreeNodeFoldedMap[nodeKey];
    }
  });
  return validKeyTreeNodeFoldedMap;
};

export const getRecordIdByTreeNodeKey = (nodeKey, treeNodeKeyRecordIdMap) => {
  return treeNodeKeyRecordIdMap[nodeKey];
};

export const getRecordsIdsByTreeNodeKeys = (nodesKeys, treeNodeKeyRecordIdMap) => {
  if (!Array.isArray(nodesKeys) || nodesKeys.length === 0 || !treeNodeKeyRecordIdMap) {
    return [];
  }
  let idExistMap = {};
  let selectedIds = [];
  nodesKeys.forEach((nodeKey) => {
    const selectedId = treeNodeKeyRecordIdMap[nodeKey];
    if (selectedId && !idExistMap[selectedId]) {
      selectedIds.push(selectedId);
      idExistMap[selectedId] = true;
    }
  });
  return selectedIds;
};

export const checkIsTreeNodeShown = (nodeKey, keyFoldedNodeMap) => {
  const foldedNodeKeys = keyFoldedNodeMap && Object.keys(keyFoldedNodeMap);
  if (!Array.isArray(foldedNodeKeys) || foldedNodeKeys.length === 0) {
    return true;
  }

  // parent node is folded
  return !foldedNodeKeys.some((foldedNodeKey) => nodeKey !== foldedNodeKey && nodeKey.includes(foldedNodeKey));
};

export const updatedKeyTreeNodeMap = (nodeKey, node, keyTreeNodeMap) => {
  if (!nodeKey || !node || !keyTreeNodeMap) return;
  keyTreeNodeMap[nodeKey] = node;
};

export const getTreeNodeByKey = (nodeKey, keyTreeNodeMap) => {
  if (!nodeKey || !keyTreeNodeMap) return null;
  return keyTreeNodeMap[nodeKey];
};

export const getTreeNodeById = (nodeId, tree) => {
  if (!nodeId || !Array.isArray(tree) || tree.length === 0) return null;
  return tree.find((node) => getTreeNodeId(node) === nodeId);
};

export const getTreeNodeId = (node) => {
  return node ? node[TREE_NODE_KEY.ID] : '';
};

export const getTreeNodeKey = (node) => {
  return node ? node[TREE_NODE_KEY.KEY] : '';
};

export const getTreeNodeDepth = (node) => {
  return node ? node[TREE_NODE_KEY.DEPTH] : 0;
};

export const checkTreeNodeHasChildNodes = (node) => {
  return node ? node[TREE_NODE_KEY.HAS_CHILD_NODES] : false;
};

export const resetTreeHasChildNodesStatus = (tree) => {
  if (!Array.isArray(tree) || tree.length === 0) {
    return;
  }
  tree.forEach((node, index) => {
    const nextNode = tree[index + 1];
    const nextNodeKey = getTreeNodeKey(nextNode);
    const currentNodeKey = getTreeNodeKey(node);
    if (checkTreeNodeHasChildNodes(node) && (!nextNode || !nextNodeKey.includes(currentNodeKey))) {
      tree[index][TREE_NODE_KEY.HAS_CHILD_NODES] = false;
    }
  });
};

export const addTreeChildNode = (newChildNode, parentNode, tree) => {
  if (!parentNode || !Array.isArray(tree) || tree.length === 0) {
    return;
  }
  const parentNodeKey = getTreeNodeKey(parentNode);
  const parentNodeDepth = getTreeNodeDepth(parentNode);
  const parentNodeIndex = tree.findIndex((node) => getTreeNodeKey(node) === parentNodeKey);
  if (parentNodeIndex < 0) {
    return;
  }

  if (!checkTreeNodeHasChildNodes(parentNode)) {
    tree[parentNodeIndex] = { ...parentNode, [TREE_NODE_KEY.HAS_CHILD_NODES]: true };
  }

  const childNodeDepth = parentNodeDepth + 1;
  let lastChildNodeIndex = parentNodeIndex;
  for (let i = parentNodeIndex + 1, len = tree.length; i < len; i++) {
    const currentNode = tree[i];
    if (!getTreeNodeKey(currentNode).includes(parentNodeKey)) {
      break;
    }

    // insert new child tag behind the last child tag
    if (getTreeNodeDepth(currentNode) === childNodeDepth) {
      lastChildNodeIndex = i;
    }
  }
  tree.splice(lastChildNodeIndex + 1, 0, newChildNode);
};

export const getAllSubTreeNodes = (nodeIndex, tree) => {
  const treeLen = Array.isArray(tree) ? tree.length : 0;
  const parentNode = tree[nodeIndex];
  const parentNodeKey = getTreeNodeKey(parentNode);
  if (!parentNodeKey || nodeIndex === treeLen - 1) return [];

  let subNodes = [];
  for (let i = nodeIndex + 1, len = treeLen; i < len; i++) {
    const currNodeKey = getTreeNodeKey(tree[i]);
    if (!currNodeKey || !currNodeKey.includes(parentNodeKey)) {
      break;
    }
    subNodes.push(tree[i]);
  }
  return subNodes;
};

export const getTreeChildNodes = (parentNode, tree) => {
  const parentNodeKey = getTreeNodeKey(parentNode);
  const parentNodeIndex = tree.findIndex((node) => getTreeNodeKey(node) === parentNodeKey);
  if (parentNodeIndex < 0) {
    return [];
  }

  const parentNodeDepth = getTreeNodeDepth(parentNode);
  const childNodeDepth = parentNodeDepth + 1;
  let childNodes = [];
  for (let i = parentNodeIndex + 1, len = tree.length; i < len; i++) {
    const currentNode = tree[i];
    if (!getTreeNodeKey(currentNode).includes(parentNodeKey)) {
      break;
    }

    if (getTreeNodeDepth(currentNode) === childNodeDepth) {
      childNodes.push({ ...currentNode });
    }
  }
  return childNodes;
};
