<template>
  <div
    v-bind="$attrs"
    class="vc-checkbox-group-wrapper"
    :data-test="`checkbox-group-${dataTestName}`"
  >
    <VcCheckbox
      hand-update
      :indeterminate="isSelectedEl && !isSelectedAll"
      :checkbox-class="checkboxClass"
      :checked="isSelectedAll"
      :label="selectAllLabelText"
      @input="handleCheckAllChange"
    />
    <VcCheckbox
      v-for="(item, i) in checkboxes"
      :key="i"
      :checkbox-class="checkboxClass"
      :checkbox-label-class="checkboxLabelClass"
      :checked="isCheckboxChecked(item)"
      :label="getLabel(item)"
      @input="(checked) => checkType(checked, item)"
    />
  </div>
</template>

<script lang="ts">
import cloneDeep from 'lodash/cloneDeep'
import isEqual from 'lodash/isEqual'
import difference from 'lodash/difference'
import { defineComponent } from 'vue'
import VcCheckbox from '@/components/VcCheckbox/VcCheckbox.vue'

export default defineComponent({
  name: 'VcCheckboxGroup',
  components: {
    VcCheckbox
  },
  // model: {
  //   prop: 'value'
  // },
  props: {
    value: {
      type: Array,
      default: () => []
    },
    valueKey: {
      type: String,
      default: ''
    },
    checkboxes: {
      type: Array,
      default: () => []
    },
    checkboxClass: {
      type: String,
      default: ''
    },
    checkboxLabelClass: {
      type: String,
      default: ''
    },
    selectAllLabel: {
      type: String,
      default: ''
    },
    dataTestName: {
      type: String,
      default: 'default'
    },
    onInput: {
      type: Function,
      default: null,
      description: 'Temporary @input method'
    }
  },
  emits: ['input', 'update:value'],
  data () {
    return {
      model: [] as string[]
    }
  },
  computed: {
    isSelectedEl () {
      return !!(this.model && this.model.length)
    },
    isSelectedAll () {
      return this.model.length === this.checkboxes.length
    },
    selectAllLabelText () {
      return this.selectAllLabel ? this.selectAllLabel : this.$tVU.t('checkbox.group.choose.all')
    }
  },
  watch: {
    value: {
      handler (newV: []) {
        if (newV && difference(newV, this.model).length) {
          this.model = newV
        }
      },
      deep: true,
      immediate: true
    },
    model: {
      handler (newV: []) {
        this.update(newV)
      },
      deep: true
    }
  },
  methods: {
    checkType (checked: boolean, data: {string?: unknown}): void {
      const val = (this.valueKey ? (data as never)[this.valueKey] : data) as string
      if (checked) {
        if (!this.model.includes(val)) {
          this.model.push(val)
        }
        return
      }
      // added isEqual because we model cloned - model and VcCheckbox arrays are different
      if (this.model.includes(val)) {
        this.model = this.model.filter(item => !isEqual(item, val))
      }
    },
    getLabel (data: {string?: unknown} | string): string {
      return this.valueKey ? (data as never)[this.valueKey] : data as string
    },
    isCheckboxChecked (data: {string?: unknown}): boolean {
      const val = this.valueKey ? (data as never)[this.valueKey] : data
      const filtered = this.model.filter(item => isEqual(item, val)) || []
      return !!filtered.length
    },
    handleCheckAllChange (): void {
      if (this.isSelectedEl && this.isSelectedAll) {
        this.clearSelected()
        return
      }
      this.setSelectedAll()
    },
    clearSelected (): void {
      this.model = []
    },
    setSelectedAll (): void {
      if (!this.valueKey) {
        this.model = cloneDeep(this.checkboxes) as never
        return
      }
      this.model = this.checkboxes.map(item => (item as never)[this.valueKey]) as []
    },
    update (value: []): void {
      if (this.$attrs && this.$attrs.onInput) {
        this.$emit('input', value)
      } else if (this.onInput && typeof this.onInput === 'function') {
        this.onInput(value)
      } else {
        this.$emit('update:value', value)
      }
    }
  }
})
</script>

<style lang="scss" scoped>
.vc-checkbox-group-wrapper {
  @apply w-full;
}
</style>
