<script lang="ts" setup>
import ImagesViewer from '../ImagesViewer/index.vue'
import { fetchGetDocumentLinks } from '@/api/answer'

interface Props {
  streamAnswer: string
  isEnd: boolean
}

const props = defineProps<Props>()

const emits = defineEmits(['onTypingEnd', 'onSourceFileParserEnd'])

function generateUniqueID(length: number = 6): string {
  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
  let result = ''
  const charactersLength = characters.length
  for (let i = 0; i < length; i++)
    result += characters.charAt(Math.floor(Math.random() * charactersLength))

  return result
}

const typedHtml = ref('')
const currentIndex = ref(0)
const isTyping = ref(false)
const timer = ref<any>()

const typingSpeed = 50 // Adjust as needed

function formatMarkdown(str: string) {
  return str.replace(/\*\*(.*?)\*\*/g, '<b>$1</b>').replace(/\n/g, '<br />')
}

function extractImageSources(htmlString: string): string[] {
  const imgRegex = /<img[^>]+src="([^">]+)"/gi
  const srcRegex = /src="([^">]+)"/i
  const matches = htmlString.match(imgRegex)

  const sources: string[] = []

  if (matches) {
    matches.forEach((match) => {
      const srcMatch = match.match(srcRegex)
      if (srcMatch && srcMatch[1])
        sources.push(srcMatch[1])
    })
  }

  return sources
}

function replaceImageSources(str: string, originalSrcs: string[], newSrcs: string[]): string {
  if (originalSrcs.length !== newSrcs.length)
    return str

  return originalSrcs.reduce((acc, originalSrc, index) => {
    const newSrc = newSrcs[index]
    const regex = new RegExp(originalSrc, 'g')
    return acc.replace(regex, newSrc)
  }, str)
}

async function formatImgAnswer(str: string) {
  const imagesSrc = extractImageSources(str)
  const res = await fetchGetDocumentLinks(imagesSrc)
  if (res.data) {
    const arr = replaceImageSources(str, imagesSrc, res.data.map((item: any) => item.docUrl))
    return arr
  }

  else { return replaceImageSources(str, imagesSrc, []) }
}

const isImgAnswer = ref(false)
const isMusicChar = ref(false)

const timern = ref<any>()

const isStop = ref(false)

function typeText() {
  if (isStop.value)
    return
  const text = props.streamAnswer
  if (currentIndex.value < text.length) {
    const nextChar = text[currentIndex.value]
    if (nextChar === '<') {
      isImgAnswer.value = true
      // 是图片，不继续打字，等接下来所有的内容拼接完成，并且之前的文字都打印完毕，然后直接展示
      timer.value = setInterval(() => {
        if (props.isEnd && isTyping.value) {
          timern.value = setTimeout(async () => {
            const res = await formatImgAnswer(props.streamAnswer)
            typedHtml.value = res.split('♪')[0] || ''
            bindImgEvent()
            endTyping()
            clearTimeout(timern.value)
          }, 500)
          clearInterval(timer.value)
        }
      }, 500)
    }
    if (nextChar === '♪' || isMusicChar.value) {
      isMusicChar.value = true
      if (isImgAnswer.value && !props.isEnd && isTyping.value) {
        // 不做任何操作
      }
      else {
        endTyping()
      }
      // splitIndex.value = currentIndex.value
      // setTimeout(getSourceFileName, typingSpeed)
    }
    else {
      currentIndex.value++
      if (!isImgAnswer.value)
        typedHtml.value = formatMarkdown(text.substring(0, currentIndex.value))

      setTimeout(typeText, typingSpeed)
    }
  }
  else {
    if (!props.isEnd) {
      setTimeout(typeText, typingSpeed)
    }
    else {
      // 结束
      endTyping()
    }
  }
}

const isShowImagesViewer = ref(false)

const imgsUrlList = ref<string[]>([])

const initialSwipeIdx = ref(0)
const classKey = ref('')
function bindImgEvent() {
  nextTick(() => {
    imgsUrlList.value = []
    const imgDomList = [...document.querySelectorAll(`.event-${classKey.value} img`) as any]
    imgDomList.forEach((img, idx) => {
      imgsUrlList.value.push(img.src)
      img.addEventListener('click', () => {
        isShowImagesViewer.value = true
        initialSwipeIdx.value = idx
      })
    })
  })
}
function onClickStop() {
  if (typedHtml.value === '')
    typedHtml.value = '已停止回答'
  isStop.value = true
  isTyping.value = false
  emits('onTypingEnd', { isStop: true })
  clearInterval(timer.value)
  clearTimeout(timern.value)
  timer.value = null
  timern.value = null
}

function endTyping() {
  clearInterval(timer.value)
  timer.value = null
  isTyping.value = false
  emits('onTypingEnd', { isStop: false })
  clearInterval(timer.value)
  clearTimeout(timern.value)
  timer.value = null
  timern.value = null
}

watch(() => props.streamAnswer, (_, oldVal) => {
  if (oldVal.length === 0 && !isStop.value) {
    typeText()
    isTyping.value = true
  }
}, {
  deep: true,
})

defineExpose({
  onClickStop,
})

onMounted(() => {
  classKey.value = generateUniqueID()
})
</script>

<template>
  <div class="answer-parser-wrap" :class="{ 'finished-typing': !isTyping }">
    <ImagesViewer v-model:show="isShowImagesViewer" :initial-swipe="initialSwipeIdx" :imgs-url-list="imgsUrlList" />
    <div class="answer-parser-content" :class="[`event-${classKey}`]" v-html="typedHtml" />
  </div>
</template>

<style scoped lang="scss">
:deep(.answer-parser-content) {
  img {
    display: block !important;
    width: 160px;
    height: 160px;
    object-fit: cover;
    margin: 12px 0;
  }
}
.answer-parser-wrap {
  display: flex;
  align-items: flex-end;
}
.answer-parser-content::after {
  content: '';
  right: 0;
  bottom: 0;
  width: 2px;
  height: 14px;
  background-color: black;
  animation: cursor-blink-51baf6dc 1s infinite;
  display: inline-block;
  position: relative;
  top: 2px;
  margin-left: 2px;
}
.finished-typing .answer-parser-content::after {
  display: none; /* 打字完成后隐藏光标 */
}
@keyframes cursor-blink {
  0% {
    opacity: 0;
  }
  50% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}
</style>
