<template>
  <div class="flex relative" :class="$attrs.class">
    <select
      v-bind="defaultAttributes"
      v-model="selected"
      @change="handleChange"
      class="w-full block form-control form-select form-select-multiple"
      :multiple="true"
      ref="selectControl"
      :class="{
        'form-control-sm': size == 'sm',
        'form-control-xs': size == 'xs',
        'form-control-xxs': size == 'xxs',
        'form-select-bordered': bordered,
        ...selectClasses,
      }"
    >
      <slot />
      <template v-for="(options, group) in groupedOptions">
        <optgroup :label="group" v-if="group" :key="group">
          <option
            v-bind="attrsFor(option)"
            v-for="option in options"
            :key="option.value"
            :selected="isSelected(option)"
          >
            {{ labelFor(option) }}
          </option>
        </optgroup>
        <template v-else>
          <option
            v-bind="attrsFor(option)"
            v-for="option in options"
            :key="option.value"
            :selected="isSelected(option)"
          >
            {{ labelFor(option) }}
          </option>
        </template>
      </template>
    </select>
  </div>
</template>

<script>
import groupBy from 'lodash/groupBy'
import map from 'lodash/map'
import omit from 'lodash/omit'

export default {
  emits: ['change'],

  inheritAttrs: false,

  props: {
    options: {
      type: Array,
      default: [],
    },
    label: { default: 'label' },
    selected: {},
    size: {
      type: String,
      default: 'md',
      validator: val => ['xxs', 'xs', 'sm', 'md'].includes(val),
    },
    bordered: {
      type: Boolean,
      default: true,
    },
    selectClasses: {
      type: [String, Object, Array],
    },
  },

  methods: {
    labelFor(option) {
      return this.label instanceof Function
        ? this.label(option)
        : option[this.label]
    },

    attrsFor(option) {
      return {
        ...(option.attrs || {}),
        ...{ value: option.value },
      }
    },

    isSelected(option) {
      return this.selected.indexOf(option.value) > -1
    },

    handleChange(event) {
      let selected = map(event.target.selectedOptions, option => {
        return option.value
      })

      this.$emit('change', selected)
    },

    resetSelection() {
      this.$refs.selectControl.selectedIndex = 0
    },
  },

  computed: {
    defaultAttributes() {
      return omit(this.$attrs, ['class'])
    },

    groupedOptions() {
      return groupBy(this.options, option => option.group || '')
    },
  },
}
</script>