<template>
  <div
    class="ant-input-tag input-tag-wrapper"
    :class="[size ? 'ant-input-tag--' + size : '']"
    @click="focusTagInput"
  >
    <a-tag
      v-for="(tag, idx) in innerTags"
      :key="tag"
      :size="size"
      color="blue"
      closable
      @close="() => remove(idx)"
    >
      {{ tag }}
    </a-tag>
    <input
      v-if="!readOnly"
      class="tag-input"
      :placeholder="placeholder"
      :value="newTag"
      @input="inputTag"
      @keydown="handleKeydown"
      @blur="addNew"
      @keydown.delete.stop="removeLastTag"
    />
  </div>
</template>

<script lang="ts" setup>
import { ref, watch, withDefaults, defineProps, defineEmits } from 'vue'
import { Tag } from 'ant-design-vue'
import { StrUtil } from '../../common/StrUtil'

interface Props {
  value?: string
  addTagOnKeys?: number[]
  size?: 'small' | 'default' | 'large'
  placeholder?: string
}

const props = withDefaults(defineProps<Props>(), {
  value: '',
  addTagOnKeys: () => [13, 188, 32], // Enter, Comma, Space
  size: 'small',
  placeholder: ''
})

const emit = defineEmits<{
  (e: 'update:value', value: string): void
  (e: 'input', tags: string[]): void
  (e: 'change', tags: string[]): void
}>()

const newTag = ref('')
const innerTags = ref<string[]>([])
const readOnly = ref(false)

// 监听value变化
watch(() => props.value, (newVal) => {
  if (StrUtil.isNotBlank(newVal)) {
    innerTags.value = newVal.split(',')
  } else {
    innerTags.value = []
  }
}, { immediate: true })

// 监听innerTags变化
watch(innerTags, (newTags) => {
  const tagString = newTags.join(',')
  emit('update:value', tagString)
  emit('input', newTags)
  emit('change', newTags)
})

const focusTagInput = () => {
  if (readOnly.value || !document.querySelector('.tag-input')) {
    return
  }
  const input = document.querySelector('.tag-input') as HTMLInputElement
  input?.focus()
}

const inputTag = (ev: Event) => {
  const target = ev.target as HTMLInputElement
  newTag.value = target.value
}

const handleKeydown = (e: KeyboardEvent) => {
  if (props.addTagOnKeys.includes(e.keyCode)) {
    e.preventDefault()
    addNew(e)
  }
}

const addNew = (e?: Event) => {
  if (e) {
    e.stopPropagation()
    e.preventDefault()
  }
  
  let addSuccess = false
  if (newTag.value.includes(',')) {
    newTag.value.split(',').forEach(item => {
      if (addTag(item.trim())) {
        addSuccess = true
      }
    })
  } else {
    if (addTag(newTag.value.trim())) {
      addSuccess = true
    }
  }
  
  if (addSuccess) {
    newTag.value = ''
  }
}

const addTag = (tag: string): boolean => {
  tag = tag.trim()
  if (tag && !innerTags.value.includes(tag)) {
    innerTags.value.push(tag)
    return true
  }
  return false
}

const remove = (index: number) => {
  innerTags.value.splice(index, 1)
}

const removeLastTag = () => {
  if (newTag.value) {
    return
  }
  innerTags.value.pop()
}
</script>

<style scoped>
.ant-form-item-has-error .ant-input-tag {
  border-color: #ff4d4f;
}

.input-tag-wrapper {
  position: relative;
  font-size: 14px;
  background-color: #fff;
  background-image: none;
  border-radius: 6px;
  border: 1px solid #d9d9d9;
  box-sizing: border-box;
  color: rgba(0, 0, 0, 0.85);
  display: inline-block;
  outline: none;
  padding: 0 11px;
  transition: all 0.3s;
  width: 100%;
  min-height: 32px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  cursor: text;
}

.input-tag-wrapper:hover {
  border-color: #40a9ff;
}

.input-tag-wrapper:focus-within {
  border-color: #40a9ff;
  box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
  outline: 0;
}

.ant-tag {
  margin: 0;
  flex-shrink: 0;
}

.tag-input {
  background: transparent;
  border: 0;
  font-size: 14px;
  outline: none;
  padding: 4px 0;
  flex: 1;
  min-width: 60px;
  height: 30px;
}

.tag-input::placeholder {
  color: #bfbfbf;
}

.ant-input-tag--small {
  min-height: 24px;
  padding: 0 7px;
}

.ant-input-tag--small .tag-input {
  height: 22px;
  font-size: 12px;
}

.ant-input-tag--default {
  min-height: 32px;
}

.ant-input-tag--large {
  min-height: 40px;
  padding: 0 11px;
}

.ant-input-tag--large .tag-input {
  height: 38px;
  font-size: 16px;
}
</style>