import { useState, useEffect, useCallback } from 'react';
import uniq from 'lodash/uniq';
import remove from 'lodash/remove';
import slice from 'lodash/slice';
import { flattenTree } from '../utils/treeUtils';

/**
 * Get all parents of a node
 * @param node
 */
export var getParents = function getParents(node) {
  var parents = [];

  if (!node.parent) {
    return parents;
  }

  parents.push(node.parent);
  parents = parents.concat(getParents(node.parent));
  return parents;
};
/**
 * Check if any child nodes are selected.
 * @param node
 * @param value
 * @param itemKeys
 */

export var isSomeChildChecked = function isSomeChildChecked(node, value, itemKeys) {
  var childrenKey = itemKeys.childrenKey,
      valueKey = itemKeys.valueKey;

  if (!node[childrenKey] || !value) {
    return false;
  }

  return node[childrenKey].some(function (child) {
    var _child$childrenKey;

    if (value.some(function (n) {
      return n === child[valueKey];
    })) {
      return true;
    }

    if ((_child$childrenKey = child[childrenKey]) === null || _child$childrenKey === void 0 ? void 0 : _child$childrenKey.length) {
      return isSomeChildChecked(child, value, itemKeys);
    }

    return false;
  });
};
/**
 * Check if the parent is selected.
 * @param node
 * @param value
 * @param itemKeys
 */

export var isSomeParentChecked = function isSomeParentChecked(node, value, itemKeys) {
  var valueKey = itemKeys.valueKey;

  if (!value) {
    return false;
  }

  if (value.some(function (n) {
    return n === node[valueKey];
  })) {
    return true;
  }

  if (node.parent) {
    return isSomeParentChecked(node.parent, value, itemKeys);
  }

  return false;
};
export var getOtherItemValuesByUnselectChild = function getOtherItemValuesByUnselectChild(itemNode, value, itemKeys) {
  var valueKey = itemKeys.valueKey,
      childrenKey = itemKeys.childrenKey;
  var parentValues = [];
  var itemValues = []; // Find the parent node of the current node by value

  function findParent(item) {
    parentValues.push(item[valueKey]);

    if (value.some(function (v) {
      return v === item[valueKey];
    })) {
      return item;
    }

    if (item.parent) {
      var p = findParent(item.parent);

      if (p) {
        return p;
      }
    }

    return null;
  } // Get child nodes through parent node


  function pushChildValue(item) {
    if (!item[childrenKey]) {
      return;
    }

    item[childrenKey].forEach(function (n) {
      // Determine whether it is a direct parent
      if (parentValues.some(function (v) {
        return v === n[valueKey];
      }) && n[childrenKey]) {
        pushChildValue(n);
      } else if (n[valueKey] !== itemNode[valueKey]) {
        itemValues.push(n[valueKey]);
      }
    });
  }

  var parent = findParent(itemNode);

  if (!parent) {
    return [];
  }

  pushChildValue(parent);
  return itemValues;
};
/**
 * Remove the values of all children.
 */

export var removeAllChildrenValue = function removeAllChildrenValue(value, item, itemKeys) {
  var valueKey = itemKeys.valueKey,
      childrenKey = itemKeys.childrenKey;
  var removedValue = [];

  if (!item[childrenKey]) {
    return;
  }

  item[childrenKey].forEach(function (n) {
    removedValue = removedValue.concat(remove(value, function (v) {
      return v === n[valueKey];
    }));

    if (n[childrenKey]) {
      removeAllChildrenValue(value, n, itemKeys);
    }
  });
  return removedValue;
};
/**
 * A hook to flatten tree structure data
 * @param data
 */

export function useFlattenData(data, itemKeys) {
  var childrenKey = itemKeys.childrenKey;

  var _useState = useState(flattenTree(data)),
      flattenData = _useState[0],
      setFlattenData = _useState[1];

  var addFlattenData = useCallback(function (children, parent) {
    var nodes = children.map(function (child) {
      child.parent = parent;
      return child;
    });
    parent[childrenKey] = nodes;
    setFlattenData([].concat(flattenData, nodes));
  }, [childrenKey, flattenData]);
  useEffect(function () {
    setFlattenData(flattenTree(data));
  }, [data]);
  return {
    addFlattenData: addFlattenData,
    flattenData: flattenData
  };
}
/**
 * A hook for column data
 * @param flattenData
 */

export function useColumnData(flattenData) {
  // The columns displayed in the cascading panel.
  var _useState2 = useState([flattenData.filter(function (item) {
    return !item.parent;
  })]),
      columnData = _useState2[0],
      setColumnData = _useState2[1];
  /**
   * Add a list of options to the cascading panel. Used for lazy loading options.
   * @param column
   * @param index The index of the current column.
   */


  function addColumn(column, index) {
    setColumnData([].concat(slice(columnData, 0, index), [column]));
  }

  return {
    columnData: columnData,
    addColumn: addColumn,
    setColumnData: setColumnData
  };
}
/**
 * A hook that converts the value into a cascading value
 * @param props
 * @param flattenData
 */

