<script setup lang="ts">
// 展示一个textarea 里面可展示图片等(暂时只加入了 图片 后续若有其他的 再添加)
// 暂时用到了快捷发布问吧 和 发布实践  以及 评论相关的内容
import MentionBox from '../MentionBox/index.vue'
import { MENTION_USER_FN_KEY } from '@/constants/symbolKey'
// console.log(VueTribute, 'VueTribute')
interface RichTextareaProps {
  placeholder?: string
  maxlength?: number
  imgList: string[]
  uploadPercent: number
  height?: number
  showMention?: boolean
}
interface Emits {
  deleteImg: [img: string]
}

const {
  placeholder = '请输入内容',
  maxlength,
  imgList,
  uploadPercent,
  height = 55,
  showMention = true,
} = defineProps<RichTextareaProps>()
const emit = defineEmits<Emits>()
const inputStr = defineModel<string>({ required: true })
const textareaRef = useTemplateRef<HTMLTextAreaElement>('textareaRef')
const highlightRef = useTemplateRef<HTMLElement>('highlightRef')
const isComposing = ref(false)
const mentionFns = inject<{
  getMentionUsers?: () => Array<{ userId: string; name: string }>
}>(MENTION_USER_FN_KEY, {})

const escapeHtml = (str: string) =>
  str
    .replaceAll('&', '&amp;')
    .replaceAll('<', '&lt;')
    .replaceAll('>', '&gt;')
    .replaceAll('"', '&quot;')
    .replaceAll("'", '&#39;')

const highlightedHtml = computed(() => {
  const source = inputStr.value || ''
  const mentions = (mentionFns?.getMentionUsers?.() ?? [])
    .filter((u) => u?.name)
    .sort((a, b) => b.name.length - a.name.length)
    .map((u) => `@${u.name}`)

  const isBoundary = (ch: string | undefined) => !ch || /\s/.test(ch)

  let html = ''
  let i = 0
  while (i < source.length) {
    const prev = i > 0 ? source[i - 1] : undefined
    const token = mentions.find((item) => {
      if (!source.startsWith(item, i)) return false
      const next = source[i + item.length]
      return isBoundary(prev) && isBoundary(next)
    })

    if (token) {
      html += `<span class="mention-token">${escapeHtml(token)}</span>`
      i += token.length
      continue
    }

    html += escapeHtml(source.charAt(i))
    i++
  }

  return html
})

const syncHighlightScroll = (e: Event) => {
  const textarea = e.target as HTMLTextAreaElement
  if (!highlightRef.value) return
  highlightRef.value.scrollTop = textarea.scrollTop
  highlightRef.value.scrollLeft = textarea.scrollLeft
}

const handleCompositionStart = () => {
  isComposing.value = true
}

const handleCompositionEnd = () => {
  isComposing.value = false
}

defineExpose({
  getTextarea: () => textareaRef.value,
})
</script>

<template>
  <div
    style="border: 1px solid rgb(229, 231, 235)"
    class="relative w-full rounded-lg border border-gray-200 px-3 py-2 transition focus-within:border-[var(--el-color-primary)] focus-within:ring-1 focus-within:ring-[var(--el-color-primary)] bg-white"
  >
    <div class="relative">
      <template v-if="showMention">
        <div
          ref="highlightRef"
          aria-hidden="true"
          class="gutter-stable pointer-events-none absolute inset-0 overflow-hidden whitespace-pre-wrap break-words text-sm leading-5"
          :style="{ height: height + 'px' }"
          :class="{ invisible: isComposing }"
        >
          <div class="w-full text-gray-800" v-html="highlightedHtml || '&nbsp;'"></div>
        </div>
        <MentionBox>
          <textarea
            ref="textareaRef"
            v-model="inputStr"
            :placeholder="placeholder"
            class="gutter-stable w-full resize-none border-none bg-transparent p-0 outline-none text-sm leading-5 whitespace-pre-wrap break-words placeholder:text-gray-400 caret-gray-800"
            :class="isComposing ? 'text-gray-800' : 'text-transparent'"
            :style="{ height: height + 'px' }"
            :maxlength="maxlength"
            @scroll="syncHighlightScroll"
            @compositionstart="handleCompositionStart"
            @compositionend="handleCompositionEnd"
          />
        </MentionBox>
      </template>
      <template v-else>
        <!-- 无镜像高亮层时不能 text-transparent，否则正文不可见 -->
        <textarea
          ref="textareaRef"
          v-model="inputStr"
          :placeholder="placeholder"
          class="w-full resize-none border-none bg-transparent outline-none text-sm leading-5 text-gray-800 placeholder:text-gray-400 caret-gray-800"
          :style="{ height: height + 'px' }"
          :maxlength="maxlength"
        />
      </template>
    </div>
    <!-- 定位到右边 -->
    <span v-if="maxlength" class="flex justify-end text-xs text-gray-400">
      {{ inputStr?.length }} / {{ maxlength }}
    </span>

    <div class="flex justify-between items-center mt-2">
      <!-- 图片列表展示 -->
      <div
        v-if="imgList.length"
        class="flex flex-wrap gap-2"
        v-loading="uploadPercent > 0"
        :element-loading-text="uploadPercent + '%'"
      >
        <div
          class="relative w-20 h-20 rounded-lg overflow-hidden group"
          v-for="img in imgList"
          :key="img"
        >
          <!-- 删除按钮 -->
          <div
            class="absolute top-1 right-1 z-10 w-5 h-5 flex items-center justify-center bg-black/60 rounded-full cursor-pointer opacity-0 group-hover:opacity-100 transition-all duration-200 hover:bg-black/80 hover:scale-110"
            @click="emit('deleteImg', img)"
          >
            <el-icon class="text-white text-xs">
              <IEpClose />
            </el-icon>
          </div>

          <el-image
            :src="img"
            class="w-full h-full rounded-lg border border-gray-200"
            fit="cover"
          />
        </div>
      </div>
    </div>
    <!-- <MentionList ref="mentionListRef" :mouse-position="mousePosition" @mention="handleMention" /> -->
  </div>
</template>

<style scoped lang="scss">
:deep(.mention-token) {
  color: #2563eb;
  background: rgba(37, 99, 235, 0.1);
  border-radius: 2px;
}

/**
 * textarea 文字是透明的，真正可见的文字由下层镜像高亮层渲染，两层必须逐字对齐。
 * 默认情况下 textarea 出现滚动条会占用其可用宽度，使它比无滚动条的高亮层更早换行，
 * 两层错位后底部文字落到透明的 textarea 上，看起来就“变透明/看不见”。
 * 给两层都加 scrollbar-gutter: stable，无论是否出现滚动条都预留同样的滚动条宽度，
 * 保证两层文字可用宽度始终一致、换行同步；同时滚动条照常显示。
 */
.gutter-stable {
  scrollbar-gutter: stable;
}
</style>
