<template lang="pug">
component(
  ref="formEl"
  :is="formRegister ? 'w-form-element' : 'div'"
  v-bind="formRegister && { validators, inputValue: checkboxItems.some(item => item._isChecked), disabled: isDisabled, noBlurValidation }"
  v-model:valid="valid"
  @reset="reset"
  :column="!inline"
  :wrap="inline"
  :class="classes")
  w-checkbox(
    ref="item"
    v-for="(item, i) in checkboxItems"
    :key="i"
    :model-value="item._isChecked"
    @update:model-value="toggleCheck(item, $event)"
    @focus="$emit('focus', $event)"
    :name="`${inputName}[]`"
    v-bind="{ label: item.label, color: item.color, labelOnLeft, labelColor, round }"
    :disabled="isDisabled || null"
    :readonly="isReadonly || null"
    :class="{ mt1: !inline && i }")
    slot(
      v-if="$slots[`item.${i + 1}`] || $slots.item"
      :name="$slots[`item.${i + 1}`] ? `item.${i + 1}` : 'item'"
      :item="getOriginalItem(item)"
      :checked="!!item._isChecked"
      :index="i + 1"
      v-html="item.label")
    div(v-else-if="item.label" v-html="item.label")
</template>

<script>
import { reactive } from 'vue'
import FormElementMixin, { useWaveUiFormIds } from '../mixins/form-elements'
import { guardFocusable, vFocusLifecycleMixin } from '../utils/focus'

export default {
  name: 'w-checkboxes',
  focusable: true,
  expose: ['focus'],
  mixins: [FormElementMixin, vFocusLifecycleMixin],

  setup () {
    return useWaveUiFormIds()
  },

  props: {
    items: { type: Array, required: true }, // All the possible options.
    modelValue: { type: Array }, // v-model on selected option.
    // If true, the returnValue set on each w-checkboxes item will be returned once the checkbox is
    // checked. If false & by default, the return value of the w-checkboxes is an array of booleans.
    returnValues: { type: Boolean },
    labelOnLeft: { type: Boolean },
    itemLabelKey: { type: String, default: 'label' },
    itemValueKey: { type: String, default: 'value' },
    itemColorKey: { type: String, default: 'color' }, // Support a different color per item.
    inline: { type: Boolean },
    round: { type: Boolean },
    color: { type: String, default: 'primary' },
    labelColor: { type: String, default: 'primary' },
    // Sizes.
    xs: { type: Boolean },
    sm: { type: Boolean },
    md: { type: Boolean },
    lg: { type: Boolean },
    xl: { type: Boolean }
    // Props from mixin: id, name, disabled, readonly, required, validators.
    // Computed from mixin: inputName, isDisabled & isReadonly.
  },

  emits: ['input', 'update:modelValue', 'focus'],

  provide () {
    // Disable w-form-el wrapping in each w-checkbox when inside a w-checkboxes component that already
    // does it.
    // Don't do a simple prop that can be turned on and off by user.
    return { wCheckboxes: true }
  },

  computed: {
    checkboxItems () {
      return (this.items || []).map((item, i) => {
        const itemValue = item[this.itemValueKey] === undefined ? (item[this.itemLabelKey] || i) : item[this.itemValueKey]

        return reactive({
          ...item,
          label: item[this.itemLabelKey],
          _index: i,
          value: itemValue, // If no value is set then add one to prevent error.
          color: item[this.itemColorKey] || this.color,
          _isChecked: this.modelValue?.includes(itemValue)
        })
      })
    },

    presetSize () {
      return (this.xs && 'xs') || (this.sm && 'sm') || (this.md && 'md') || (this.lg && 'lg') || (this.xl && 'xl') || null
    },

    classes () {
      return [
        'w-checkboxes',
        `w-checkboxes--${this.inline ? 'inline' : 'column'}`,
        this.presetSize && `size--${this.presetSize}`
      ]
    }
  },

  methods: {
    focus () {
      if (!guardFocusable(this)) return
      const items = [].concat(this.$refs.item || []).filter(Boolean)
      items.find(c => !c.isDisabled && !c.isReadonly)?.focus?.()
    },

    reset () {
      this.checkboxItems.forEach(item => (item._isChecked = null))
      this.$emit('update:modelValue', [])
      this.$emit('input', [])
    },

    toggleCheck (checkbox, isChecked) {
      checkbox._isChecked = isChecked
      const selection = this.checkboxItems.filter(item => item._isChecked).map(item => this.returnValues ? item.returnValue : item.value)

      this.$emit('update:modelValue', selection)
      this.$emit('input', selection)
    },

    // Return the original item (so there is no `_index`, etc.).
    getOriginalItem (item) {
      return this.items[item._index]
    }
  }
}
</script>

<style lang="scss">
.w-checkboxes {
  &--column {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
  }

  &--inline {
    display: inline-flex;
    flex-wrap: wrap;
    vertical-align: middle;

    .w-checkbox {margin-right: calc(var(--w-base-increment) * 3);}
    .w-checkbox:last-child {margin-right: 0;}
  }

  // Sizes (cascades --w-small-form-el-size to child w-checkbox elements).
  // ------------------------------------------------------
  &.size--xs {--w-size: round(nearest, calc(0.86 * var(--w-base-font-size)), 2px);}
  &.size--sm {--w-size: round(nearest, calc(1.14 * var(--w-base-font-size)), 2px);}
  &.size--lg {--w-size: round(nearest, calc(1.43 * var(--w-base-font-size)), 2px);}
  &.size--xl {--w-size: round(nearest, calc(1.71 * var(--w-base-font-size)), 2px);}
}
</style>
