<template>
  <Listbox
    :modelValue="selected"
    @update:modelValue="valueChanged"
    :disabled="disabled"
    by="value"
  >
    <div class="relative mt-1">
      <ListboxButton :class="inputClass" :id="forId">
        <span class="block truncate">{{ selected?.name || '' }}</span>
        <span
          class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"
        >
          <ChevronUpDownIcon class="h-5 w-5 text-gray-400" aria-hidden="true" />
        </span>
      </ListboxButton>

      <Transition
        leave-active-class="transition duration-100 ease-in"
        leave-from-class="opacity-100"
        leave-to-class="opacity-0"
      >
        <ListboxOptions
          class="absolute z-20 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
        >
          <ListboxOption
            v-slot="{ active, selected }"
            v-for="option in options"
            :key="option.name"
            :value="option"
            as="template"
          >
            <li :class="itemClass(active, selected)">
              <slot
                name="option"
                :selected="selected"
                :active="active"
                :option="option"
              >
                <span
                  :class="[
                    selected ? 'font-medium' : 'font-normal',
                    'block truncate',
                  ]"
                >
                  {{ option.name }}
                </span>
              </slot>
              <span
                v-if="selected"
                class="absolute inset-y-0 left-0 flex items-center pl-2 text-blue-600"
              >
                <CheckIcon class="h-5 w-5" aria-hidden="true" />
              </span>
            </li>
          </ListboxOption>
        </ListboxOptions>
      </Transition>
    </div>
  </Listbox>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue';
import {
  Listbox,
  ListboxButton,
  ListboxOptions,
  ListboxOption,
} from '@headlessui/vue';
import { CheckIcon, ChevronUpDownIcon } from '@heroicons/vue/24/outline';
import { SelectOption } from '../form.types';

const props = withDefaults(
  defineProps<{
    forId?: string;
    modelValue: string | number | null;
    options: SelectOption[];
    disabled?: boolean;
    invalid?: boolean;
    placeholder?: string;
  }>(),
  {
    modelValue: null,
    disabled: false,
    invalid: false,
  }
);

const localValue = ref(props.modelValue);

const emit = defineEmits<{
  (e: 'update:modelValue', value: string | number | null): void;
}>();

const valueChanged = (option: SelectOption) => {
  localValue.value = option.value;
  emit('update:modelValue', localValue.value);
};

const selected = computed(() =>
  props.options.find((item) => localValue.value === item.value)
);

const inputClass = computed(() => {
  return [
    props.invalid
      ? 'border-red-300 focus:border-red-500 focus:ring-red-500'
      : 'border-gray-300 focus:border-blue-500 focus:ring-blue-500',
    props.disabled ? 'bg-gray-100 hover:border-gray-400' : '',
    `block w-full rounded-md shadow-sm sm:text-sm transition-colors duration-200`,
    'py-2 pl-3 pr-10 text-left cursor-default border-[1px] focus:ring-1',
    'sm:min-h-[38px] min-h-[42px]',
  ];
});

const itemClass = (active: boolean, selected: boolean) => {
  const classList = ['relative cursor-default select-none py-2 pl-8 pr-4'];
  if (selected) {
    classList.push('text-blue-900');
  }

  if (active && selected) {
    classList.push('bg-blue-200');
  } else if (active || selected) {
    classList.push('bg-blue-100');
  } else {
    classList.push('text-gray-900');
  }

  return classList;
};
</script>
