import { defineComponent, computed, ref, watch, unref, provide, resolveComponent, openBlock, createElementBlock, normalizeClass, createVNode, withCtx, normalizeStyle, withKeys, withModifiers, renderSlot, createCommentVNode, createTextVNode, toDisplayString, createElementVNode } from 'vue';
import { isNil } from 'lodash-es';
import { useTheme } from '../_theme/useTheme';
import { useArrayModel, useNormalModel } from '../_util/use/useModel';
import { UPDATE_MODEL_EVENT, CHANGE_EVENT } from '../_util/constants';
import useFormAdaptor from '../_util/use/useFormAdaptor';
import Popper from '../popper';
import SelectTrigger from '../select-trigger';
import { useLocale } from '../config-provider/useLocale';
import { SELECT_PROVIDE_KEY, prefixCls } from './const';
import OptionList from './optionList';
import { selectProps } from './props';
import useOptions from './useOptions';

var script = defineComponent({
  name: 'FSelect',
  components: {
    Popper,
    SelectTrigger,
    OptionList
  },
  props: selectProps,
  emits: [UPDATE_MODEL_EVENT, CHANGE_EVENT, 'removeTag', 'visibleChange', 'focus', 'blur', 'clear', 'scroll', 'search', 'filter'],
  setup(props, _ref) {
    let {
      emit
    } = _ref;
    useTheme();
    const {
      validate,
      isError,
      isFormDisabled
    } = useFormAdaptor({
      valueType: computed(() => props.multiple ? 'array' : 'string')
    });
    const innerDisabled = computed(() => props.disabled === true || isFormDisabled.value);
    const isOpenedRef = ref(false);
    // 与 props 中 modelValue 类型保持一致
    const [currentValue, updateCurrentValue] = props.multiple ? useArrayModel(props, emit) : useNormalModel(props, emit);
    const triggerRef = ref();
    const triggerWidth = ref(0);
    const filterText = ref('');
    const cacheOptions = ref([]);
    watch(isOpenedRef, () => {
      emit('visibleChange', unref(isOpenedRef));
      // trigger 在mounted 之后可能会改变
      if (isOpenedRef.value && triggerRef.value) {
        triggerWidth.value = triggerRef.value.$el.offsetWidth;
      }
    });
    const handleChange = () => {
      emit(CHANGE_EVENT, unref(currentValue));
      validate(CHANGE_EVENT);
    };
    const handleClear = () => {
      const value = props.multiple ? [] : null;
      if (props.multiple ? (currentValue.value || []).length : currentValue.value !== null) {
        updateCurrentValue(value);
        handleChange();
      }
      filterText.value = '';
      cacheOptions.value = [];
      emit('clear');
    };
    const {
      t
    } = useLocale();
    const inputPlaceholder = computed(() => props.placeholder || t('select.placeholder'));
    const listEmptyText = computed(() => props.emptyText || t('select.emptyText'));
    const {
      addOption,
      removeOption,
      flatBaseOptions
    } = useOptions({
      props
    });
    provide(SELECT_PROVIDE_KEY, {
      addOption,
      removeOption
    });

    // 自定义选项
    const cacheOptionsForTag = computed(() => {
      if (props.filterable && props.tag) {
        if (filterText.value && flatBaseOptions.value.every(option => {
          return option.label !== filterText.value;
        }) && cacheOptions.value.every(option => {
          return option.value !== filterText.value;
        })) {
          return [{
            value: filterText.value,
            label: filterText.value,
            __cache: true
          }, ...cacheOptions.value];
        }
        return cacheOptions.value;
      }
      return [];
    });
    const allOptions = computed(() => {
      return [...cacheOptionsForTag.value, ...flatBaseOptions.value];
    });
    const filteredOptions = computed(() => {
      if (!props.remote && props.filterable && filterText.value) {
        return allOptions.value.filter(option => {
          if (option.__isGroup) {
            return false;
          } else {
            if (props.filter) {
              return props.filter(filterText.value, option);
            }
            return String(option.label).includes(filterText.value);
          }
        });
      }
      return allOptions.value;
    });
    const isSelect = value => {
      const selectVal = unref(currentValue) || [];
      const optVal = unref(value);
      if (selectVal === null) {
        return false;
      }
      if (props.multiple) {
        return selectVal.includes(optVal);
      }
      return selectVal === optVal;
    };
    const isLimitRef = computed(() => {
      if (props.multiple) {
        const selectVal = unref(currentValue) || [];
        return props.multipleLimit > 0 && props.multipleLimit === selectVal.length;
      }
      return false;
    });
    const onSelect = (value, option) => {
      if (innerDisabled.value) {
        return;
      }
      if (props.multiple) {
        filterText.value = '';
        if (isSelect(value)) {
          emit('removeTag', value);
        } else {
          if (isLimitRef.value) {
            return;
          }
        }
      } else {
        // 体验更好
        setTimeout(() => {
          filterText.value = '';
        }, 400);
        isOpenedRef.value = false;
      }
      if (props.filterable && props.tag) {
        if (props.multiple) {
          if (isSelect(value)) {
            const index = cacheOptions.value.findIndex(option => {
              return option.value === value;
            });
            if (index !== -1) {
              cacheOptions.value.splice(index, 1);
            }
          } else {
            if (option !== null && option !== void 0 && option.__cache) {
              cacheOptions.value = [option, ...cacheOptions.value];
            }
          }
        } else {
          if (option !== null && option !== void 0 && option.__cache) {
            cacheOptions.value = [option];
          } else {
            cacheOptions.value = [];
          }
        }
      }
      updateCurrentValue(unref(value));
      handleChange();
    };

    // select-trigger 选择项展示，只在 currentValue 改变时才改变
    const selectedOptionsRef = ref([]);
    watch([currentValue, allOptions], _ref2 => {
      let [newValue, newOptions] = _ref2;
      const getOption = val => {
        let cacheOption;
        if (newOptions && newOptions.length) {
          cacheOption = newOptions.find(option => option.value === val);
          if (cacheOption) {
            return cacheOption;
          }
        }
        cacheOption = selectedOptionsRef.value.find(option => !option.__isGroup && option.value === val);
        if (cacheOption) {
          return cacheOption;
        }
        return val ? {
          value: val,
          label: null
        } : null;
      };
      if (!props.multiple) {
        const option = getOption(newValue);
        selectedOptionsRef.value = option ? [option] : [];
      } else {
        selectedOptionsRef.value = (newValue || []).map(value => {
          return getOption(value);
        }).filter(Boolean);
      }
    }, {
      immediate: true,
      deep: true
    });
    const focus = e => {
      emit('focus', e);
      validate('focus');
    };
    const blur = e => {
      if (isOpenedRef.value) {
        isOpenedRef.value = false;
      }
      emit('blur', e);
      validate('blur');
    };
    const handleFilterTextChange = (val, extraInfo) => {
      filterText.value = val;
      emit('filter', val);
      // blur 自动清的 inputText 不触发 search
      if (props.remote && !(extraInfo !== null && extraInfo !== void 0 && extraInfo.isClear)) {
        emit('search', val);
      }
    };
    const dropdownStyle = computed(() => {
      const style = {};
      if (triggerWidth.value) {
        style['min-width'] = `${triggerWidth.value}px`;
      }
      return style;
    });
    const onScroll = e => {
      emit('scroll', e);
    };
    const hoverOptionValue = ref();
    const onHover = option => {
      hoverOptionValue.value = option.value;
    };
    function getFirstOption() {
      const len = filteredOptions.value.length;
      if (len < 1) {
        return;
      }
      let index = 0;
      while (index < len) {
        if (!filteredOptions.value[index].__isGroup && !filteredOptions.value[index].disabled) {
          break;
        }
        index++;
      }
      if (index < len) {
        return filteredOptions.value[index];
      }
    }
    watch(isOpenedRef, () => {
      if (isOpenedRef.value) {
        if (props.multiple) {
          const currentSelectValues = currentValue.value || [];
          if (currentSelectValues.length > 0) {
            hoverOptionValue.value = currentSelectValues[0];
          }
        } else if (!isNil(currentValue.value)) {
          hoverOptionValue.value = currentValue.value;
        }
        const option = getFirstOption();
        if (isNil(hoverOptionValue.value) && option) {
          hoverOptionValue.value = option.value;
        }
      } else {
        hoverOptionValue.value = undefined;
      }
    });
    watch(filteredOptions, () => {
      const option = getFirstOption();
      if (isOpenedRef.value && option) {
        hoverOptionValue.value = option.value;
      }
    });
    const onKeyDown = () => {
      if (!isNil(hoverOptionValue.value)) {
        const option = allOptions.value.find(option => {
          return !option.__isGroup && option.value === hoverOptionValue.value;
        });
        onSelect(hoverOptionValue.value, option);
      }
    };
    const warnDeprecatedSlot = () => console.warn('[FSelect]: addon 插槽即将废弃，请使用 footer 插槽代替');
    return {
      prefixCls,
      isOpenedRef,
      currentValue,
      handleRemove: onSelect,
      handleClear,
      selectedOptionsRef,
      focus,
      blur,
      handleFilterTextChange,
      triggerRef,
      dropdownStyle,
      isSelect,
      onSelect,
      filteredOptions,
      listEmptyText,
      inputPlaceholder,
      isError,
      innerDisabled,
      onScroll,
      isLimitRef,
      hoverOptionValue,
      onHover,
      onKeyDown,
      warnDeprecatedSlot,
      filterText
    };
  }
});

