<template>
  <div class="mention-container" ref="containerRef">
    <slot></slot>

    <div ref="mirrorRef" class="textarea-mirror" aria-hidden="true"></div>

    <transition name="el-zoom-in-top">
      <div 
        v-if="showPopover" 
        class="mention-popover"
        :style="{ top: popoverPos.top + 'px', left: popoverPos.left + 'px' }"
      >
        <div class="mention-list" v-if="filteredList.length">
          <div
            v-for="(user, index) in filteredList"
            :key="user.id"
            :class="['mention-item', { active: index === selectIndex }]"
            @mousedown.prevent="handleSelect(user)"
            @mouseenter="selectIndex = index"
          >
            <name-avatar :name="user.nickname" :size="20" />
            <span class="name">{{ user.nickname }}</span>
          </div>
        </div>
        <div v-else class="mention-empty">未找到人员</div>
      </div>
    </transition>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, nextTick, watch } from 'vue';

const props = defineProps({
  modelValue: String,
  users: { type: Array, default: () => [] }
});

const emit = defineEmits(['update:modelValue', 'select']);

const containerRef = ref(null);
const mirrorRef = ref(null);
const showPopover = ref(false);
const searchKey = ref("");
const selectIndex = ref(0);
const popoverPos = ref({ top: 0, left: 0 });
let targetInput = null; // 存储原生 textarea 引用

const filteredList = computed(() => {
  return props.users.filter(u => u.nickname.includes(searchKey.value));
});

// 计算像素坐标的核心逻辑
const computeCaretPosition = () => {
  if (!targetInput || !mirrorRef.value) return;

  const val = targetInput.value;
  const cursorIndex = targetInput.selectionStart;
  
  // 找到最近的一个 @
  const textBefore = val.slice(0, cursorIndex);
  const lastAtPos = textBefore.lastIndexOf("@");
  
  // 获取 @ 到光标之间的内容作为搜索词
  searchKey.value = textBefore.slice(lastAtPos + 1);

  // --- 镜像同步逻辑 ---
  const style = window.getComputedStyle(targetInput);
  const mirror = mirrorRef.value;

  // 复制所有会影响排版的样式
  const propList = ['fontFamily', 'fontSize', 'fontWeight', 'lineHeight', 'paddingTop', 'paddingLeft', 'paddingRight', 'paddingBottom', 'borderWidth', 'boxSizing', 'letterSpacing', 'wordBreak'];
  propList.forEach(prop => mirror.style[prop] = style[prop]);
  mirror.style.width = targetInput.offsetWidth + 'px';

  // 将内容填入镜像，在 @ 位置插入标记元素
  const contentBeforeAt = val.slice(0, lastAtPos);
  const contentAtToCursor = val.slice(lastAtPos, cursorIndex);
  
  // 使用 textContent 防止 XSS，使用 <br> 处理换行
  mirror.textContent = contentBeforeAt;
  const marker = document.createElement('span');
  marker.textContent = '@';
  marker.style.color = 'red'; // 仅调试可见
  mirror.appendChild(marker);
  
  // 继续填充剩余内容以保持排版一致
  const remaining = document.createTextNode(contentAtToCursor);
  mirror.appendChild(remaining);

  // 获取标记位的相对坐标
  nextTick(() => {
    popoverPos.value = {
      // offsetTop 是输入框顶部的偏移 + 标记位高度 - 滚动条高度
      top: targetInput.offsetTop + marker.offsetTop + parseInt(style.lineHeight) - targetInput.scrollTop,
      left: targetInput.offsetLeft + marker.offsetLeft
    };
  });
};

const handleInput = (e) => {
  const el = e.target;
  const val = el.value;
  const pos = el.selectionStart; // 当前光标位置
  
  // 1. 获取光标之前的文本
  const textBefore = val.slice(0, pos);
  
  // 2. 找到光标前最近的一个 @
  const lastAtPos = textBefore.lastIndexOf("@");

  if (lastAtPos !== -1) {
    // 3. 关键逻辑：获取 @ 到光标之间的内容
    const contentBetween = textBefore.slice(lastAtPos + 1);
    
    // 4. 判断逻辑：
    // - 如果 @ 和光标之间有空格，说明这一段提及已结束，不弹窗
    // - 如果 @ 前面不是空格且不是开头，说明可能是邮箱地址，不弹窗
    const charBeforeAt = textBefore[lastAtPos - 1];
    const isAtStartOrAfterSpace = !charBeforeAt || /\s/.test(charBeforeAt);
    const hasSpaceBetween = /\s/.test(contentBetween);

    if (isAtStartOrAfterSpace && !hasSpaceBetween) {
      searchKey.value = contentBetween;
      showPopover.value = true;
      computeCaretPosition(); // 重新计算位置
      return;
    }
  }
  
  // 其余情况全部关闭
  showPopover.value = false;
};

const handleKeyDown = (e) => {
  if (!showPopover.value) return;
  if (e.key === 'ArrowUp') {
    e.preventDefault();
    selectIndex.value = (selectIndex.value - 1 + filteredList.value.length) % filteredList.value.length;
  } else if (e.key === 'ArrowDown') {
    e.preventDefault();
    selectIndex.value = (selectIndex.value + 1) % filteredList.value.length;
  } else if (e.key === 'Enter') {
    e.preventDefault();
    if (filteredList.value[selectIndex.value]) handleSelect(filteredList.value[selectIndex.value]);
  } else if (e.key === 'Escape') {
    showPopover.value = false;
  }
};

const handleSelect = (user) => {
  const val = props.modelValue;
  const pos = targetInput.selectionStart;
  const textBefore = val.slice(0, pos);
  const lastAtPos = textBefore.lastIndexOf("@");
  
  const newValue = val.slice(0, lastAtPos) + `@${user.nickname} ` + val.slice(pos);
  emit('update:modelValue', newValue);
  emit('select', user);
  showPopover.value = false;
  nextTick(() => targetInput.focus());
};

onMounted(() => {
  targetInput = containerRef.value.querySelector('textarea');
  if (targetInput) {
    targetInput.addEventListener('input', handleInput);
    targetInput.addEventListener('keydown', handleKeyDown);
    targetInput.addEventListener('scroll', () => {
      if (showPopover.value) computeCaretPosition();
    });
  }
});
</script>

<style scoped lang="scss">
.mention-container {
  position: relative;
  width: 100%;
}

/* 镜像层：必须绝对定位且隐藏 */
.textarea-mirror {
  position: absolute;
  top: 0;
  left: 0;
  z-index: -999;
  visibility: hidden;
  white-space: pre-wrap;
  word-wrap: break-word;
  overflow: hidden;
}

.mention-popover {
  position: absolute;
  z-index: 3000;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
  border: 1px solid #e4e7ed;
  min-width: 160px;
}

.mention-list {
  max-height: 200px;
  overflow-y: auto;
  padding: 4px 0;

  .mention-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    cursor: pointer;
    transition: all 0.2s;
    
    &.active {
      background-color: #f0f7ff;
      color: #409eff;
    }

    .name {
      font-size: 14px;
      font-weight: 500;
    }
  }
}

.mention-empty {
  padding: 12px;
  text-align: center;
  color: #909399;
  font-size: 13px;
}
</style>