<script setup>

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

const props = defineProps({
  placeholder: {
    type: String,
    default: 'Select'
  },
  selectedLabel: {
    type: String,
    default: '%n selected'
  },
  selectAllLabel: {
    type: String,
    default: '',
  },
  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();

let selected = ref([]);

const elem = useTemplateRef('elem');
const menu = useTemplateRef('menu');
const nativeSelect = useTemplateRef('nativeSelect');

watch(model, (newValue, oldValue) => {
  selected.value = newValue;
});

const buttonLabel = computed(() => {
  return props.selectedLabel.replace('%n', selected.value.length);
});

const allSelected = ref(false);

const menuModel = computed(() => {
  const ret = props.options.map((item) => {
    if (item.separator) {
      return {
        separator: true,
        label: item[props.optionLabel],
      };
    } else {
      return {
        label: item[props.optionLabel],
        checkbox: true,
        checked: selected.value.indexOf(props.optionKey ? item[props.optionKey] : item) !== -1,
        action: () => {
          const idx = selected.value.indexOf(props.optionKey ? item[props.optionKey] : item);

          if (idx >= 0) {
            selected.value.splice(idx, 1);
            allSelected.value = false;
          } else {
            selected.value.push(props.optionKey ? item[props.optionKey] : item);
          }

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

          // let the browser know about the changes
          // multi selects work different than single, we set the value of the first selected item only to react to required
          nativeSelect.value.value = selected.value.length ? selected.value[0] : null;
          nativeSelect.value.dispatchEvent(new Event('input', { bubbles: true }));
          nativeSelect.value.dispatchEvent(new Event('change', { bubbles: true }));
        }
      };
    }
  });

  if (props.selectAllLabel) {
    ret.unshift({
      label: props.selectAllLabel || '',
      checkbox: true,
      checked: allSelected.value,
      // checked: props.options.filter((item) => !item.separator).every((item) => item.checked),
      action: () => {
        allSelected.value = !allSelected.value;

        if (allSelected.value) {
          selected.value = props.options.filter((item) => !item.separator).map((item) => props.optionKey ? item[props.optionKey] : item);
        } else {
          selected.value = [];
        }

        model.value = selected.value;
        emits('select', selected.value);
      }
    }, {
      separator: true,
      label: '',
    });
  }

  return ret;
});

function isSelected(item) {
  return selected.value.indexOf(props.optionKey ? item[props.optionKey] : item) !== -1;
}

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() {
  elem.value.focus();
  emits('close');
}

function onEscape(event) {
  if (menu.value.isOpen) {
    event.stopPropagation();
    menu.value.close();
    onClose();
  }
}

onMounted(() => {
  selected.value = model.value;
});

</script>

<template>
  <div class="pankow-multiselect" :class="{ 'pankow-multiselect-disabled': disabled }" ref="elem" tabindex="0" @click="onClick" @keydown.enter="onOpen" @keydown.down.stop="onOpen" @keydown.up.stop="onOpen" @keydown.esc="onEscape">
    <!-- native select for required and accessibility handling -->
    <select ref="nativeSelect" :required="$attrs['required']" multiple style="display: none">
      <option value=""></option>
      <option v-for="item in options" :value="optionKey ? item[optionKey] : item" :selected="isSelected(item) ? true : null">{{ optionKey }} - {{ item[optionLabel] }}</option>
    </select>

    <Menu ref="menu" :model="menuModel" :close-on-activation="false" @close="onClose" :search-threshold="searchThreshold"></Menu>
    {{ buttonLabel }}
    <Icon icon="fa-solid fa-chevron-down" class="pankow-button-icon-right-with-text" />
  </div>
</template>

<style>

.pankow-multiselect {
  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);
  background-color: var(--pankow-input-background-color);
  padding: var(--pankow-input-vertial-padding) max(12px, var(--pankow-input-horizontal-padding));
  border-radius: var(--pankow-border-radius);
  text-decoration: none;
  cursor: pointer;
  transition: background-color 250ms;
  min-width: 100px;
  white-space: nowrap;
}

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

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

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

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

</style>
