<template>
  <VcPopover
    v-click-outside="closeDropdown"
    trigger="manual"
    :open="open"
    popover-width="100%"
    :max-width="maxWidth"
    arrow-position="top-right"
    class="vc-dropdown-wrapper"
  >
    <template #reference>
      <VcMultipleSelectbox
        v-model="searchQuery"
        :selected-elements-list="selectedElementsList"
        :open="open"
        :options-list="localOptions"
        :config="config"
        :placeholder="placeholder"
        readonly
        v-bind="$attrs"
        @onclick="onClick"
        @handleNavigate="handleNavigate"
        @onDropdownClose="closeDropdown"
        @onblur="onBlur"
        @onchange="onChange"
        @onclear="onClear"
        @onfocus="onFocus"
        @onArrowDown="onArrowDown"
        @onArrowUp="onArrowUp"
        @onEnter="onEnter"
        @onItemDelete="deleteSelectedOption"
        @onHiddenItemsDelete="onHiddenOptionsDelete"
        @onInputWrapperClick="onInputWrapperClick"
      />
    </template>
    <div
      ref="dropdown"
      class="vc-dropdown"
      :data-test-name="dataTestName"
    >
      <div
        class="vc-dropdown-option-list"
        @scroll="loadMore"
      >
        <template v-if="$slots.default">
          <slot />
        </template>
        <template v-else>
          <div
            v-if="localOptions.length"
          >
            <div ref="options">
              <VcDropdownOption
                v-for="(option, index) in localOptions"
                :key="optionKey(option, index, config)"
                :value="optionValue(option, config)"
                :active="index === keyup_selected"
                :icon="optionIcon(option, config)"
                @click="onDropdownOptionClick(option)"
              >
                <div class="w-full">
                  <VcCheckbox
                    :checked="!!selectedElementsList.includes(optionValue(option, config))"
                    class="dropdownCheckbox w-full"
                  >
                    {{ optionLabel(option, config) }}
                  </VcCheckbox>
                </div>
              </VcDropdownOption>
            </div>
          </div>
          <div v-else-if="!localOptions.length&&!loading">
            <VcDropdownOption
              :value="null"
              disabled
              :label="noDataMessage"
            />
          </div>
        </template>
      </div>
    </div>
  </VcPopover>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue'
import VcDropdownOption from '@/components/VcDropdown/VcDropdownOption.vue'
import VcMultipleSelectbox from '@/components/VcMultipleSelectbox/VcMultipleSelectbox.vue'
import VcCheckbox from '@/components/VcCheckbox/VcCheckbox.vue'
import VcPopover from '@/components/VcPopover/VcPopover.vue'
import { LazyLoadConfig, OptionType, OptionValueType } from '@/types/OptionAndConfig.types'
import optionMethods from '@/mixins/optionMethods.mixin'
import clickOutside from '@/directives/click-outside'
import dropdownMixin from '@/mixins/dropdown.mixin'
import { JSONPath } from 'jsonpath-plus'

