<script setup lang="ts">
import { Check, ChevronDown } from '@lucide/vue'
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'

import type { HomeSelectionOption } from './homeModalTypes'

const props = withDefaults(defineProps<{
  modelValue: number
  options: HomeSelectionOption[]
  dataRef?: string
  label: string
  placeholder?: string
}>(), {
  dataRef: undefined,
  placeholder: 'Auswählen',
})

const emit = defineEmits<{
  'update:modelValue': [value: number]
  change: [value: number]
}>()

const rootEl = ref<HTMLElement | null>(null)
const open = ref(false)
const activeIndex = ref(0)

const selectedOption = computed(() =>
  props.options.find((option) => option.id === props.modelValue) ?? props.options[0] ?? null,
)

const selectedValue = computed(() => selectedOption.value?.id ?? 0)

const listboxId = computed(() =>
  `home-select-${props.label.toLowerCase().replace(/[^a-z0-9]+/g, '-')}`,
)

watch(
  () => props.options,
  (options) => {
    const currentIndex = options.findIndex((option) => option.id === props.modelValue)
    if (currentIndex >= 0) {
      activeIndex.value = currentIndex
      return
    }

    activeIndex.value = 0
    if (options[0]) {
      emit('update:modelValue', options[0].id)
      emit('change', options[0].id)
    }
  },
  { immediate: true },
)

function toggleDropdown() {
  if (!props.options.length) return
  open.value = !open.value
  if (open.value) {
    activeIndex.value = Math.max(0, props.options.findIndex((option) => option.id === selectedValue.value))
  }
}

function closeDropdown() {
  open.value = false
}

function selectOption(option: HomeSelectionOption) {
  emit('update:modelValue', option.id)
  emit('change', option.id)
  closeDropdown()
}

function selectActiveOption() {
  const option = props.options[activeIndex.value]
  if (option) selectOption(option)
}

function moveActive(delta: number) {
  if (!props.options.length) return
  if (!open.value) {
    open.value = true
    return
  }

  activeIndex.value = (activeIndex.value + delta + props.options.length) % props.options.length
}

function onButtonKeydown(event: KeyboardEvent) {
  if (event.key === 'ArrowDown') {
    event.preventDefault()
    moveActive(1)
    return
  }

  if (event.key === 'ArrowUp') {
    event.preventDefault()
    moveActive(-1)
    return
  }

  if (event.key === 'Enter' || event.key === ' ') {
    event.preventDefault()
    open.value ? selectActiveOption() : toggleDropdown()
    return
  }

  if (event.key === 'Escape') {
    event.preventDefault()
    closeDropdown()
  }
}

function onDocumentPointerDown(event: PointerEvent) {
  if (!rootEl.value?.contains(event.target as Node)) {
    closeDropdown()
  }
}

onMounted(() => {
  document.addEventListener('pointerdown', onDocumentPointerDown)
})

onBeforeUnmount(() => {
  document.removeEventListener('pointerdown', onDocumentPointerDown)
})
</script>

<template>
  <div ref="rootEl" class="home-select" :class="{ 'home-select--open': open }">
    <select
      v-if="props.dataRef"
      class="home-select__native"
      :data-dc-ref="props.dataRef"
      :value="selectedValue"
      aria-hidden="true"
      tabindex="-1"
    >
      <option v-for="option in props.options" :key="option.id" :value="option.id">
        {{ option.label }}
      </option>
    </select>

    <button
      type="button"
      class="home-select__trigger"
      :aria-label="props.label"
      :aria-expanded="open"
      aria-haspopup="listbox"
      :aria-controls="listboxId"
      @click="toggleDropdown"
      @keydown="onButtonKeydown"
    >
      <span class="home-select__value">{{ selectedOption?.label ?? props.placeholder }}</span>
      <span class="home-select__chevron" aria-hidden="true">
        <ChevronDown :size="20" :stroke-width="2.8" />
      </span>
    </button>

    <div v-if="open" :id="listboxId" class="home-select__menu" role="listbox">
      <button
        v-for="(option, optionIndex) in props.options"
        :key="option.id"
        type="button"
        class="home-select__option"
        :class="{
          'home-select__option--active': optionIndex === activeIndex,
          'home-select__option--selected': option.id === selectedValue,
        }"
        role="option"
        :aria-selected="option.id === selectedValue"
        @mouseenter="activeIndex = optionIndex"
        @click="selectOption(option)"
      >
        <span class="home-select__option-label">{{ option.label }}</span>
        <Check v-if="option.id === selectedValue" class="home-select__check" :size="18" :stroke-width="3" />
      </button>
    </div>
  </div>