function render(_ctx, _cache, $props, $setup, $data, $options) {
  const _component_SelectTrigger = resolveComponent("SelectTrigger");
  const _component_OptionList = resolveComponent("OptionList");
  const _component_Popper = resolveComponent("Popper");
  return openBlock(), createElementBlock("div", {
    class: normalizeClass(_ctx.prefixCls)
  }, [createVNode(_component_Popper, {
    modelValue: _ctx.isOpenedRef,
    "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => _ctx.isOpenedRef = $event),
    trigger: "click",
    placement: "bottom-start",
    onlyShowTrigger: _ctx.filterable || _ctx.remote,
    popperClass: [`${_ctx.prefixCls}-popper`, _ctx.popperClass],
    appendToContainer: _ctx.appendToContainer,
    getContainer: _ctx.getContainer,
    offset: 4,
    hideAfter: 0,
    disabled: _ctx.innerDisabled
  }, {
    trigger: withCtx(() => [createVNode(_component_SelectTrigger, {
      ref: "triggerRef",
      selectedOptions: _ctx.selectedOptionsRef,
      disabled: _ctx.innerDisabled,
      clearable: _ctx.clearable,
      isOpened: _ctx.isOpenedRef,
      multiple: _ctx.multiple,
      placeholder: _ctx.inputPlaceholder,
      filterable: _ctx.filterable || _ctx.remote,
      collapseTags: _ctx.collapseTags,
      collapseTagsLimit: _ctx.collapseTagsLimit,
      tagBordered: _ctx.tagBordered,
      class: normalizeClass([{
        'is-error': _ctx.isError
      }, _ctx.triggerClass]),
      style: normalizeStyle(_ctx.triggerStyle),
      renderTag: _ctx.$slots.tag,
      onKeydown: withKeys(_ctx.onKeyDown, ["enter"]),
      onRemove: _ctx.onSelect,
      onClear: _ctx.handleClear,
      onFocus: _ctx.focus,
      onBlur: _ctx.blur,
      onInput: _ctx.handleFilterTextChange
    }, null, 8 /* PROPS */, ["selectedOptions", "disabled", "clearable", "isOpened", "multiple", "placeholder", "filterable", "collapseTags", "collapseTagsLimit", "tagBordered", "class", "style", "renderTag", "onKeydown", "onRemove", "onClear", "onFocus", "onBlur", "onInput"])]),
    default: withCtx(() => [_ctx.$slots.header ? (openBlock(), createElementBlock("div", {
      key: 0,
      class: normalizeClass(`${_ctx.prefixCls}-addon ${_ctx.prefixCls}-option-header`),
      onMousedown: _cache[0] || (_cache[0] = withModifiers(() => {}, ["prevent"]))
    }, [renderSlot(_ctx.$slots, "header")], 34 /* CLASS, NEED_HYDRATION */)) : createCommentVNode("v-if", true), createVNode(_component_OptionList, {
      hoverOptionValue: _ctx.hoverOptionValue,
      options: _ctx.filteredOptions,
      prefixCls: _ctx.prefixCls,
      containerStyle: _ctx.dropdownStyle,
      isSelect: _ctx.isSelect,
      onSelect: _ctx.onSelect,
      onHover: _ctx.onHover,
      isLimit: _ctx.isLimitRef,
      emptyText: _ctx.listEmptyText,
      renderOption: _ctx.$slots.option,
      renderEmpty: _ctx.$slots.empty,
      virtualScroll: _ctx.virtualScroll,
      filterText: _ctx.filterText,
      filterTextHighlight: _ctx.filterTextHighlight,
      onScroll: _ctx.onScroll,
      onMousedown: _cache[1] || (_cache[1] = withModifiers(() => {}, ["prevent"]))
    }, null, 8 /* PROPS */, ["hoverOptionValue", "options", "prefixCls", "containerStyle", "isSelect", "onSelect", "onHover", "isLimit", "emptyText", "renderOption", "renderEmpty", "virtualScroll", "filterText", "filterTextHighlight", "onScroll"]), _ctx.$slots.footer ? (openBlock(), createElementBlock("div", {
      key: 1,
      class: normalizeClass(`${_ctx.prefixCls}-addon ${_ctx.prefixCls}-option-footer`),
      onMousedown: _cache[2] || (_cache[2] = withModifiers(() => {}, ["prevent"]))
    }, [renderSlot(_ctx.$slots, "footer")], 34 /* CLASS, NEED_HYDRATION */)) : _ctx.$slots.addon ? (openBlock(), createElementBlock("div", {
      key: 2,
      class: normalizeClass(`${_ctx.prefixCls}-addon ${_ctx.prefixCls}-option-footer`),
      onMousedown: _cache[3] || (_cache[3] = withModifiers(() => {}, ["prevent"]))
    }, [createTextVNode(toDisplayString(_ctx.warnDeprecatedSlot()) + " ", 1 /* TEXT */), renderSlot(_ctx.$slots, "addon")], 34 /* CLASS, NEED_HYDRATION */)) : createCommentVNode("v-if", true)]),
    _: 3 /* FORWARDED */
  }, 8 /* PROPS */, ["modelValue", "onlyShowTrigger", "popperClass", "appendToContainer", "getContainer", "disabled"]), createElementVNode("div", {
    class: normalizeClass(`${_ctx.prefixCls}-hidden-options`)
  }, [renderSlot(_ctx.$slots, "default")], 2 /* CLASS */)], 2 /* CLASS */);
}

script.render = render;
script.__file = "components/select/select.vue";

export { script as default };
