<template>
  <RawAutocomplete
    v-model="modelValue"
    :options="options"
    :open="open"
    :label="label"
    :optional="optional"
    :disabled="disabled"
    :errors="errors"
    :captions="captions"
    :required="required"
    :helperText="helperText"
    :placeholder="placeholder"
    :small="small"
    :canAddNewOption="false"
    @input="onInput"
    @select="(option: any) => emits('select', option)"
  >
    <template #icon-start v-if="currentOptionIcon">
      <span
        class="icon"
        v-if="typeof currentOptionIcon === 'string'"
        v-html="currentOptionIcon"
      ></span>
      <component v-else :is="currentOptionIcon" />
    </template>
    <template #icon-end>
      <BmsLoader v-if="loading" :size="16" />
    </template>
    <template #option="{ option }: { option: any }">
      <template v-if="option.icon">
        <span
          class="icon datalist-icon"
          v-if="typeof option.icon === 'string'"
          v-html="option.icon"
        ></span>

        <component class="icon datalist-icon" v-else :is="option.icon" />
      </template>
      {{ option.label }}
    </template>
  </RawAutocomplete>
</template>

<script setup lang="ts">
import { computed, onMounted, ref, useTemplateRef } from 'vue';
import RawAutocomplete from './RawAutocomplete.vue';
import { InputOption } from '@/models';
import { FieldComponentProps } from '@/plugins/field/field-component.model';
import BmsLoader from '../feedback/BmsLoader.vue';
import { useDebounceFn } from '@vueuse/core';

export interface Props extends FieldComponentProps {
  url?: string;
  request?: (
    abortController: AbortController,
    inputValue?: string,
    url?: string,
  ) => Promise<{ data: InputOption[] }>;

  modelValue?: string;
  placeholder?: string;
  open?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  open: false,
  request: async (controller: AbortController, url?: string) => {
    if (!url) {
      throw 'URL must be defined';
    }

    const response = await fetch(url, {
      method: 'GET',
      signal: controller.signal,
    });

    const data = await response.json();
    return { data };
  },
});

const modelValue = defineModel<string>('modelValue', {
  default: null,
});
const emits = defineEmits<{
  addNewOption: [newOption: string];
  select: [option: InputOption];
}>();

const debouncedLoadData = useDebounceFn(
  (search?: string) => loadData(search ?? ''),
  500,
);

const options = ref<InputOption[]>([]);
const loading = ref(true);
const controller = ref<AbortController>();

const loadData = async (search: string) => {
  loading.value = true;
  try {
    if (controller.value) {
      controller.value.abort();
    }
    controller.value = new AbortController();
    loading.value = true;

    const { data } = await props.request(controller.value, search, props.url);

    options.value = data;
  } catch (e) {
    console.error(e);
  } finally {
    loading.value = false;
  }
};

onMounted(() => loadData(''));
const onInput = (e: InputEvent) => {
  const search = (e.target as HTMLInputElement)?.value;
  if (search?.length > 2) {
    debouncedLoadData(search);
  } else {
    options.value = [];
  }
};

const currentOptionIcon = computed(() => {
  const option = options.value.find((o) => o.value === modelValue.value);
  if (!option || typeof option === 'string') return undefined;
  return option.icon;
});
</script>

<style scoped lang="scss">
.icon {
  height: 1em;
  width: 1em;

  &.datalist-icon {
    margin: 0 0.5em;
  }

  :deep(svg) {
    height: 100%;
    width: 100%;

    * {
      fill: currentColor !important;
    }
  }
}
</style>
