import _extends from "@babel/runtime/helpers/esm/extends";
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
import React, { useRef, useState, useCallback, useEffect } from 'react';
import PropTypes from 'prop-types';
import omit from 'lodash/omit';
import pick from 'lodash/pick';
import isNil from 'lodash/isNil';
import isFunction from 'lodash/isFunction';
import shallowEqual from '../utils/shallowEqual';
import DropdownMenu from './DropdownMenu';
import { findNodeOfTree, flattenTree, getNodeParents } from '../utils/treeUtils';
import { usePaths } from './utils';
import { getSafeRegExpString, createChainedFunction, mergeRefs, useControlled, useCustom, useClassNames } from '../utils';
import { PickerToggle, PickerOverlay, SearchBar, PickerToggleTrigger, usePickerClassName, usePublicMethods, useToggleKeyDownEvent, useFocusItemValue, pickTriggerPropKeys, omitTriggerPropKeys, listPickerPropTypes } from '../Picker';
var defaultProps = {
  as: 'div',
  classPrefix: 'picker',
  cleanable: true,
  placement: 'bottomStart',
  appearance: 'default',
  data: [],
  disabledItemValues: [],
  childrenKey: 'children',
  valueKey: 'value',
  labelKey: 'label',
  searchable: true
};
var Cascader = /*#__PURE__*/React.forwardRef(function (props, ref) {
  var Component = props.as,
      data = props.data,
      classPrefix = props.classPrefix,
      childrenKey = props.childrenKey,
      valueKey = props.valueKey,
      labelKey = props.labelKey,
      defaultValue = props.defaultValue,
      placeholder = props.placeholder,
      disabled = props.disabled,
      disabledItemValues = props.disabledItemValues,
      cleanable = props.cleanable,
      overrideLocale = props.locale,
      toggleAs = props.toggleAs,
      style = props.style,
      valueProp = props.value,
      inline = props.inline,
      menuClassName = props.menuClassName,
      menuStyle = props.menuStyle,
      menuWidth = props.menuWidth,
      menuHeight = props.menuHeight,
      searchable = props.searchable,
      parentSelectable = props.parentSelectable,
      placement = props.placement,
      id = props.id,
      renderMenuItem = props.renderMenuItem,
      renderValue = props.renderValue,
      renderMenu = props.renderMenu,
      renderExtraFooter = props.renderExtraFooter,
      onEnter = props.onEnter,
      onExited = props.onExited,
      onClean = props.onClean,
      onChange = props.onChange,
      onSelect = props.onSelect,
      onSearch = props.onSearch,
      onClose = props.onClose,
      onOpen = props.onOpen,
      getChildren = props.getChildren,
      rest = _objectWithoutPropertiesLoose(props, ["as", "data", "classPrefix", "childrenKey", "valueKey", "labelKey", "defaultValue", "placeholder", "disabled", "disabledItemValues", "cleanable", "locale", "toggleAs", "style", "value", "inline", "menuClassName", "menuStyle", "menuWidth", "menuHeight", "searchable", "parentSelectable", "placement", "id", "renderMenuItem", "renderValue", "renderMenu", "renderExtraFooter", "onEnter", "onExited", "onClean", "onChange", "onSelect", "onSearch", "onClose", "onOpen", "getChildren"]); // Use component active state to support keyboard events.


  var _useState = useState(false),
      active = _useState[0],
      setActive = _useState[1];

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

  var triggerRef = useRef();
  var overlayRef = useRef();
  var targetRef = useRef();

  var _useControlled = useControlled(valueProp, defaultValue),
      value = _useControlled[0],
      setValue = _useControlled[1];

  var _usePaths = usePaths({
    data: data,
    valueKey: valueKey,
    childrenKey: childrenKey,
    value: value
  }),
      selectedPaths = _usePaths.selectedPaths,
      valueToPaths = _usePaths.valueToPaths,
      columnData = _usePaths.columnData,
      addColumn = _usePaths.addColumn,
      setValueToPaths = _usePaths.setValueToPaths,
      setColumnData = _usePaths.setColumnData,
      setSelectedPaths = _usePaths.setSelectedPaths,
      enforceUpdate = _usePaths.enforceUpdate;

  useEffect(function () {
    setFlattenData(flattenTree(data));
  }, [data]);
  usePublicMethods(ref, {
    triggerRef: triggerRef,
    overlayRef: overlayRef,
    targetRef: targetRef
  });

  var _useCustom = useCustom('Picker', overrideLocale),
      locale = _useCustom.locale,
      rtl = _useCustom.rtl;
  /**
   * 1.Have a value and the value is valid.
   * 2.Regardless of whether the value is valid, as long as renderValue is set, it is judged to have a value.
   */


  var hasValue = valueToPaths.length > 0 || !isNil(value) && isFunction(renderValue);

  var _useClassNames = useClassNames(classPrefix),
      prefix = _useClassNames.prefix,
      merge = _useClassNames.merge;

  var _useState3 = useState(''),
      searchKeyword = _useState3[0],
      setSearchKeyword = _useState3[1];

  var someKeyword = useCallback(function (item, keyword) {
    if (item[labelKey].match(new RegExp(getSafeRegExpString(keyword || searchKeyword), 'i'))) {
      return true;
    }

    if (item.parent && someKeyword(item.parent)) {
      return true;
    }

    return false;
  }, [labelKey, searchKeyword]);
  var getSearchResult = useCallback(function (keyword) {
    var items = [];
    var result = flattenData.filter(function (item) {
      if (item[childrenKey]) {
        return false;
      }

      return someKeyword(item, keyword);
    });

    for (var i = 0; i < result.length; i++) {
      items.push(result[i]); // A maximum of 100 search results are returned.

      if (i === 99) {
        return items;
      }
    }

    return items;
  }, [childrenKey, flattenData, someKeyword]); // Used to hover the focuse item  when trigger `onKeydown`

  var _useFocusItemValue = useFocusItemValue(value, {
    rtl: rtl,
    data: flattenData,
    valueKey: valueKey,
    defaultLayer: (valueToPaths === null || valueToPaths === void 0 ? void 0 : valueToPaths.length) ? valueToPaths.length - 1 : 0,
    target: function target() {
      return overlayRef.current;
    },
    callback: useCallback(function (value) {
      enforceUpdate(value, true);
    }, [enforceUpdate])
  }),
      focusItemValue = _useFocusItemValue.focusItemValue,
      setFocusItemValue = _useFocusItemValue.setFocusItemValue,
      setLayer = _useFocusItemValue.setLayer,
      setKeys = _useFocusItemValue.setKeys,
      onFocusItem = _useFocusItemValue.onKeyDown;

  var handleSearch = useCallback(function (value, event) {
    var items = getSearchResult(value);
    setSearchKeyword(value);
    onSearch === null || onSearch === void 0 ? void 0 : onSearch(value, event);

    if (items === null || items === void 0 ? void 0 : items[0]) {
      setFocusItemValue(items === null || items === void 0 ? void 0 : items[0]);
      setLayer(0);
      setKeys([]);
    }
  }, [getSearchResult, onSearch, setFocusItemValue, setKeys, setLayer]);
  var handleEntered = useCallback(function () {
    onOpen === null || onOpen === void 0 ? void 0 : onOpen();
    setActive(true);
  }, [onOpen]);
  var handleExited = useCallback(function () {
    onClose === null || onClose === void 0 ? void 0 : onClose();
    setActive(false);
    setSearchKeyword('');
  }, [onClose]);
  var handleClose = useCallback(function () {
    var _triggerRef$current;

    (_triggerRef$current = triggerRef.current) === null || _triggerRef$current === void 0 ? void 0 : _triggerRef$current.close();
  }, [triggerRef]);
  var handleClean = useCallback(function (event) {
    if (disabled) {
      return;
    }

    setColumnData([data]);
    setValue(null);
    setSelectedPaths([]);
    setValueToPaths([]);
    onChange === null || onChange === void 0 ? void 0 : onChange(null, event);
  }, [data, disabled, onChange, setSelectedPaths, setColumnData, setValueToPaths, setValue]);
  var handleMenuPressEnter = useCallback(function (event) {
    var focusItem = findNodeOfTree(data, function (item) {
      return item[valueKey] === focusItemValue;
    });
    var isLeafNode = focusItem && !focusItem[childrenKey];

    if (isLeafNode) {
      setValue(focusItemValue);
      setValueToPaths(selectedPaths);

      if (selectedPaths.length) {
        setLayer(selectedPaths.length - 1);
      }

      if (!shallowEqual(value, focusItemValue)) {
        onChange === null || onChange === void 0 ? void 0 : onChange(focusItemValue, event);
      }

      handleClose();
    }
  }, [childrenKey, data, focusItemValue, handleClose, onChange, selectedPaths, setLayer, setValue, setValueToPaths, value, valueKey]);
  var onPickerKeyDown = useToggleKeyDownEvent(_extends({
    toggle: !focusItemValue || !active,
    triggerRef: triggerRef,
    targetRef: targetRef,
    overlayRef: overlayRef,
    active: active,
    onExit: handleClean,
    onMenuKeyDown: onFocusItem,
    onMenuPressEnter: handleMenuPressEnter
  }, rest));

  var handleSelect = function handleSelect(node, cascadeData, cascadePaths, isLeafNode, event) {
    var _node$children, _triggerRef$current2;

    var nextValue = node[valueKey];
    onSelect === null || onSelect === void 0 ? void 0 : onSelect(node, cascadePaths, event);
    setSelectedPaths(cascadePaths);
    setColumnData(cascadeData); // Lazy load node's children

    if (typeof getChildren === 'function' && ((_node$children = node.children) === null || _node$children === void 0 ? void 0 : _node$children.length) === 0) {
      node.loading = true;
      var children = getChildren(node);

      if (children instanceof Promise) {
        children.then(function (data) {
          node.loading = false;
          node[childrenKey] = data;
          addColumn(data, cascadePaths.length);
        });
      } else {
        node.loading = false;
        node[childrenKey] = children;
        addColumn(children, cascadePaths.length);
      }
    }

    if (isLeafNode) {
      // Determines whether the option is a leaf node, and if so, closes the picker.
      handleClose(); // Update the selected path to the value path.
      // That is, the selected path will be displayed on the button after clicking the child node.

      setValueToPaths(cascadePaths);
      setValue(nextValue);

      if (!shallowEqual(value, nextValue)) {
        onChange === null || onChange === void 0 ? void 0 : onChange(nextValue, event);
      }

      return;
    }
    /** When the parent is optional, the value and the displayed path are updated. */


    if (parentSelectable && !shallowEqual(value, nextValue)) {
      setValue(nextValue);
      onChange === null || onChange === void 0 ? void 0 : onChange(nextValue, event);
      setValueToPaths(cascadePaths);
    } // Update menu position


    (_triggerRef$current2 = triggerRef.current) === null || _triggerRef$current2 === void 0 ? void 0 : _triggerRef$current2.updatePosition();
  };
  /**
   * The search structure option is processed after being selected.
   */


  var handleSearchRowSelect = function handleSearchRowSelect(node, nodes, event) {
    var nextValue = node[valueKey];
    handleClose();
    setSearchKeyword('');
    setValue(nextValue);
    setValueToPaths(nodes);
    enforceUpdate(nextValue);
    onSelect === null || onSelect === void 0 ? void 0 : onSelect(node, null, event);
    onChange === null || onChange === void 0 ? void 0 : onChange(nextValue, event);
  };

  var renderSearchRow = function renderSearchRow(item, key) {
    var regx = new RegExp(getSafeRegExpString(searchKeyword), 'ig');
    var nodes = getNodeParents(item);
    nodes.push(item);
    var formattedNodes = nodes.map(function (node) {
      var _extends2;

      var labelElements = [];
      var a = node[labelKey].split(regx);
      var b = node[labelKey].match(regx);

      for (var i = 0; i < a.length; i++) {
        labelElements.push(a[i]);

        if (b && b[i]) {
          labelElements.push( /*#__PURE__*/React.createElement("strong", {
            key: i
          }, b[i]));
        }
      }

      return _extends({}, node, (_extends2 = {}, _extends2[labelKey] = labelElements, _extends2));
    });
    var disabled = disabledItemValues.some(function (value) {
      return formattedNodes.some(function (node) {
        return node[valueKey] === value;
      });
    });
    var itemClasses = prefix('cascader-row', {
      'cascader-row-disabled': disabled,
      'cascader-row-focus': item[valueKey] === focusItemValue
    });
    return /*#__PURE__*/React.createElement("div", {
      key: key,
      "aria-disabled": disabled,
      "data-key": item[valueKey],
      className: itemClasses,
      onClick: function onClick(event) {
        if (!disabled) {
          handleSearchRowSelect(item, nodes, event);
        }
      }
    }, formattedNodes.map(function (node, index) {
      return /*#__PURE__*/React.createElement("span", {
        key: "col-" + index,
        className: prefix('cascader-col')
      }, node[labelKey]);
    }));
  };

  var renderSearchResultPanel = function renderSearchResultPanel() {
    if (searchKeyword === '') {
      return null;
    }

    var items = getSearchResult();
    return /*#__PURE__*/React.createElement("div", {
      className: prefix('cascader-search-panel'),
      "data-layer": 0
    }, items.length ? items.map(renderSearchRow) : /*#__PURE__*/React.createElement("div", {
      className: prefix('none')
    }, locale.noResultsText));
  };

  var renderDropdownMenu = function renderDropdownMenu(positionProps, speakerRef) {
    var _ref = positionProps || {},
        left = _ref.left,
        top = _ref.top,
        className = _ref.className;

    var styles = _extends({}, menuStyle, {
      left: left,
      top: top
    });

    var classes = merge(className, menuClassName, prefix('cascader-menu', {
      inline: inline
    }));
    return /*#__PURE__*/React.createElement(PickerOverlay, {
      ref: mergeRefs(overlayRef, speakerRef),
      className: classes,
      style: styles,
      target: triggerRef,
      onKeyDown: onPickerKeyDown
    }, searchable && /*#__PURE__*/React.createElement(SearchBar, {
      placeholder: locale === null || locale === void 0 ? void 0 : locale.searchPlaceholder,
      onChange: handleSearch,
      value: searchKeyword
    }), renderSearchResultPanel(), searchKeyword === '' && /*#__PURE__*/React.createElement(DropdownMenu, {
      id: id ? id + "-listbox" : undefined,
      menuWidth: menuWidth,
      menuHeight: menuHeight,
      disabledItemValues: disabledItemValues,
      valueKey: valueKey,
      labelKey: labelKey,
      childrenKey: childrenKey,
      classPrefix: 'picker-cascader-menu',
      cascadeData: columnData,
      cascadePaths: selectedPaths,
      activeItemValue: value,
      loadingText: locale === null || locale === void 0 ? void 0 : locale.loading,
      onSelect: handleSelect,
      renderMenu: renderMenu,
      renderMenuItem: renderMenuItem
    }), renderExtraFooter === null || renderExtraFooter === void 0 ? void 0 : renderExtraFooter());
  };

  var selectedElement = placeholder;

  if (valueToPaths.length > 0) {
    selectedElement = [];
    valueToPaths.forEach(function (item, index) {
      var key = item[valueKey] || item[labelKey];
      selectedElement.push( /*#__PURE__*/React.createElement("span", {
        key: key
      }, item[labelKey]));

      if (index < valueToPaths.length - 1) {
        selectedElement.push( /*#__PURE__*/React.createElement("span", {
          className: "separator",
          key: key + "-separator"
        }, ' / '));
      }
    });
  }

  if (!isNil(value) && isFunction(renderValue)) {
    selectedElement = renderValue(value, valueToPaths, selectedElement); // If renderValue returns null or undefined, hasValue is false.

    if (isNil(selectedElement)) {
      hasValue = false;
    }
  }

  var _usePickerClassName = usePickerClassName(_extends({}, props, {
    hasValue: hasValue,
    name: 'cascader'
  })),
      classes = _usePickerClassName[0],
      usedClassNamePropKeys = _usePickerClassName[1];

  if (inline) {
    return renderDropdownMenu();
  }

  return /*#__PURE__*/React.createElement(PickerToggleTrigger, {
    pickerProps: pick(props, pickTriggerPropKeys),
    ref: triggerRef,
    placement: placement,
    onEntered: createChainedFunction(handleEntered, onEnter),
    onExited: createChainedFunction(handleExited, onExited),
    speaker: renderDropdownMenu
  }, /*#__PURE__*/React.createElement(Component, {
    className: classes,
    style: style
  }, /*#__PURE__*/React.createElement(PickerToggle, _extends({}, omit(rest, [].concat(omitTriggerPropKeys, usedClassNamePropKeys)), {
    id: id,
    ref: targetRef,
    as: toggleAs,
    disabled: disabled,
    onClean: createChainedFunction(handleClean, onClean),
    onKeyDown: onPickerKeyDown,
    cleanable: cleanable && !disabled,
    hasValue: hasValue,
    active: active,
    placement: placement,
    inputValue: value
  }), selectedElement || (locale === null || locale === void 0 ? void 0 : locale.placeholder))));
});
Cascader.displayName = 'Cascader';
Cascader.defaultProps = defaultProps;
Cascader.propTypes = _extends({}, listPickerPropTypes, {
  disabledItemValues: PropTypes.array,
  locale: PropTypes.any,
  appearance: PropTypes.oneOf(['default', 'subtle']),
  renderMenu: PropTypes.func,
  onSelect: PropTypes.func,
  onSearch: PropTypes.func,
  cleanable: PropTypes.bool,
  renderMenuItem: PropTypes.func,
  menuWidth: PropTypes.number,
  menuHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  searchable: PropTypes.bool,
  inline: PropTypes.bool,
  parentSelectable: PropTypes.bool
});
export default Cascader;