<template>
  <div
    :class="containerClass"
    :style="style"
    class="vc-tag-input__wrap"
  >
    <div
      v-if="label || labelIcon"
      class="vc-tag-input__label-wrap"
    >
      <label class="vc-tag-input__label">
        <span
          v-if="labelIcon"
          class="vc-tag-input__label-icon-wrap"
        >
          <i
            :class="labelIcon"
            class="icon-lg vc-tag-input__label-icon text-icons"
          />
        </span>
        <span
          v-if="label"
          class="vc-tag-input__label-text"
        >
          {{ label }}
          <span
            v-if="required"
            class="vc-tag-input__label-required-icon"
          >
            *
          </span>
        </span>
      </label>
    </div>
    <div
      :class="[
        ...inputClasses,
        {
          'is-clearable': clearable,
          'is-prefix-icon': prefixIcon,
          'is-error': !!error,
          'is-disabled': hasDisabled
        }
      ]"
      class="vc-tag-input__container"
    >
      <div
        v-if="prefixIcon"
        class="vc-tag-input__prefix-icon"
      >
        <i
          :class="prefixClasses"
          :style="prefixStyle"
        />
      </div>
      <div
        class="vc-tag-input__content"
        @click="onWrapperClick"
      >
        <div class="vc-tag-input__tags flex flex-wrap">
          <template
            v-for="(item, index) in modelValue"
            :key="`tag-${item}-${index}`"
          >
            <div class="vc-tag-input__tag-item">
              <VcTag
                :label="item"
                :color="tagColor"
                :max-content="tagMaxContent"
                :borderless="tagBorderless"
                closable
                @on-close="removeTag($event, index)"
              />
            </div>
          </template>
          <div class="vc-tag-input__input-token">
            <input
              ref="tagInput"
              :data-test="dataTestName"
              type="text"
              class="vc-tag-input__input"
              :disabled="hasDisabled"
              v-bind="$attrs"
              @focus="onFocus"
              @blur="onBlur($event)"
              @input="onInput"
              @keydown="onKeyDown($event)"
              @paste="onPaste($event)"
            >
          </div>
        </div>
      </div>
      <div
        v-if="clearable"
        class="vc-tag-input__clearable"
      >
        <i
          class="vc-icon-close icon-xxs"
          @click="clear"
        />
      </div>
    </div>
    <div
      v-if="!dynamicErrorSpace || (dynamicErrorSpace && error)"
      class="vc-tag-input__error-wrapper"
    >
      <span class="vc-tag-input__error-wrapper--text">
        {{ error }}
      </span>
    </div>
  </div>
</template>

<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import { defineComponent, PropType } from 'vue'
import { VcTagColorType } from '@/components/VcTag/VcTag.types'
import VcTag from '@/components/VcTag/VcTag.vue'
import baseInputMixin from '@/mixins/baseInput.mixin'

const CLASS_FOR_COLOR = 'vc-tag-input-color--'
const CLASS_FOR_ICON_COLOR = 'vc-tag-input-icon-color--'