</template>

<style scoped>
.home-select{
  position:relative;
  width:100%;
  z-index:1;
}

.home-select--open{
  z-index:30;
}

.home-select__native{
  position:absolute;
  width:1px;
  height:1px;
  opacity:0;
  pointer-events:none;
}

.home-select__trigger{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:14px;
  width:100%;
  min-height:56px;
  padding:11px 11px 11px 17px;
  border:2px solid #e8def8;
  border-radius:16px;
  background:linear-gradient(180deg,#fff 0%,#fbf8ff 100%);
  box-shadow:0 8px 22px rgba(139,108,219,.08), inset 0 1px 0 rgba(255,255,255,.9);
  color:#3f3556;
  cursor:pointer;
  font-family:'Outfit',sans-serif;
  font-size:15px;
  font-weight:700;
  line-height:1.2;
  outline:none;
  text-align:left;
  transition:border-color .18s ease, box-shadow .18s ease, transform .18s ease, background .18s ease;
}

.home-select__trigger:hover,
.home-select__trigger:focus-visible,
.home-select--open .home-select__trigger{
  border-color:#9b7ce4;
  background:#fff;
  box-shadow:0 12px 30px rgba(139,108,219,.16), 0 0 0 4px rgba(139,108,219,.12);
}

.home-select__value{
  min-width:0;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}

.home-select__chevron{
  display:grid;
  place-items:center;
  flex:none;
  width:34px;
  height:34px;
  border-radius:12px;
  background:#f1eafd;
  color:#7c5bd2;
  box-shadow:inset 0 0 0 1px rgba(139,108,219,.09);
  transition:transform .18s ease, background .18s ease, color .18s ease;
}

.home-select--open .home-select__chevron{
  transform:rotate(180deg);
  background:#835fd8;
  color:#fff;
}

.home-select__menu{
  position:absolute;
  top:calc(100% + 8px);
  left:0;
  right:0;
  display:grid;
  gap:4px;
  max-height:286px;
  padding:8px;
  overflow:auto;
  border:1px solid rgba(139,108,219,.18);
  border-radius:18px;
  background:rgba(255,255,255,.98);
  box-shadow:0 24px 54px rgba(63,53,86,.22);
  backdrop-filter:blur(14px);
  -webkit-backdrop-filter:blur(14px);
}

.home-select__option{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:12px;
  width:100%;
  min-height:46px;
  padding:10px 12px;
  border:0;
  border-radius:13px;
  background:transparent;
  color:#514765;
  cursor:pointer;
  font-family:'Outfit',sans-serif;
  font-size:15px;
  font-weight:650;
  line-height:1.2;
  text-align:left;
  transition:background .16s ease, color .16s ease, transform .16s ease;
}

.home-select__option:hover,
.home-select__option--active{
  background:#f6f0ff;
  color:#4f3a8a;
}

.home-select__option--selected{
  background:linear-gradient(135deg,#efe6ff,#e7dcfb);
  color:#3f2f70;
  font-weight:800;
}

.home-select__option-label{
  min-width:0;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}

.home-select__check{
  flex:none;
  color:#7c5bd2;
}

@media (max-width:760px){
  .home-select__trigger{
    min-height:52px;
    padding-left:14px;
    font-size:14px;
  }

  .home-select__menu{
    max-height:232px;
  }
}
</style>