export default defineComponent({
  name: 'VcMultipleDropdown',
  components: {
    VcDropdownOption,
    VcPopover,
    VcMultipleSelectbox,
    VcCheckbox
  },
  directives: {
    'click-outside': clickOutside
  },
  mixins: [optionMethods, dropdownMixin],
  props: {
    modelValue: {
      type: Array as PropType<OptionType[]>,
      default: () => []
    },
    lazyLoadConfig: {
      type: Object as PropType<LazyLoadConfig>,
      default: undefined
    }
  },
  emits: ['update:modelValue'],
  data () {
    return {
      localOptions: this.options as OptionType[],
      selectedElementsList: [] as OptionType[]
    }
  },
  watch: {
    open (val) {
      if (!val) {
        this.keyup_selected = -1
        this.searchQuery = ''
      }
    },
    async searchQuery (val): Promise<void> {
      if (val && !this.open) {
        this.open = true
      }
      this.keyup_selected = -1
      this.localOptions = []
      this.lazyLoad.page = 0
      await this.loadData()
      this.localOptions = this.localOptions.filter((option) => {
        const opt = this.optionLabel(option, this.config)
        return opt.toLowerCase().includes(val.toLowerCase())
      })
    },
    modelValue: {
      immediate: true,
      deep: true,
      handler (val: OptionType[]) {
        this.selectedElementsList = [...val]
      }
    }
  },
  async created () {
    if (this.lazyLoadConfig) {
      await this.loadData()
    } else {
      this.localOptions = this.options
    }
  },
  methods: {
    async loadData (): Promise<void> {
      if (!this.lazyLoadConfig) {
        return
      }
      try {
        this.loading = true
        const result = await this.lazyLoadConfig.getDataFunc(this.lazyLoad.page++, this.searchQuery)
        this.localOptions = [...this.localOptions, ...JSONPath({ json: result, path: this.lazyLoadConfig.getDataPath })[0]]
        this.lazyLoad.total = JSONPath({ json: result, path: this.lazyLoadConfig.getPaginationTotalPath })[0]
        this.loading = false
      } catch (e) {
        console.log(e)
      }
    },
    deleteSelectedOption (id: number) {
      this.selectedElementsList = this.selectedElementsList.filter((el) => {
        return this.optionValue(el, this.config) !== id
      })

      this.$emit('update:modelValue', this.selectedElementsList)
    },
    onHiddenOptionsDelete (items: OptionValueType[]) {
      const allOptions = [...this.selectedElementsList, ...items]
      const itemsToDeleteValues = items.map(item => this.optionValue(item, this.config))

      this.selectedElementsList = allOptions.filter((el) => {
        return !itemsToDeleteValues.includes(this.optionValue(el, this.config))
      })

      this.$emit('update:modelValue', this.selectedElementsList)
    },
    onInputWrapperClick () {
      if (!this.open) {
        this.open = true
      }
    },
    onArrowDown (ev: Event): void {
      ev.preventDefault()

      if (this.keyup_selected <= this.localOptions.length - 1) {
        this.keyup_selected = +this.keyup_selected + 1
        this.fixScrolling()
      }
    },
    onArrowUp (ev: Event): void {
      ev.preventDefault()

      if (this.keyup_selected >= 0) {
        this.keyup_selected = +this.keyup_selected - 1
        this.fixScrolling()
      }
    },
    onEnter (): void {
      if (this.keyup_selected || this.keyup_selected === 0) {
        const activeElem = this.localOptions[this.keyup_selected]
        this.addToSelectedList(activeElem)
      }
    },
    loadMore (): void {
      if (this.LOAD_MORE_BLOCK.scrollTop === (this.LOAD_MORE_BLOCK.scrollHeight - this.LOAD_MORE_BLOCK.offsetHeight) &&
        this.localOptions.length < this.lazyLoad.total) {
        this.loadData()
      }
    },
    addToSelectedList (option: OptionType): void {
      const foundOption = this.selectedElementsList.find((el) => {
        return el === this.optionValue(option, this.config)
      })
      if (!foundOption) {
        this.selectedElementsList.push(this.optionValue(option, this.config))
      } else {
        this.selectedElementsList = this.selectedElementsList.filter((el) =>
          el !== this.optionValue(option, this.config))
      }
      this.$emit('update:modelValue', this.selectedElementsList)
    },
    onDropdownOptionClick (option: OptionType): void {
      const onclickFuncKey = this.config.onClickFuncKey

      if (typeof option === 'object' && onclickFuncKey !== undefined) {
        const onClickFunc = option[onclickFuncKey]

        if (typeof onClickFunc === 'function') {
          onClickFunc()

          return
        }
      }
      this.addToSelectedList(option)
    },
    onClick (): void {
      this.$nextTick(() => {
        this.LOAD_MORE_BLOCK = document.querySelector('.vc-dropdown-option-list') as HTMLElement
      })
      this.open = !this.open
    },
    onClear (): void {
      this.searchQuery = ''
    }
  }
})
</script>

<style lang="scss" scoped>
.vc-dropdown-wrapper{
  .vc-dropdown {
    &-option-list {
      max-height: 252px;
      @apply overflow-auto;

      :deep(.vc-dropdown-option:last-child:after) {
        @apply hidden;
      }
      .dropdownCheckbox {
          :deep(.vc-form-check-label) {
            width: inherit;
            .vc-form-check-label-text {
              @apply truncate block w-full;
            }
          }
      }
    }
  }
}
:deep(.vc-form-check-sign) {
  flex-shrink: 0;
}
</style>
