<script setup>
import { computed, onBeforeMount } from "vue";
import { useDebounceFn } from "@vueuse/core";
import VSelect from "vue-select";
import FormatIcon from "../format/FormatIcon.vue";
const emit = defineEmits(["request:selected", "search"]);
const props = defineProps({
  debounce: { type: Number, default: () => 250 },
  disabled: { type: Boolean, default: () => false },
  fieldDisabled: { type: Function, default: void 0 },
  fieldSort: { type: Function, default: (it) => it.fieldText },
  fieldText: { type: Function, default: ((it) => it.name) },
  fieldValue: { type: Function, default: ((it) => it._id) },
  filterable: { type: Boolean, default: () => true },
  loading: { type: Boolean, default: () => false },
  multiple: { type: Boolean, default: () => false },
  noClear: { type: Boolean, default: () => false },
  noCloseOnSelect: { type: Boolean, default: () => false },
  options: { type: Array, default: () => [] },
  placeholder: { type: String, default: () => void 0 },
  required: { type: Boolean, default: () => false },
  selectedItem: { type: Object, default: void 0 },
  size: { type: String, default: () => void 0 }
});
const model = defineModel({ type: [String, Object, Number, Boolean, Array], default: void 0 });
onBeforeMount(() => {
  if (props.selectedItem) return;
  if (model.value) {
    emit("request:selected", model.value);
  }
});
function filterItems(options, search) {
  if (!props.fieldText) {
    return false;
  }
  return options.filter((item) => {
    const text = props.fieldText?.(item) || "";
    return text.toLocaleLowerCase().includes(search.toLowerCase());
  });
}
const sortedOptions = computed(() => {
  const options = props.options;
  return options.sort((prev, next) => {
    const textPrev = props.fieldSort(prev)?.toLowerCase?.();
    const textNext = props.fieldSort(next)?.toLowerCase?.();
    if (textPrev > textNext) {
      return 1;
    }
    if (textPrev < textNext) {
      return -1;
    }
    return 0;
  });
});
const mergedOptions = computed(() => {
  const options = sortedOptions.value;
  const selected = props.selectedItem;
  if (!selected) return options;
  const filtered = options.filter(
    (item) => props.fieldValue(item) !== props.fieldValue(selected)
  );
  return [selected, ...filtered];
});
const bindAttrs = computed(() => ({
  appendToBody: true,
  autocomplete: "one-time-code",
  clearable: props.disabled ? false : !props.noClear,
  closeOnSelect: props.multiple ? false : !props.noCloseOnSelect,
  disabled: props.disabled,
  filter: filterItems,
  filterable: props.filterable,
  getOptionLabel: props.fieldText,
  loading: props.loading,
  options: mergedOptions.value,
  placeholder: props.placeholder,
  readonly: props.loading,
  reduce: (option) => props.fieldValue?.(option),
  selectable: props.fieldDisabled
}));
const bindOn = {
  search: (value) => {
    if (value) debouncedFn(value);
    return value;
  }
};
const debouncedFn = useDebounceFn((value) => emit("search", value), props.debounce);
const isRequired = computed(() => {
  const isEmpty = props.multiple ? !model.value?.length : !model.value;
  return props.required && isEmpty ? true : void 0;
});
</script>

<template>
  <VSelect v-model="model" v-bind="bindAttrs" v-on="bindOn">
    <template #search="scope">
      <input
        :required="isRequired"
        class="vs__search"
        v-bind="scope.attributes"
        v-on="scope.events">
    </template>

    <template #option="scope">
      <slot v-bind="{ item: scope, text: props.fieldText?.(scope), selectedOption: false }">
        <div class="text-truncate">
          <span v-text="props.fieldText?.(scope)" />
        </div>
      </slot>
    </template>

    <template #selected-option="scope">
      <span v-text="props.fieldText?.(scope)" />
    </template>

    <template #footer>
      <span :required="props.required || void 0" />
    </template>

    <template #spinner="{ loading: isLoading }">
      <BSpinner v-if="isLoading" class="spinner" variant="primary" />
    </template>

    <template #no-options="scope">
      <slot name="no-options" v-bind="scope">
        <span v-if="scope.loading">Searching for "{{ scope.search }}"</span>
        <span v-else-if="scope.searching">No results for "{{ scope.search }}"</span>
        <span v-else>Type to search</span>
      </slot>
    </template>

    <template #open-indicator="{ attributes }">
      <FormatIcon mode="solid" name="chevron-down" size="lg" v-bind="attributes" />
    </template>
  </VSelect>
</template>

<style scoped>
.spinner {
  --bs-spinner-width: 1.25rem;
  --bs-spinner-height: 1.25rem;
  --bs-spinner-border-width: 0.15em;
}
</style>