export default defineComponent({
  name: 'VcTagInput',
  components: { VcTag },
  mixins: [baseInputMixin],
  inheritAttrs: false,
  props: {
    modelValue: {
      type: Array,
      default: () => []
    },
    clearable: {
      type: Boolean,
      default: false
    },
    max: {
      type: Number,
      default: null
    },
    separator: {
      type: String,
      default: ''
    },
    addOnBlur: {
      type: Boolean,
      default: false
    },
    allowDuplicate: {
      type: Boolean,
      default: true
    },
    class: {
      type: String,
      default: ''
    },
    style: {
      type: [String, Object],
      default: null
    },
    tagColor: {
      type: String as PropType<VcTagColorType>,
      default: 'primary'
    },
    tagMaxContent: {
      type: [Number, String],
      default: ''
    },
    tagBorderless: {
      type: Boolean,
      default: false
    }
  },
  emits: ['update:modelValue', 'add', 'remove'],
  data: () => ({
    inputValue: '',
    focused: false,
    colorClass: CLASS_FOR_COLOR,
    iconColorClass: CLASS_FOR_ICON_COLOR
  }),
  computed: {
    hasDisabled (): boolean {
      return (this.disabled || this.maxedOut)
    },
    inputClasses (): (string)[] {
      const colorClass = this.color ? (this.colorClass + this.color) : ''
      return [
        colorClass
      ]
    },
    maxedOut (): boolean {
      return !!(this.max && this.modelValue?.length && this.max === this.modelValue.length)
    },
    containerClass () {
      return [this.class, {
        'vc-tag-input__wrap--filled': !!((this.modelValue && this.modelValue.length) || (this.inputValue && this.inputValue.length)),
        'vc-tag-input__wrap--focus': this.focused,
        'vc-tag-input__wrap--error': !!this.error,
        'vc-tag-input__wrap--disabled': this.hasDisabled
      }]
    }
  },
  methods: {
    clear (): void {
      this.$emit('update:modelValue', [])
    },
    removeTag (evt: Event, index: number): void {
      this.removeItem(evt, index)
    },
    onFocus (): void {
      this.focused = true
    },
    onBlur (event: FocusEvent): void {
      this.focused = false
      if (this.addOnBlur) {
        const target = event.target as HTMLInputElement
        this.addItem(event, target?.value, false)
      }
    },
    addItem (event: Event, item: string, preventDefault: boolean | undefined): void {
      if (item && item.trim().length) {
        const value = this.modelValue ? [...this.modelValue] : []
        if (this.allowDuplicate || value.indexOf(item) === -1) {
          value.push(item)
          this.updateModel(event, value, preventDefault)
        }
      }
    },
    updateModel (event: Event, value: unknown[], preventDefault: boolean | undefined): void {
      this.$emit('update:modelValue', value)
      this.$emit('add', {
        originalEvent: event,
        value: value
      })
      const input = this.$refs.tagInput as HTMLInputElement
      input.value = ''
      this.inputValue = ''
      if (preventDefault) {
        event.preventDefault()
      }
    },
    onWrapperClick (): void {
      const input = this.$refs.tagInput as HTMLElement
      input.focus()
    },
    onInput (event: InputEvent): void {
      const target = event.target as HTMLInputElement
      this.inputValue = target?.value
    },
    onKeyDown (event: KeyboardEvent): void {
      const target = event.target as HTMLInputElement
      const inputValue = target.value
      switch (event.key) {
        // backspace
        case 'Backspace':
          if (inputValue.length === 0 && this.modelValue && this.modelValue.length > 0) {
            this.removeItem(event, this.modelValue.length - 1)
          }
          break
        // enter
        case 'Enter':
          if (inputValue && inputValue.trim().length && !this.maxedOut) {
            this.addItem(event, inputValue, true)
          }
          break
        default:
          if (this.separator) {
            if (this.separator === ',' && (event.which === 188 || event.which === 110)) {
              this.addItem(event, inputValue, true)
            }
          }
          break
      }
    },
    removeItem (event: Event, index: number): void {
      if (this.$attrs.disabled) {
        return
      }
      const values = [...this.modelValue]
      const removedItem = values.splice(index, 1)
      this.$emit('update:modelValue', values)
      this.$emit('remove', {
        originalEvent: event,
        value: removedItem
      })
    },
    onPaste (event: ClipboardEvent): void {
      if (this.separator) {
        const pastedData = (event.clipboardData || (window as any).clipboardData).getData('Text')
        if (pastedData) {
          let value = (this.modelValue || []) as any[]
          let pastedValues = pastedData.split(this.separator)
          pastedValues = pastedValues.filter((val: string) => (this.allowDuplicate || value.indexOf(val) === -1))
          value = [...value, ...pastedValues]
          this.updateModel(event, value, true)
        }
      }
    }
  }
})
</script>

<style lang="scss" scoped>
  @import "./VcTagInputStyle";
</style>
