<script setup>

import { ref, useTemplateRef, computed, watch, watchEffect } from 'vue';
import Button from './Button.vue';
import Menu from './Menu.vue';
import Icon from './Icon.vue';

const props = defineProps({
  placeholder: {
    type: String,
    default: 'Select'
  },
  id: String,
  optionLabel: {
    type: String,
    default: 'label'
  },
  optionKey: {
    type: String,
    default: ''
  },
  options: {
    type: Array,
    default: []
  },
  disabled: {
    type: Boolean,
    default: false
  },
  searchThreshold: {
    type: Number,
    default: Infinity,
  },
});

const emits = defineEmits(['select', 'close']);

const model = defineModel();
const selected = ref(null);
const selectedKey = ref(null);
const elem = useTemplateRef('elem');
const menu = useTemplateRef('menu');

watch(model, (newValue, oldValue) => {
  if (props.optionKey) {
    selectedKey.value = newValue;
    selected.value = props.options.find((o) => o[props.optionKey] === newValue);
  } else {
    selectedKey.value = null;
    selected.value = newValue;
  }
});

const menuModel = computed(() => {
  return props.options.map((item) => {
    return {
      label: item[props.optionLabel],
      disabled: item.disabled,
      icon: item.icon,
      separator: item.separator,
      action: () => {
        if (item.separator) return;
        if (selected.value === item) return;

        let index;
        if (props.optionKey) {
          index = props.options.findIndex((o) => o[props.optionKey] === item[props.optionKey]);
        } else {
          index = props.options.indexOf(item);
        }

        selectIndex(index);
      }
    };
  });
});

const nativeSelect = useTemplateRef('nativeSelect');
function selectIndex(index) {
  const item = props.options[index];

  selected.value = item;
  selectedKey.value = props.optionKey ? item[props.optionKey] : null;

  const newValue = props.optionKey ? item[props.optionKey] : item;

  model.value = newValue;
  emits('select', newValue);

  // let the browser know about the changes
  nativeSelect.value.value = newValue;
  nativeSelect.value.dispatchEvent(new Event('input', { bubbles: true }));
  nativeSelect.value.dispatchEvent(new Event('change', { bubbles: true }));
}

function onClick(event) {
  if (props.disabled) return;

  if (menu.value.isOpen) menu.value.close();
  else menu.value.open(event, elem.value);
}

function onOpen(event) {
  if (props.disabled) return;

  menu.value.open(event, elem.value);
}

function onClose(event = null) {
  if (menu.value.isOpen) {
    elem.value.focus();
    menu.value.close();
    emits('close');
  }
}

function onClosed() {
  // restores the focus
  elem.value.focus();
}

function onSelectNext() {
  if (props.disabled) return;

  let index = props.optionKey ? props.options.findIndex((o) => o[props.optionKey] === selectedKey.value) : props.options.indexOf(selected.value);
  selectIndex(index+1 >= props.options.length ? 0 : index+1);
}

function onSelectPrev() {
  if (props.disabled) return;

  let index = props.optionKey ? props.options.findIndex((o) => o[props.optionKey] === selectedKey.value) : props.options.indexOf(selected.value);
  selectIndex(index <= 0 ? props.options.length-1 : index-1);
}

let search = '';
let debounceTimer = null;
function onKeyDown(event) {
  if (props.disabled) return;

  // only handle alphanumerics
  if (!/^[a-zA-Z0-9]$/.test(event.key)) return;

  const key = event.key.toLowerCase();

  if (debounceTimer) clearTimeout(debounceTimer);
  debounceTimer = setTimeout(() => search = '', 500);

  search += key;

  const found = menuModel.value.findIndex(i => {
    if (i.label && i.label.toLowerCase().indexOf(search) === 0) return true;
    return false;
  });

  if (found !== -1) selectIndex(found);

  event.stopPropagation();
}

function handleDefaultSelect() {
  if (props.optionKey) {
    selectedKey.value = model.value;
    selected.value = props.options.find((o) => o[props.optionKey] === selectedKey.value);
  } else {
    selected.value = model.value;
  }
}

watchEffect(handleDefaultSelect);

</script>

<template>
  <div class="pankow-singleselect" :class="{ 'pankow-singleselect-disabled': disabled }" ref="elem" tabindex="0" @click="onClick" @keydown.enter="onOpen" @keydown.down.stop.prevent="onSelectNext" @keydown.up.stop.prevent="onSelectPrev" @keydown="onKeyDown($event)">
    <!-- native select for required and accessibility handling -->
    <select v-model="selectedKey" :id="id" ref="nativeSelect" :required="$attrs['required']" style="display: none">
      <option value=""></option>
      <option v-for="item in options" :value="optionKey ? item[optionKey] : item">{{ item[optionLabel] }}</option>
    </select>

    <Menu ref="menu" :model="menuModel" :search-threshold="searchThreshold" :close-on-activation="true" @close="onClose"/>
    <span class="pankow-singleselect-label">
      <Icon v-if="selected ? selected.icon : false" :icon="selected.icon" style="margin-right: 6px" />
      {{ selected ? selected[optionLabel] : placeholder }}
    </span>
    <Icon icon="fa-solid fa-chevron-down" class="pankow-button-icon-right-with-text" />
  </div>
</template>

<style>

.pankow-singleselect {
  display: inline-flex;
  justify-content: space-between;
  align-items: center;
  font-weight: 400;
  font-size: 14px;
  color: var(--pankow-color-dark);
  user-select: none;
  text-align: center;
  border-width: 1px;
  border-style: solid;
  border-color: var(--pankow-input-border-color);
  padding: var(--pankow-input-vertial-padding) max(12px, var(--pankow-input-horizontal-padding));
  border-radius: var(--pankow-border-radius);
  background-color: var(--pankow-input-background-color);
  text-decoration: none;
  cursor: pointer;
  transition: background-color 250ms;
  min-width: 100px;
  white-space: nowrap;
  overflow: hidden;
}

@media (prefers-color-scheme: dark) {
  .pankow-singleselect {
    color: var(--pankow-color-light-dark);
  }
}

.pankow-singleselect:focus {
  outline: none;
  border-color: var(--pankow-color-dark) !important;
}

.pankow-singleselect:hover {
  border-color: var(--pankow-color-primary-hover);
}

.pankow-singleselect-disabled,
.pankow-singleselect-disabled:hover {
  cursor: not-allowed;
  border-color: var(--pankow-input-border-color);
}

.pankow-singleselect-label {
  overflow: hidden;
  text-overflow: ellipsis;
}

</style>
