<template>
  <a-select
    :value="valueV"
    :mode="multiple ? 'multiple' : 'combobox'"
    :allowClear="allowClear"
    :showSearch="showSearch"
    :disabled="disabled"
    :placeholder="placeholder"
    :default-active-first-option="false"
    :notFoundContent="notFoundContent"
    :options="data"
    :filterOption="showSearch && filterDirectly ? filterOption : false"
    @update:value="handleChange"
    @search="handleSearch"
    v-bind="customProps"
    style="min-width: 150px"
  >
    <template v-if="renderOption" #option="data">
      <VNodeRenderer :render="renderOption" :data="data" />
    </template>
    <a-select-option v-if="showNull" :value="null">(空)</a-select-option>
  </a-select>
</template>

<script lang="ts">
import VNodeRenderer from "@/components/VNodeRenderer.vue";
import { type SelectProps } from "ant-design-vue";
import { defineComponent, markRaw, type PropType, type VNode } from "vue";
import { debounce } from 'lodash-es';
import type { DropdownValues, LoadDataFun } from "./IdAsValueDropdown";
import type { DataModel } from "@imengyu/js-request-transform";
import { StringUtils } from "@imengyu/imengyu-utils";

/**
 * IdAsValueDropdown 的公共接口
 */
export interface IdAsValueDropdownInterface {
  /**
   * 获取某个ID的Lablel
   * @param value 要获取的ID
   */
  getLableByValue(value: number): string;
  /**
   * 重新加载数据
   * @param clearValue 是否需要清除选中数据，默认否
   */
  reload(clearValue?: boolean): void;
}
/**
 * IdAsValueDropdown 的公共接口
 */
export interface IdAsValueDropdownProps<T extends DataModel> {
  /**
   * 允许清除
   */
  allowClear?: boolean,
  /**
   * 显示空？
   */
  showNull?: boolean,
  /**
   * 禁用
   */
  disabled?: boolean,
  /**
   * 多选？
   */
  multiple?: boolean,
  /**
   * 允许搜索
   */
  showSearch?: boolean,
  placeholder?: string,
  /**
   * 未找到数据时的文案
   */
  notFoundContent?: string,
  /**
   * 初始化时加载数据
   */
  loadAtStart?: boolean,
  /**
   * 不使用后端筛选数据而是前端直接筛选
   */
  filterDirectly?: boolean,
  /**
   * 初始化时的搜索数据
   */
  intitialSearchValue?: Record<string, unknown>,
  /**
   * 加载数据回调
   */
  loadData: LoadDataFun<T>,
  /**
   * a-select 其他自定义参数
   */
  customProps?: SelectProps,
  /**
   * 是否自定义渲染option插槽
   */
  renderOption?: RenderOption<T>;
}
type RenderOption<T> = (data: {
  value: unknown,
  label: string,
  raw: T
}) => VNode;

/**
 * 使用数据的ID作为value的下拉框包装
 */
export default defineComponent({
  name: "IdAsValueDropdown",
  data() {
    return {
      valueV: null,
      data: [] as DropdownValues<DataModel>[],
      lastLoadValue: null,
      handleSearch: markRaw(debounce((val: string) => {
        if (!this.filterDirectly)
          this.doLoadData(val);
      }, 500)),
    };
  },
  emits: [
    "update:value",
    "change",
    "loaded",
  ],
  props: {
    showNull: {
      default: false,
      type: Boolean
    },
    renderOption: {
      default: null,
      type: Function as PropType<RenderOption<DataModel>>
    },
    allowClear: {
      default: false,
      type: Boolean
    },
    multiple: {
      default: false,
      type: Boolean
    },
    disabled: {
      default: false,
      type: Boolean
    },
    showSearch: {
      default: true,
      type: Boolean
    },
    placeholder: {
      default: "输入可进行搜索",
      type: String
    },
    notFoundContent: {
      default: "未找到数据，请换个搜索词再试",
      type: String
    },
    loadAtStart: {
      default: true,
      type: Boolean
    },
    filterDirectly: {
      default: true,
      type: Boolean
    },
    value: {
      default: null,
    },
    intitialSearchValue: {
      default: null,
      type: String
    },
    loadData: {
      type: Function as PropType<LoadDataFun<DataModel>>,
      default: null,
    },
    /**
     * a-select 其他自定义参数
     */
    customProps: {
      type: Object as PropType<SelectProps>,
      default: null,
    },
  },
  methods: {
    handleChange(value: unknown) {
      this.$emit("change", value);
      this.$emit("update:value", value);
    },
    doLoadData(val: string | null) {
      if (typeof this.loadData === "function") {
        const oldValue = this.valueV;
        this.valueV = null;
        (this.loadData as LoadDataFun<DataModel>)(val).then((d) => {
          this.data = d;
          setTimeout(() => {
            this.valueV = oldValue;
            this.$emit("loaded");
          }, 30);
        });
      }
    },
    filterOption(input: string, option: {
      label: string;
    }) {
      return !this.filterDirectly || option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
    },
    getLableByValue(value: number) {
      for (let i = 0; i < this.data.length; i++) {
        if (this.data[i].value == value) {
          return this.data[i].label;
        }
      }
      return "";
    },
    reload(clearValue = false) {
      if (clearValue) {
        this.valueV = null;
        this.handleChange(null);
      }
      this.data = [];
      this.doLoadData(this.intitialSearchValue as string);
    },
  },
  watch: {
    loadData() {
      this.doLoadData(this.intitialSearchValue);
    },
    intitialSearchValue(v) {
      if (!this.filterDirectly && !StringUtils.isNullOrEmpty(v)) {
        this.doLoadData(v);
      }
    },
    value(v) {
      this.valueV = v;
    },
  },
  mounted() {
    this.valueV = this.value;
    setTimeout(() => {
      if (this.loadAtStart) {
        this.doLoadData(this.intitialSearchValue as string);
      }
    }, 300);
  },
  components: { VNodeRenderer }
});
</script>