<template>
  <div class="tag-input-wrapper">
    <!-- 输入区域 -->
    <div class="input-wrapper">
      <div 
        ref="editableArea" 
        class="editable-area" 
        contenteditable="true" 
        @blur="saveCaretPosition"
        @focus="restoreCaretPosition" 
        @keydown.delete="handleDelete"
      ></div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { onMounted, onUnmounted, ref, watch } from 'vue'

/** 定义组件 */
defineOptions({ name: 'TagInputWrapper' })

/** 定义属性 */
const props = defineProps({
   initValue: {  type: String, default: '' },
})

/** 定义事件 */
const emit = defineEmits(['deleteTag']) 

const editableArea = ref<any>(null) /* 编辑区域 */
const lastCaretPosition = ref<any>(0) /* 光标位置 */

/* 监听外部数据变化 */
watch(() => props.initValue, (newValue) => {
  if (!editableArea.value) return;
  // 更新内容
  editableArea.value.innerHTML = unparseText(newValue);
});

/** 绑定删除标签事件 */
const bindRemoveTagEvent = (event: any) => {
  const tagId = event.target.getAttribute('data-id')
  if (event.target.tagName === 'svg' && tagId) { 
    emit('deleteTag', { id: tagId });
    event.target.parentNode.parentNode.remove()
  }
}
/** 批量插入标签创建dom*/
const insertTags = (tags: Array<any>) => {
  const result = [];
  for (let index = 0; index < tags.length; index++) {
    const element = tags[index];
    const tagElement: any = document.createElement('span');
    tagElement.className = 'el-tag is-closable el-tag--primary el-tag--light tag-input-wrapper-span';
    tagElement.setAttribute('data-id', element.id);
    tagElement.contentEditable = 'false';
    tagElement.style.margin = '0 3px';
    tagElement.innerHTML = `<span class="el-tag__content">${element.name}</span><i class="el-icon el-tag__close"><svg data-id="${element.id}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="M764.288 214.592 512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z"></path></svg></i>`;
    result.push(tagElement)
  }
  insertTag(result)
}

/** 删除标签：删除当前光标前面的标签  */
const handleDelete = (event: any) => {
  const selection: any = window.getSelection();
  if (selection.rangeCount === 0) return;

  const range = selection.getRangeAt(0);
  const startContainer = range.startContainer;

  // 如果光标在标签前面，删除标签
  if (startContainer.previousSibling && startContainer.previousSibling.classList &&
    startContainer.previousSibling.classList.contains('tag')) {
    startContainer.previousSibling.remove();
    event.preventDefault();
  }
};

/** 恢复光标位置：将光标恢复到上次保存的位置 */
const restoreCaretPosition = () => {
  if (lastCaretPosition.value) {
    const selection: any = window.getSelection();
    selection.removeAllRanges();
    selection.addRange(lastCaretPosition.value);
  }
};

/** 备份当前光标位置 */
const saveCaretPosition = () => {
  const selection: any = window.getSelection();
  if (selection.rangeCount > 0) {
    lastCaretPosition.value = selection.getRangeAt(0);
  }
};

/** 插入标签：将标签插入到编辑器中 */
const insertTag = (tags: Array<any>) => {
  const selection: any = window.getSelection();
  selection.removeAllRanges();

  // 确保 lastCaretPosition 是一个有效的 Range 对象
  if (lastCaretPosition.value && lastCaretPosition.value instanceof Range) {
    selection.addRange(lastCaretPosition.value);
  } else {
    // 如果无效，则创建一个新的 Range
    const range = document.createRange();
    const editor = editableArea.value;
    if (editor) {
      range.selectNodeContents(editor);
      range.collapse(false);
    }
    selection.addRange(range);
  }

  const range = selection.getRangeAt(0);
  for (let index = 0; index < tags.length; index++) {
    const tagElement = tags[index];
    range.insertNode(tagElement);
  }
  range.collapse(false);
};

/** 解析函数：将HTML字符串转换为文本格式 */
const parseHtml = (htmlStr: string) => {
  // 创建临时div容器
  const tempDiv = document.createElement('div');
  tempDiv.innerHTML = htmlStr;
  
  // 遍历所有子节点
  let result = '';
  
  const traverseNodes = (node: any) => {
    if (node.nodeType === Node.TEXT_NODE) {
      result += node.textContent;
      return;
    }
    
    if (node.nodeType === Node.ELEMENT_NODE) {
      // 检查是否为特定标签
      if (node.classList.contains('el-tag') && node.hasAttribute('data-id')) {
        const contentSpan = node.querySelector('.el-tag__content');
        if (contentSpan) {
          const id = node.getAttribute('data-id');
          const content = contentSpan.textContent;
          result += `{${content}:${id}}`;
          return;
        }
      }
      
      // 处理子节点
      for (const child of node.childNodes) {
        traverseNodes(child);
      }
    }
  };
  
  for (const child of tempDiv.childNodes) {
    traverseNodes(child);
  }
  
  return result;
}

/** 反解析函数：将文本格式转换为HTML字符串 */
const unparseText = (textStr: string) => {
  if (!textStr) return '';
  // 匹配 {content:id} 格式
  const regex = /\{([^:]+):([^}]+)\}/g;
  
  let lastIndex = 0;
  let result = '';
  
  let match;
  while ((match = regex.exec(textStr)) !== null) {
    // 添加前面的文本
    result += textStr.substring(lastIndex, match.index);
    
    // 添加标签
    const content = match[1];
    const id = match[2];
    
    result += `<span data-id="${id}" class="el-tag is-closable el-tag--primary el-tag--light tag-input-wrapper-span" contenteditable="false" style="margin: 0px 3px; display: inline-flex; align-items: center;">
      <span class="el-tag__content">${content}</span>
      <i class="el-icon el-tag__close">
        <svg  data-id="${id}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" style="width: 14px; height: 14px;">
          <path fill="currentColor" d="M764.288 214.592 512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z"></path>
        </svg>
      </i>
    </span>`;
    
    lastIndex = regex.lastIndex;
  }
  
  // 添加剩余的文本
  result += textStr.substring(lastIndex);
  
  return result;
}

/** 从字符串中提取标签对象 */
const extractTagsFromString = (str: string) => {
  // 定义匹配 {名称:ID} 格式的正则表达式
  const regex = /\{([^:]+):([^}]+)\}/g;
  
  // 存储提取结果的数组
  const tags = [];
  
  // 使用正则表达式匹配所有符合条件的标签
  let match;
  while ((match = regex.exec(str)) !== null) {
    // 将每个匹配项转换为对象并添加到结果数组
    tags.push({
      name: match[1],  // 名称
      id: match[2]     // ID
    });
  }
  return tags;
}

/** 获取内容 */
const getHtml = () => {
  return editableArea.value.innerHTML
}

/** 组件挂载时绑定删除标签事件*/
onMounted(() => {
  restoreCaretPosition()
  if(editableArea.value) {
    editableArea.value.addEventListener('click', bindRemoveTagEvent)
  }
})

/** 组件卸载时移除事件监听  */
onUnmounted(() => {
  if(editableArea.value) {
    editableArea.value.removeEventListener('click', bindRemoveTagEvent)
  }
})

/** 定义暴露的属性和方法 */
defineExpose({ saveCaretPosition, insertTags, parseHtml, unparseText, extractTagsFromString, getHtml })

</script>

<style scoped>
.tag-input-wrapper {
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  padding: 5px;
  min-height: 120px;
}

.editable-area {
  min-height: 120px;
  line-height: 30px;
  outline: none;
  white-space: pre-wrap;
  font-size: 14px;
  font-weight: normal
}
</style>
