<script setup lang="ts">
import type { DefaultOptionType, SelectValue } from 'ant-design-vue/es/select';

import type { ApiConfig } from '../types';

import type { BasicOptionResult } from '#/api/model';

import { computed, type PropType, ref, unref, watch, watchEffect } from 'vue';

import { isFunction } from '@vben/utils';

import { useVModel } from '@vueuse/core';
import { Select } from 'ant-design-vue';

import { Icon } from '#/components/icon';
import { get, omit } from '#/utils';

import { createApiFunction } from '../helper';

const props = defineProps({
  value: {
    type: [String, Number, Array] as PropType<SelectValue>,
    default: undefined,
  },
  numberToString: {
    type: Boolean,
    default: false,
  },
  api: {
    type: Object as PropType<ApiConfig>,
    default: () => ({
      type: 'none',
      method: 'get',
      params: {},
      result: '',
      url: null,
    }),
  },
  fieldNames: {
    type: Object as PropType<{ label: string; options: any; value: string }>,
    default: () => ({ label: 'label', value: 'value', options: null }),
  },
  immediate: {
    type: Boolean,
    default: true,
  },
  showSearch: {
    type: Boolean,
    default: false,
  },
});
const emit = defineEmits(['update:value', 'optionsChange']);
const modelValue = useVModel(props, 'value', emit, {
  defaultValue: props.value,
  passive: true,
});
const options = ref<BasicOptionResult[]>([]);
const loading = ref(false);
const isFirstLoad = ref(true);

const getOptions = computed(() => {
  const { fieldNames, numberToString } = props;
  const res: BasicOptionResult[] = [];
  unref(options).forEach((item: any) => {
    const value = item[fieldNames.value];
    res.push({
      ...omit(item, [fieldNames.label, fieldNames.value]),
      label: item[fieldNames.label],
      value: numberToString ? `${value}` : value,
      disabled: item.disabled || false,
    });
  });
  return res as DefaultOptionType[];
});

const emitChange = () => {
  emit('optionsChange', unref(options));
};

const fetch = async () => {
  const api = createApiFunction({ ...props.api });
  if (!api || !isFunction(api)) return;
  try {
    loading.value = true;
    const res = await api(props.api.params);
    if (Array.isArray(res)) {
      options.value = res;
    } else {
      options.value = props.api.result ? get(res, props.api.result) : [];
    }
    emitChange();
  } catch (error) {
    console.warn(error);
  } finally {
    loading.value = false;
  }
};
const handleFetch = async () => {
  if (!props.immediate && unref(isFirstLoad)) {
    await fetch();
    isFirstLoad.value = false;
  }
};

const handelFilterOption = (input: string, option: any) => {
  const { fieldNames } = props;
  return (
    `${option[fieldNames.value]}`
      .toLowerCase()
      .includes(`${input}`.toLowerCase()) ||
    option[fieldNames.label]
      .toString()
      .toLowerCase()
      .includes(input.toLowerCase())
  );
};

watchEffect(() => {
  props.immediate && fetch();
});

watch(
  () => props.api.params,
  () => {
    !unref(isFirstLoad) && fetch();
  },
  { deep: true },
);
</script>

<template>
  <Select
    v-model:value="modelValue"
    :options="getOptions"
    v-bind="$attrs"
    :filter-option="handelFilterOption"
    :show-search="showSearch"
    class="w-full"
    @dropdown-visible-change="handleFetch"
  >
    <template v-for="item in Object.keys($slots)" #[item]="data">
      <slot :name="item" v-bind="data || {}"></slot>
    </template>
    <template v-if="loading" #suffixIcon>
      <Icon icon="ant-design:loading-outlined" spin />
    </template>
    <template v-if="loading" #notFoundContent>
      <span>
        <Icon class="mr-1" icon="ant-design:loading-outlined" spin />
        请等待数据加载完成
      </span>
    </template>
  </Select>
</template>
