import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _typeof from "@babel/runtime/helpers/esm/typeof";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import { createVNode as _createVNode, resolveDirective as _resolveDirective } from "vue";
import generateSelector from '../vc-select/generate';
import TreeNode from './TreeNode';
import { flattenOptions, filterOptions, isValueDisabled, findValueOption, addValue, removeValue, getRawValueLabeled, toArray, fillFieldNames } from './utils/valueUtil';
import warningProps from './utils/warningPropsUtil';
import { SelectContext } from './Context';
import useTreeData from './hooks/useTreeData';
import useKeyValueMap from './hooks/useKeyValueMap';
import useKeyValueMapping from './hooks/useKeyValueMapping';
import { formatStrategyKeys, SHOW_ALL, SHOW_PARENT, SHOW_CHILD } from './utils/strategyUtil';
import { fillAdditionalInfo } from './utils/legacyUtil';
import useSelectValues from './hooks/useSelectValues';
import { treeSelectProps } from './props';
import { getLabeledValue } from '../vc-select/utils/valueUtil';
import omit from '../_util/omit';
import { computed, defineComponent, ref, shallowRef, toRef, watch, watchEffect } from 'vue';
import { convertDataToEntities } from '../vc-tree/utils/treeUtil';
import { conductCheck } from '../vc-tree/utils/conductUtil';
import { warning } from '../vc-util/warning';
import { INTERNAL_PROPS_MARK } from '../vc-select/interface/generator';
var OMIT_PROPS = ['expandedKeys', 'treeData', 'treeCheckable', 'showCheckedStrategy', 'searchPlaceholder', 'treeLine', 'treeIcon', 'showTreeIcon', 'switcherIcon', 'treeNodeFilterProp', 'filterTreeNode', 'dropdownPopupAlign', 'treeDefaultExpandAll', 'treeCheckStrictly', 'treeExpandedKeys', 'treeLoadedKeys', 'treeMotion', 'onTreeExpand', 'onTreeLoad', 'labelRender', 'loadData', 'treeDataSimpleMode', 'treeNodeLabelProp', 'treeDefaultExpandedKeys', 'bordered'];
export default function generate(config) {
  var prefixCls = config.prefixCls,
      optionList = config.optionList;
  var RefSelect = generateSelector({
    prefixCls: prefixCls,
    components: {
      optionList: optionList
    },
    // Not use generate since we will handle ourself
    convertChildrenToData: function convertChildrenToData() {
      return null;
    },
    flattenOptions: flattenOptions,
    // Handle `optionLabelProp` in TreeSelect component
    getLabeledValue: getLabeledValue,
    filterOptions: filterOptions,
    isValueDisabled: isValueDisabled,
    findValueOption: findValueOption,
    omitDOMProps: function omitDOMProps(props) {
      return omit(props, OMIT_PROPS);
    }
  });
  return defineComponent({
    name: 'TreeSelect',
    props: treeSelectProps(),
    slots: ['title', 'placeholder', 'maxTagPlaceholder', 'treeIcon', 'switcherIcon', 'notFoundContent', 'treeCheckable'],
    TreeNode: TreeNode,
    SHOW_ALL: SHOW_ALL,
    SHOW_PARENT: SHOW_PARENT,
    SHOW_CHILD: SHOW_CHILD,
    setup: function setup(props, _ref) {
      var expose = _ref.expose,
          slots = _ref.slots,
          attrs = _ref.attrs;
      var mergedCheckable = computed(function () {
        return props.treeCheckable || props.treeCheckStrictly;
      });
      var mergedMultiple = computed(function () {
        return props.multiple || mergedCheckable.value;
      });
      var treeConduction = computed(function () {
        return props.treeCheckable && !props.treeCheckStrictly;
      });
      var mergedLabelInValue = computed(function () {
        return props.treeCheckStrictly || props.labelInValue;
      }); // ======================= Tree Data =======================
      // FieldNames

      var mergedFieldNames = computed(function () {
        return fillFieldNames(props.fieldNames, true);
      }); // Legacy both support `label` or `title` if not set.
      // We have to fallback to function to handle this

      var getTreeNodeTitle = function getTreeNodeTitle(node) {
        var _mergedFieldNames$val;

        if (!props.treeData) {
          return node.title;
        }

        if ((_mergedFieldNames$val = mergedFieldNames.value) !== null && _mergedFieldNames$val !== void 0 && _mergedFieldNames$val.label) {
          return node[mergedFieldNames.value.label];
        }

        return node.label || node.title;
      };

      var getTreeNodeLabelProp = function getTreeNodeLabelProp(entity) {
        var labelRender = props.labelRender,
            treeNodeLabelProp = props.treeNodeLabelProp;
        var node = entity.data.node;

        if (labelRender) {
          return labelRender(entity);
        }

        if (treeNodeLabelProp) {
          return node[treeNodeLabelProp];
        }

        return getTreeNodeTitle(node);
      };

      var mergedTreeData = useTreeData(toRef(props, 'treeData'), toRef(props, 'children'), {
        getLabelProp: getTreeNodeTitle,
        simpleMode: toRef(props, 'treeDataSimpleMode'),
        fieldNames: mergedFieldNames
      });
      var flattedOptions = computed(function () {
        return flattenOptions(mergedTreeData.value);
      });

      var _useKeyValueMap = useKeyValueMap(flattedOptions),
          _useKeyValueMap2 = _slicedToArray(_useKeyValueMap, 2),
          cacheKeyMap = _useKeyValueMap2[0],
          cacheValueMap = _useKeyValueMap2[1];

      var _useKeyValueMapping = useKeyValueMapping(cacheKeyMap, cacheValueMap),
          _useKeyValueMapping2 = _slicedToArray(_useKeyValueMapping, 2),
          getEntityByKey = _useKeyValueMapping2[0],
          getEntityByValue = _useKeyValueMapping2[1]; // Only generate keyEntities for check conduction when is `treeCheckable`


      var conductKeyEntities = computed(function () {
        if (treeConduction.value) {
          return convertDataToEntities(mergedTreeData.value).keyEntities;
        }

        return null;
      }); // ========================== Ref ==========================

      var selectRef = ref();
      expose({
        scrollTo: function scrollTo() {
          var _selectRef$value$scro, _selectRef$value;

          for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
            args[_key] = arguments[_key];
          }

          return (_selectRef$value$scro = (_selectRef$value = selectRef.value).scrollTo) === null || _selectRef$value$scro === void 0 ? void 0 : _selectRef$value$scro.call.apply(_selectRef$value$scro, [_selectRef$value].concat(args));
        },
        focus: function focus() {
          var _selectRef$value$focu, _selectRef$value2;

          return (_selectRef$value$focu = (_selectRef$value2 = selectRef.value).focus) === null || _selectRef$value$focu === void 0 ? void 0 : _selectRef$value$focu.call(_selectRef$value2);
        },
        blur: function blur() {
          var _selectRef$value3;

          return (_selectRef$value3 = selectRef.value) === null || _selectRef$value3 === void 0 ? void 0 : _selectRef$value3.blur();
        },

        /** @private Internal usage. It's save to remove if `rc-cascader` not use it any longer */
        getEntityByValue: getEntityByValue
      });
      var valueRef = ref(props.defaultValue);
      watch(function () {
        return props.value;
      }, function () {
        if (props.value !== undefined) {
          valueRef.value = props.value;
        }
      }, {
        immediate: true
      });
      /** Get `missingRawValues` which not exist in the tree yet */

      var splitRawValues = function splitRawValues(newRawValues) {
        var missingRawValues = [];
        var existRawValues = []; // Keep missing value in the cache

        newRawValues.forEach(function (val) {
          if (getEntityByValue(val)) {
            existRawValues.push(val);
          } else {
            missingRawValues.push(val);
          }
        });
        return {
          missingRawValues: missingRawValues,
          existRawValues: existRawValues
        };
      };

      var rawValues = shallowRef([]);
      var rawHalfCheckedKeys = shallowRef([]);
      watchEffect(function () {
        var valueHalfCheckedKeys = [];
        var newRawValues = [];
        toArray(valueRef.value).forEach(function (item) {
          if (item && _typeof(item) === 'object' && 'value' in item) {
            if (item.halfChecked && props.treeCheckStrictly) {
              var entity = getEntityByValue(item.value);
              valueHalfCheckedKeys.push(entity ? entity.key : item.value);
            } else {
              newRawValues.push(item.value);
            }
          } else {
            newRawValues.push(item);
          }
        }); // We need do conduction of values

        if (treeConduction.value) {
          var _splitRawValues = splitRawValues(newRawValues),
              missingRawValues = _splitRawValues.missingRawValues,
              existRawValues = _splitRawValues.existRawValues;

          var keyList = existRawValues.map(function (val) {
            return getEntityByValue(val).key;
          });

          var _conductCheck = conductCheck(keyList, true, conductKeyEntities.value),
              checkedKeys = _conductCheck.checkedKeys,
              halfCheckedKeys = _conductCheck.halfCheckedKeys;

          rawValues.value = [].concat(_toConsumableArray(missingRawValues), _toConsumableArray(checkedKeys.map(function (key) {
            return getEntityByKey(key).data.value;
          })));
          rawHalfCheckedKeys.value = halfCheckedKeys;
        } else {
          var _ref2 = [newRawValues, valueHalfCheckedKeys];
          rawValues.value = _ref2[0];
          rawHalfCheckedKeys.value = _ref2[1];
        }
      });
      var selectValues = useSelectValues(rawValues, {
        treeConduction: treeConduction,
        value: valueRef,
        showCheckedStrategy: toRef(props, 'showCheckedStrategy'),
        conductKeyEntities: conductKeyEntities,
        getEntityByValue: getEntityByValue,
        getEntityByKey: getEntityByKey,
        getLabelProp: getTreeNodeLabelProp
      });

      var triggerChange = function triggerChange(newRawValues, extra, source) {
        var onChange = props.onChange,
            showCheckedStrategy = props.showCheckedStrategy,
            treeCheckStrictly = props.treeCheckStrictly;
        var preValue = valueRef.value;
        valueRef.value = mergedMultiple.value ? newRawValues : newRawValues[0];

        if (onChange) {
          var eventValues = newRawValues;

          if (treeConduction.value && showCheckedStrategy !== 'SHOW_ALL') {
            var keyList = newRawValues.map(function (val) {
              var entity = getEntityByValue(val);
              return entity ? entity.key : val;
            });
            var formattedKeyList = formatStrategyKeys(keyList, showCheckedStrategy, conductKeyEntities.value);
            eventValues = formattedKeyList.map(function (key) {
              var entity = getEntityByKey(key);
              return entity ? entity.data.value : key;
            });
          }

          var _ref3 = extra || {
            triggerValue: undefined,
            selected: undefined
          },
              triggerValue = _ref3.triggerValue,
              selected = _ref3.selected;

          var returnValues = mergedLabelInValue.value ? getRawValueLabeled(eventValues, preValue, getEntityByValue, getTreeNodeLabelProp) : eventValues; // We need fill half check back

          if (treeCheckStrictly) {
            var halfValues = rawHalfCheckedKeys.value.map(function (key) {
              var entity = getEntityByKey(key);
              return entity ? entity.data.value : key;
            }).filter(function (val) {
              return !eventValues.includes(val);
            });
            returnValues = [].concat(_toConsumableArray(returnValues), _toConsumableArray(getRawValueLabeled(halfValues, preValue, getEntityByValue, getTreeNodeLabelProp)));
          }

          var additionalInfo = {
            // [Legacy] Always return as array contains label & value
            preValue: selectValues.value,
            triggerValue: triggerValue
          }; // [Legacy] Fill legacy data if user query.
          // This is expansive that we only fill when user query
          // https://github.com/react-component/tree-select/blob/fe33eb7c27830c9ac70cd1fdb1ebbe7bc679c16a/src/Select.jsx

          var showPosition = true;

          if (treeCheckStrictly || source === 'selection' && !selected) {
            showPosition = false;
          }

          fillAdditionalInfo(additionalInfo, triggerValue, newRawValues, mergedTreeData.value, showPosition);

          if (mergedCheckable.value) {
            additionalInfo.checked = selected;
          } else {
            additionalInfo.selected = selected;
          }

          onChange(mergedMultiple.value ? returnValues : returnValues[0], mergedLabelInValue.value ? null : eventValues.map(function (val) {
            var entity = getEntityByValue(val);
            return entity ? entity.data.title : null;
          }), additionalInfo);
        }
      };

      var onInternalSelect = function onInternalSelect(selectValue, option, source) {
        var _props$onSelect;

        var eventValue = mergedLabelInValue.value ? selectValue : selectValue;

        if (!mergedMultiple.value) {
          // Single mode always set value
          triggerChange([selectValue], {
            selected: true,
            triggerValue: selectValue
          }, source);
        } else {
          var newRawValues = addValue(rawValues.value, selectValue); // Add keys if tree conduction

          if (treeConduction.value) {
            // Should keep missing values
            var _splitRawValues2 = splitRawValues(newRawValues),
                missingRawValues = _splitRawValues2.missingRawValues,
                existRawValues = _splitRawValues2.existRawValues;

            var keyList = existRawValues.map(function (val) {
              return getEntityByValue(val).key;
            });

            var _conductCheck2 = conductCheck(keyList, true, conductKeyEntities.value),
                checkedKeys = _conductCheck2.checkedKeys;

            newRawValues = [].concat(_toConsumableArray(missingRawValues), _toConsumableArray(checkedKeys.map(function (key) {
              return getEntityByKey(key).data.value;
            })));
          }

          triggerChange(newRawValues, {
            selected: true,
            triggerValue: selectValue
          }, source);
        }

        (_props$onSelect = props.onSelect) === null || _props$onSelect === void 0 ? void 0 : _props$onSelect.call(props, eventValue, option);
      };

      var onInternalDeselect = function onInternalDeselect(selectValue, option, source) {
        var _props$onDeselect;

        var eventValue = selectValue;
        var newRawValues = removeValue(rawValues.value, selectValue); // Remove keys if tree conduction

        if (treeConduction.value) {
          var _splitRawValues3 = splitRawValues(newRawValues),
              missingRawValues = _splitRawValues3.missingRawValues,
              existRawValues = _splitRawValues3.existRawValues;

          var keyList = existRawValues.map(function (val) {
            return getEntityByValue(val).key;
          });

          var _conductCheck3 = conductCheck(keyList, {
            checked: false,
            halfCheckedKeys: rawHalfCheckedKeys.value
          }, conductKeyEntities.value),
              checkedKeys = _conductCheck3.checkedKeys;

          newRawValues = [].concat(_toConsumableArray(missingRawValues), _toConsumableArray(checkedKeys.map(function (key) {
            return getEntityByKey(key).data.value;
          })));
        }

        triggerChange(newRawValues, {
          selected: false,
          triggerValue: selectValue
        }, source);
        (_props$onDeselect = props.onDeselect) === null || _props$onDeselect === void 0 ? void 0 : _props$onDeselect.call(props, eventValue, option);
      };

      var onInternalClear = function onInternalClear() {
        triggerChange([], null, 'clear');
      }; // ========================= Open ==========================


      var onInternalDropdownVisibleChange = function onInternalDropdownVisibleChange(open) {
        if (props.onDropdownVisibleChange) {
          var legacyParam = {};
          Object.defineProperty(legacyParam, 'documentClickClose', {
            get: function get() {
              warning(false, 'Second param of `onDropdownVisibleChange` has been removed.');
              return false;
            }
          });
          props.onDropdownVisibleChange(open, legacyParam);
        }
      }; // ======================== Warning ========================


      if (process.env.NODE_ENV !== 'production') {
        warningProps(props);
      }

      return function () {
        var treeNodeFilterProp = props.treeNodeFilterProp,
            dropdownPopupAlign = props.dropdownPopupAlign,
            filterTreeNode = props.filterTreeNode,
            treeDefaultExpandAll = props.treeDefaultExpandAll,
            treeExpandedKeys = props.treeExpandedKeys,
            treeDefaultExpandedKeys = props.treeDefaultExpandedKeys,
            onTreeExpand = props.onTreeExpand,
            treeIcon = props.treeIcon,
            treeMotion = props.treeMotion,
            showTreeIcon = props.showTreeIcon,
            switcherIcon = props.switcherIcon,
            treeLine = props.treeLine,
            loadData = props.loadData,
            treeLoadedKeys = props.treeLoadedKeys,
            onTreeLoad = props.onTreeLoad; // ======================== Render =========================
        // We pass some props into select props style

        var selectProps = {
          optionLabelProp: null,
          optionFilterProp: treeNodeFilterProp,
          dropdownAlign: dropdownPopupAlign,
          internalProps: {
            mark: INTERNAL_PROPS_MARK,
            onClear: onInternalClear,
            skipTriggerChange: true,
            skipTriggerSelect: true,
            onRawSelect: onInternalSelect,
            onRawDeselect: onInternalDeselect
          },
          filterOption: filterTreeNode
        };

        if (props.filterTreeNode === undefined) {
          delete selectProps.filterOption;
        }

        var selectContext = {
          checkable: mergedCheckable.value,
          loadData: loadData,
          treeLoadedKeys: treeLoadedKeys,
          onTreeLoad: onTreeLoad,
          checkedKeys: rawValues.value,
          halfCheckedKeys: rawHalfCheckedKeys.value,
          treeDefaultExpandAll: treeDefaultExpandAll,
          treeExpandedKeys: treeExpandedKeys,
          treeDefaultExpandedKeys: treeDefaultExpandedKeys,
          onTreeExpand: onTreeExpand,
          treeIcon: treeIcon,
          treeMotion: treeMotion,
          showTreeIcon: showTreeIcon,
          switcherIcon: switcherIcon,
          treeLine: treeLine,
          treeNodeFilterProp: treeNodeFilterProp,
          getEntityByKey: getEntityByKey,
          getEntityByValue: getEntityByValue,
          customCheckable: slots.treeCheckable,
          slots: slots
        };
        return _createVNode(SelectContext, {
          "value": selectContext
        }, {
          default: function _default() {
            return [_createVNode(RefSelect, _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, attrs), {}, {
              "ref": selectRef,
              "mode": mergedMultiple.value ? 'multiple' : null
            }, props), selectProps), {}, {
              "value": selectValues.value,
              "labelInValue": true,
              "options": mergedTreeData.value,
              "onChange": null,
              "onSelect": null,
              "onDeselect": null,
              "onDropdownVisibleChange": onInternalDropdownVisibleChange
            }), slots)];
          }
        });
      };
    }
  });
}