<template>
  <div
    ref="dListWrapper"
    v-resize="updateList"
    class="vc-delimited-list-wrapper"
  >
    <div
      ref="dList"
      class="flex md:inline-flex items-center vc-delimited-list-container"
    >
      <span
        v-for="(item, index) in shortList"
        :key="index"
        class="flex items-center vc-delimited-list-item-wrapper"
      >
        <slot
          name="list-item"
          :item="item"
          :index="index"
        >
          {{ item.value }}
        </slot>
        <span
          v-if="showSeparator(index)"
          class="text-break"
        >
          <slot name="separator">{{ separator }}</slot>
        </span>
      </span>
      <template v-if="otherList.length">
        <VcPopover
          v-click-outside="onClickOutside"
          :open="open"
          :trigger="trigger"
          :max-width="maxWidth"
          :arrow-position="arrowPosition"
          :delay-close="300"
          popover-width="max-content"
        >
          <slot
            name="other-content"
            :list="otherList"
          />
          <template
            #reference
          >
            <slot
              name="reference"
              :listLength="otherList.length"
              :items="otherList"
            >
              <span class="flex items-center">
                ({{ otherList.length }})
              </span>
            </slot>
          </template>
        </VcPopover>
      </template>
    </div>
  </div>
</template>

<script lang="ts">
import { getRefElement } from '@/utils/helpers'
import { VcPopover } from '@/components'
import { resize } from '@/directives/resize'
import { defineComponent, PropType } from 'vue'
import { VcDelimitedListOptionType } from './VcDelimitedList.types'
import { ArrowPositionType } from '@/types/Dropdown.types'
import clickOutside from '@/directives/click-outside'
export default defineComponent({
  name: 'VcDelimitedList',
  components: {
    VcPopover
  },
  directives: {
    resize,
    'click-outside': clickOutside
  },
  props: {
    list: {
      type: Array as PropType<VcDelimitedListOptionType[]>,
      default: () => []
    },
    open: {
      type: Boolean,
      default: false
    },
    limit: {
      type: Number,
      default: 3
    },
    trigger: {
      type: String,
      default: ''
    },
    separator: {
      type: String,
      default: ','
    },
    showTooltip: {
      type: Boolean,
      default: true
    },
    maxWidth: {
      type: String,
      default: ''
    },
    arrowPosition: {
      type: String as PropType<ArrowPositionType>,
      default: 'top-center'
    }
  },
  emits: ['click-outside'],
  data () {
    return {
      localLimit: 1 as number,
      oldLocalLimit: 1 as number,
      minLimit: 1 as number
    }
  },
  computed: {
    shortList () {
      return this.list.slice(0, this.localLimit)
    },
    otherList () {
      return this.list.length > this.localLimit ? this.list.slice(this.localLimit) : []
    }
  },
  watch: {
    limit () {
      this.updateList()
    },
    'list.length' () {
      this.updateList()
    }
  },
  mounted () {
    this.updateList()
  },
  methods: {
    showSeparator (index: number): boolean | number {
      return (index < this.shortList.length - 1 && !this.otherList.length) ||
        (index < this.shortList.length && this.otherList.length)
    },
    initLimit (): void {
      this.localLimit = 1
    },
    onClickOutside () {
      this.$emit('click-outside')
    },
    async isListSmaller ({ listWidth, listWrapperWidth, list, listWrapper }:
                           {listWidth:number, listWrapperWidth:number, list: Element, listWrapper:Element}): Promise<void> {
      while (listWidth < listWrapperWidth) {
        if (this.localLimit > this.limit) {
          this.localLimit = this.limit
          break
        }
        this.localLimit++
        await this.$nextTick()
        listWidth = list.clientWidth
        listWrapperWidth = listWrapper.clientWidth

        if (listWidth > listWrapperWidth) {
          if (this.localLimit > this.minLimit && this.localLimit <= this.limit) {
            this.localLimit--
          } else if (this.localLimit > this.limit) {
            this.localLimit = this.limit
          } else if (this.localLimit < this.minLimit) {
            this.localLimit = this.minLimit
          }
          break
        }
      }
    },
    async isListBigger ({ listWidth, listWrapperWidth, list, listWrapper }:
                          {listWidth:number, listWrapperWidth:number, list: Element, listWrapper:Element}): Promise<void> {
      while (listWidth > listWrapperWidth) {
        if (this.localLimit < this.minLimit) {
          this.localLimit = this.minLimit
          break
        }
        this.localLimit--
        await this.$nextTick()
        listWidth = list.clientWidth
        listWrapperWidth = listWrapper.clientWidth

        if (listWidth < listWrapperWidth) {
          if (this.localLimit > this.limit) {
            this.localLimit = this.limit
          } else if (this.localLimit < this.minLimit) {
            this.localLimit = this.minLimit
          }
          break
        }
      }
    },
    async updateLocalLimit ({ list, listWrapper }:{ list:Element, listWrapper:Element }): Promise<void> {
      const listWidth = list.clientWidth
      const listWrapperWidth = listWrapper.clientWidth

      if (listWidth < listWrapperWidth) {
        await this.isListSmaller({ listWidth, listWrapperWidth, list, listWrapper })
      } else if (listWidth > listWrapperWidth) {
        await this.isListBigger({ listWidth, listWrapperWidth, list, listWrapper })
      }
      this.oldLocalLimit = this.localLimit
    },
    async updateList (): Promise<void> {
      this.initLimit()
      const listRef = this.$refs.dList as Element
      const listWrapperRef = this.$refs.dListWrapper as Element

      if (listRef && listWrapperRef) {
        const list = getRefElement(listRef) as Element
        const listWrapper = getRefElement(listWrapperRef) as Element
        await this.$nextTick()
        await this.updateLocalLimit({ list, listWrapper })
      }
    }
  }
})
</script>

<style scoped lang="scss">
.vc-delimited-list-wrapper {
  @apply w-full;
}
.text-break {
  @apply mx-1;
}

</style>
