<template>
  <div class="w-full">
    <Combobox v-model="selected" :disabled="disabled">
      <ComboboxLabel>{{ title }}</ComboboxLabel>
      <div class="relative mt-1">
        <ComboboxInput
          class="rounded-md shadow-xs relative block w-full px-3 py-2 border border-gray-300 focus:border-primary placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-hidden focus:ring-0 focus:z-10 sm:text-sm resize-none"
          :class="useScanner ? 'pl-9!' : ''"
          :displayValue="() => chosen?.name ?? ''"
          @input="query = $event.target.value"
        />
        <QrCodeIcon
          v-if="useScanner"
          class="absolute h-6 stroke-1 left-2 top-1/2 -translate-y-1/2 cursor-pointer z-10"
          @click="scanCode"
        />
        <ComboboxButton class="absolute inset-y-0 right-0 flex items-center pr-2">
          <ChevronUpDownIcon class="h-5 w-5 text-gray-400" aria-hidden="true" />
        </ComboboxButton>
        <TransitionRoot
          leave="transition ease-in duration-100"
          leaveFrom="opacity-100"
          leaveTo="opacity-0"
          @after-leave="query = ''"
        >
          <ComboboxOptions
            class="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-md ring-1 ring-black/5 focus:outline-hidden sm:text-sm z-20"
          >
            <ComboboxOption v-if="loading || deferingSearch" as="template" disabled>
              <li class="flex flex-row gap-2 text-text relative cursor-default select-none py-2 pl-3 pr-4">
                <Spinner />
                <span class="font-normal block truncate">suche</span>
              </li>
            </ComboboxOption>
            <ComboboxOption v-else-if="available.length === 0 && all.length == 0" as="template" disabled>
              <li class="text-text relative cursor-default select-none py-2 pl-3 pr-4">
                <span class="font-normal block truncate">tippe, um zu suchen...</span>
              </li>
            </ComboboxOption>
            <ComboboxOption v-else-if="available.length === 0 && query != ''" as="template" disabled>
              <li class="text-text relative cursor-default select-none py-2 pl-3 pr-4">
                <span class="font-normal block truncate">Keine Auswahl gefunden.</span>
              </li>
            </ComboboxOption>

            <ComboboxOption
              v-if="!(loading || deferingSearch)"
              v-for="wearable in available"
              as="template"
              :key="wearable.id"
              :value="wearable.id"
              v-slot="{ selected, active }"
            >
              <li
                class="relative cursor-default select-none py-2 pl-10 pr-4"
                :class="{
                  'bg-primary text-white': active,
                  'text-gray-900': !active,
                }"
              >
                <span class="block truncate" :class="{ 'font-medium': selected, 'font-normal': !selected }">
                  {{ wearable.name }}<span v-if="wearable.code"> - Code: {{ wearable.code }}</span>
                </span>
                <span
                  v-if="selected"
                  class="absolute inset-y-0 left-0 flex items-center pl-3"
                  :class="{ 'text-white': active, 'text-primary': !active }"
                >
                  <CheckIcon class="h-5 w-5" aria-hidden="true" />
                </span>
              </li>
            </ComboboxOption>
          </ComboboxOptions>
        </TransitionRoot>
      </div>
    </Combobox>
  </div>
</template>

<script setup lang="ts">
import { defineAsyncComponent, defineComponent, markRaw, type Prop } from "vue";
import { mapState, mapActions } from "pinia";
import {
  Combobox,
  ComboboxLabel,
  ComboboxInput,
  ComboboxButton,
  ComboboxOptions,
  ComboboxOption,
  TransitionRoot,
} from "@headlessui/vue";
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
import Spinner from "../Spinner.vue";
import { useWearableStore } from "@/stores/admin/unit/wearable/wearable";
import type { WearableViewModel } from "@/viewmodels/admin/unit/wearable/wearable.models";
import { QrCodeIcon } from "@heroicons/vue/24/outline";
import { useModalStore } from "@/stores/modal";
</script>

<script lang="ts">
export default defineComponent({
  props: {
    modelValue: {
      type: String,
      default: "",
    },
    title: String,
    disabled: {
      type: Boolean,
      default: false,
    },
    useScanner: {
      type: Boolean,
      default: false,
    },
  },
  emits: ["update:model-value"],
  watch: {
    modelValue() {
      if (this.initialLoaded) return;
      this.initialLoaded = true;
      this.loadWearableInitial();
    },
    query() {
      this.deferingSearch = true;
      clearTimeout(this.timer);
      this.timer = setTimeout(() => {
        this.deferingSearch = false;
        this.search();
      }, 600);
    },
  },
  data() {
    return {
      initialLoaded: false as boolean,
      loading: false as boolean,
      deferingSearch: false as boolean,
      timer: undefined as any,
      query: "" as string,
      all: [] as Array<WearableViewModel>,
      filtered: [] as Array<WearableViewModel>,
      chosen: undefined as undefined | WearableViewModel,
    };
  },
  computed: {
    available() {
      return this.query == "" ? this.all : this.filtered;
    },
    selected: {
      get() {
        return this.modelValue;
      },
      set(val: string) {
        this.chosen = this.getWearableFromSearch(val);
        this.$emit("update:model-value", val);
      },
    },
  },
  mounted() {
    this.loadWearableInitial();
    this.preloadAll();
  },
  methods: {
    ...mapActions(useWearableStore, ["searchWearables", "fetchWearableById", "getAllWearables"]),
    ...mapActions(useModalStore, ["openModal"]),
    preloadAll() {
      this.loading = true;
      this.getAllWearables()
        .then((res) => {
          this.all = res.data;
        })
        .catch((err) => {})
        .finally(() => {
          this.loading = false;
        });
    },
    search() {
      this.filtered = [];
      if (this.query == "") return;
      this.loading = true;
      this.searchWearables(this.query)
        .then((res) => {
          this.filtered = res.data;
        })
        .catch((err) => {})
        .finally(() => {
          this.loading = false;
        });
    },
    getWearableFromSearch(id: string) {
      return this.all.find((f) => f.id == id);
    },
    loadWearableInitial() {
      if (this.modelValue == "") return;
      this.fetchWearableById(this.modelValue)
        .then((res) => {
          this.chosen = res.data;
        })
        .catch(() => {});
    },
    scanCode() {
      this.openModal(
        markRaw(defineAsyncComponent(() => import("@/components/CodeDetector.vue"))),
        "codeScanInput",
        (result: string) => {
          this.getWearableFromSearch(result);
        }
      );
    },
  },
});
</script>
