<!-- @format -->

<template>
  <a-select
    v-bind="getBindValues"
    :options="getOptions"
    :getPopupContainer="(triggerNode) => triggerNode.parentNode"
    @dropdown-visible-change="dropdownHandle"
    @popupScroll="onPopupScroll"
    v-model:value="modelValue"
    @search="onSearch"
    @clear="onClear"
    :filterOption="onFilterOption">
    <template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
      <slot :name="item" v-bind="data || {}"></slot>
    </template>
  </a-select>
</template>
<script lang="ts">
  import { computed, defineComponent, nextTick, PropType, ref, unref, watch } from 'vue';
  import { cloneDeep, get, isArray, isEqual, isFunction, isString, isUndefined } from 'lodash-es';
  import { HttpRequestConfig, useApi } from '/nerv-lib/use/use-api';
  type ChangeValue = string | number | undefined | string[] | number[];
  export default defineComponent({
    name: 'NsSelectApi',
    props: {
      api: {
        type: [String, Object, Function] as PropType<string | Function | HttpRequestConfig>,
        required: true,
      },
      value: [String, Object, Array, Number],
      field: String,
      params: {
        type: Object,
        default: () => ({}),
      },
      resultField: {
        type: String,
        default: 'data.data',
      },
      firstOption: {
        type: Object,
      },
      numberToString: {
        type: Boolean,
        default: false,
      },
      immediate: {
        type: Boolean,
        default: false,
      },
      labelField: {
        type: [String, Function],
        default: 'label',
      },
      valueField: {
        type: String,
        default: 'value',
      },
      autoSelectFirst: {
        // 与autoClearValue同时使用，只会执行autoSelectFirst
        type: Boolean,
        default: false,
      },
      autoClearValue: {
        type: Boolean,
        default: false,
      },
      //数据筛选函数
      filterData: {
        type: Function,
      },
      // 开启前端筛选默认label包含
      autoSearch: {
        type: Boolean,
        default: false,
      },
      //后端筛选字段
      filterFiled: {
        type: String,
      },
      //是否开启滚动加载
      scrollLoad: {
        type: Boolean,
        default: false,
      },
      //分页字段
      pageField: {
        type: String,
        default: 'page',
      },
      //第一页
      defaultPage: {
        type: Number,
        default: 0,
      },
      dropdownReload: {
        type: Boolean,
        default: false,
      },
    },
    emits: ['change', 'validateChange'],
    setup(props, { attrs, emit }) {
      const options = ref([]);
      const isLoad = ref(false);
      const changeValue = ref<any>(undefined);
      const filterFiledRef = ref<string | undefined>(undefined);
      let isFirstLoad = !!props.api; // 是否第一次加载
      // eslint-disable-next-line vue/no-setup-props-destructure
      let page = props.defaultPage; //
      let isLoading = false;
      const getBindValues = computed(() => {
        const selectProps: Recordable = {};
        if (props.filterFiled || props.autoSearch) {
          selectProps['showSearch'] = true;
        }
        return {
          ...attrs,
          ...selectProps,
        };
      });
      /**
       * 设置filterFiled时注册search事件
       */
      const onSearch = computed(() => {
        if (props.filterFiled) {
          return (input: string) => {
            if (filterFiledRef.value !== input) {
              filterFiledRef.value = input;
              page = props.defaultPage;
              fetch();
            }
          };
        } else {
          return undefined;
        }
      });
      /**
       * 设置filterFiled时注册filterOption事件
       */
      const onFilterOption = computed(() => {
        if (props.autoSearch) {
          return (input: string, option: any) => {
            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
          };
        } else {
          return false;
        }
      });
      /**
       * 根据请求得到的data获取options
       */
      const getOptions = computed(() => {
        const { firstOption, labelField, valueField, numberToString } = props;
        const sourceOptions = [];
        firstOption && sourceOptions.push(firstOption);
        return unref(options).reduce((acc, next: Recordable) => {
          if (isString(next)) {
            //值是字符串类型
            const option = {
              label: next,
              value: next,
            };
            acc.push(option);
            return acc;
          } else {
            const value = get(next, valueField);
            const option = {
              ...next,
              label: isFunction(labelField) ? labelField(next) : get(next, labelField as string),
              value: numberToString ? `${value}` : value,
            };
            acc.push(option);
            return acc;
          }
        }, sourceOptions);
      });

      /**
       * 获取数据
       */
      const fetch = () => {
        if (getBindValues.value.checkRequiredParams === false) {
          options.value = [];
          return;
        }

        isLoading = true;
        const requestConfig: HttpRequestConfig = { method: 'get' };
        const {
          api,
          params: _params,
          resultField,
          filterData,
          filterFiled,
          pageField,
          scrollLoad,
          defaultPage,
        } = props;
        const params: Recordable = cloneDeep(_params);
        if (filterFiled && filterFiledRef.value) {
          params[filterFiled] = filterFiledRef.value;
        }
        if (scrollLoad) {
          params[pageField] = page;
          if (page === defaultPage) {
            options.value = [];
          }
        }
        const { httpRequest } = useApi();
        httpRequest({ api, params, requestConfig })
          .then((res: Recordable) => {
            emit('validateChange', { help: undefined });
            if (resultField) {
              let data = get(res, resultField) || [];
              // data = data.splice(Math.floor(Math.random() * 3), Math.floor(Math.random() * 5 + 5));
              if (isFunction(filterData)) {
                data = data.filter(filterData);
              }
              if (scrollLoad) {
                options.value = [...options.value, ...data];
              } else {
                options.value = data;
              }
            }
            isLoad.value = true;
            isLoading = false;
          })
          .catch((error: any) => {
            if (error?.response?.status === 403) {
              emit('validateChange', { help: '暂无权限', validateStatus: 'error' });
              nextTick(() => {
                //清空编辑初始值
                modelValue.value = undefined;
              });
            }
            options.value = [];
            isLoading = false;
          });
      };
      let lastParams: any = undefined;
      /**
       ** 延迟获取数据
       */
      async function dropdownHandle(open: boolean) {
        if (!open) return;
        if ((!props.immediate && !isLoad.value) || props.dropdownReload) {
          await fetch();
        }
      }
      /**
       ** 绑定value
       */
      const modelValue = computed({
        set(value: ChangeValue) {
          if (isEqual(value, changeValue.value)) return;
          changeValue.value = value;
          triggerChange(value);
        },
        get() {
          return changeValue.value;
        },
      });
      /**
       * 传入值需要
       * 前提option已获取到数据
       */
      watch(
        () => props.value,
        () => {
          if (isLoad.value) {
            modelValue.value = props.value;
          }
        },
        {
          immediate: true,
        },
      );
      watch(
        () => getOptions.value,
        () => {
          const { value, autoSelectFirst, autoClearValue } = props;
          // 首次加载如果有值则选中值
          if (isFirstLoad && !isUndefined(value)) {
            modelValue.value = value;
          } else if (!filterFiledRef.value) {
            if (autoSelectFirst) {
              modelValue.value = getOptions.value[0]?.value;
            } else if (autoClearValue) {
              modelValue.value = undefined;
            }
          }
          isFirstLoad = false;
        },
      );
      /**
       * 重写change ant 初始化数据、删除数据时不触发change
       * @param value
       */
      function triggerChange(value: ChangeValue) {
        if (isUndefined(value)) {
          emit('change', value, undefined);
        } else if (isArray(value)) {
          const options: Record<string, any>[] = [];
          value.forEach((v) => {
            getOptions.value.forEach((option) => {
              if (option.value === v) {
                options.push(option);
              }
            });
          });
          emit('change', value, options);
        } else {
          let op = {};
          getOptions.value.forEach((option) => {
            if (option.value === value) {
              op = option;
            }
          });
          emit('change', value, op);
        }
      }
      /**
       ** 联动 immediate 为是否主动获取数据
       */
      watch(
        [() => props.params, () => props.api],
        async () => {
          const { params } = props;
          if (!isEqual(lastParams, params)) {
            if (props.immediate || true) {
              //todo 暂时全设为主动获取
              lastParams = cloneDeep(params);
              await fetch();
            }
            isLoad.value = false; // 设置成false 点击下拉才会触发
          }
        },
        { deep: true, immediate: props.immediate },
      );
      const onPopupScroll = computed(() => {
        if (props.scrollLoad) {
          return (e) => {
            const { scrollHeight, scrollTop, clientHeight } = e.target;
            if (scrollHeight - scrollTop - 80 < clientHeight && !isLoading) {
              page++;
              fetch();
            }
          };
        } else {
          return undefined;
        }
      });
      const onClear = () => {
        filterFiledRef.value = undefined;
      };
      return {
        onPopupScroll,
        dropdownHandle,
        getOptions,
        getBindValues,
        modelValue,
        onSearch,
        onFilterOption,
        onClear,
      };
    },
  });
</script>
<style lang="less" scoped></style>