export function useCascadeValue(props, flattenData) {
  var valueKey = props.valueKey,
      childrenKey = props.childrenKey,
      uncheckableItemValues = props.uncheckableItemValues,
      cascade = props.cascade,
      valueProp = props.value;
  /**
   * Get the values of all children
   */

  var getChildrenValue = useCallback(function (item) {
    var values = [];

    if (!item[childrenKey]) {
      return values;
    }

    item[childrenKey].forEach(function (n) {
      if (uncheckableItemValues && !uncheckableItemValues.some(function (v) {
        return v === n[valueKey];
      })) {
        values.push(n[valueKey]);
      }

      values = values.concat(getChildrenValue(n));
    });
    return values;
  }, [childrenKey, uncheckableItemValues, valueKey]);
  var splitValue = useCallback(function (item, checked, value) {
    var itemValue = item[valueKey];
    var childrenValue = getChildrenValue(item);
    var parents = getParents(item);
    var nextValue = [].concat(value);
    var removedValue = [];

    if (checked) {
      nextValue.push(itemValue); // Delete all values under the current node

      removedValue = removedValue.concat(removeAllChildrenValue(nextValue, item, {
        valueKey: valueKey,
        childrenKey: childrenKey
      })); // Traverse all ancestor nodes of the current node
      // Then determine whether all the child nodes of these nodes are selected, and then they themselves must be selected

      var _loop = function _loop(i) {
        // Whether the parent node can be selected
        var isCheckableParent = !uncheckableItemValues.some(function (v) {
          return v === parents[i][valueKey];
        });

        if (isCheckableParent) {
          var isCheckAll = parents[i][childrenKey] // Filter out options that are marked as not selectable
          .filter(function (n) {
            return !uncheckableItemValues.some(function (v) {
              return v === n[valueKey];
            });
          }) // Check if all nodes are selected
          .every(function (n) {
            return nextValue.some(function (v) {
              return v === n[valueKey];
            });
          });

          if (isCheckAll) {
            // Add parent node value
            nextValue.push(parents[i][valueKey]); // Delete all values under the parent node

            removedValue = removedValue.concat(removeAllChildrenValue(nextValue, parents[i], {
              valueKey: valueKey,
              childrenKey: childrenKey
            }));
          }
        }
      };

      for (var i = 0; i < parents.length; i++) {
        _loop(i);
      }
    } else {
      var tempValue = childrenValue.concat(parents.map(function (item) {
        return item[valueKey];
      }));
      nextValue = nextValue.concat(getOtherItemValuesByUnselectChild(item, nextValue, {
        valueKey: valueKey,
        childrenKey: childrenKey
      })); // Delete related child and parent nodes

      removedValue = remove(nextValue, function (v) {
        // Delete yourself
        if (v === itemValue) {
          return true;
        }

        return tempValue.some(function (n) {
          return n === v;
        });
      });
    }

    var uniqValue = uniq(nextValue);
    var uniqRemovedValue = uniq(removedValue);
    return {
      value: uniqValue,
      removedValue: uniqRemovedValue
    };
  }, [valueKey, childrenKey, uncheckableItemValues, getChildrenValue]);
  var transformValue = useCallback(function (value) {
    if (value === void 0) {
      value = [];
    }

    if (!cascade) {
      return value;
    }

    var tempRemovedValue = [];
    var nextValue = [];

    var _loop2 = function _loop2(i) {
      // If the value in the current value is already in the deleted list, it will not be processed
      if (tempRemovedValue.some(function (v) {
        return v === value[i];
      })) {
        return "continue";
      }

      var item = flattenData.find(function (v) {
        return v[valueKey] === value[i];
      });

      if (!item) {
        return "continue";
      }

      var sv = splitValue(item, true, value);
      tempRemovedValue = uniq(tempRemovedValue.concat(sv.removedValue)); // Get all relevant values

      nextValue = uniq(nextValue.concat(sv.value));
    };

    for (var i = 0; i < value.length; i++) {
      var _ret = _loop2(i);

      if (_ret === "continue") continue;
    } // Finally traverse all nextValue, and delete if its parent node is also nextValue


    return nextValue.filter(function (v) {
      var item = flattenData.find(function (n) {
        return n[valueKey] === v;
      });

      if ((item === null || item === void 0 ? void 0 : item.parent) && nextValue.some(function (v) {
        return v === item.parent[valueKey];
      })) {
        return false;
      }

      return true;
    });
  }, [cascade, flattenData, splitValue, valueKey]);

  var _useState3 = useState(transformValue(valueProp) || []),
      value = _useState3[0],
      setValue = _useState3[1];

  useEffect(function () {
    // Update value when valueProp is updated.
    setValue(transformValue(valueProp) || []);
  }, [transformValue, valueProp]);
  return {
    value: value,
    setValue: setValue,
    splitValue: splitValue
  };
}