<script setup lang='ts'>
import { computed, onUpdated, reactive, ref, watch } from 'vue'
import { NDropdown, useMessage } from 'naive-ui'
import axios from 'axios'
import AvatarComponent from './Avatar.vue'
import TextComponent from './Text.vue'
import { SvgIcon } from '@/components/common'
import { useIconRender } from '@/hooks/useIconRender'
import { t } from '@/locales'
import { useBasicLayout } from '@/hooks/useBasicLayout'
import { copyToClip } from '@/utils/copy'

interface Props {
  dateTime?: string
  text?: string
  tts?: string
  autoplay?: boolean
  inversion?: boolean
  error?: boolean
  loading?: boolean
}

interface Emit {
  (ev: 'regenerate'): void
  (ev: 'delete'): void
  (ev: 'changeNowStatus'): string
}

const props = defineProps<Props>()

const emit = defineEmits<Emit>()

const { isMobile } = useBasicLayout()

const { iconRender } = useIconRender()

const message = useMessage()

const textRef = ref<HTMLElement>()

const asRawText = ref(props.inversion)

const messageRef = ref<HTMLElement>()

const options = computed(() => {
  const common = [
    {
      label: t('chat.copy'),
      key: 'copyText',
      icon: iconRender({ icon: 'ri:file-copy-2-line' }),
    },
    {
      label: t('common.delete'),
      key: 'delete',
      icon: iconRender({ icon: 'ri:delete-bin-line' }),
    },
  ]

  if (!props.inversion) {
    common.unshift({
      label: asRawText.value ? t('chat.preview') : t('chat.showRawText'),
      key: 'toggleRenderType',
      icon: iconRender({ icon: asRawText.value ? 'ic:outline-code-off' : 'ic:outline-code' }),
    })
  }

  return common
})

function handleSelect(key: 'copyText' | 'delete' | 'toggleRenderType') {
  switch (key) {
    case 'copyText':
      handleCopy()
      return
    case 'toggleRenderType':
      asRawText.value = !asRawText.value
      return
    case 'delete':
      emit('delete')
  }
}

function handleRegenerate() {
  messageRef.value?.scrollIntoView()
  emit('regenerate')
}

async function handleCopy() {
  try {
    await copyToClip(props.text || '')
    message.success('复制成功')
  }
  catch {
    message.error('复制失败')
  }
}

const http = (text: any) => {
  return new Promise((resolve, reject) => {
    axios.post('https://chat.lihaink.cn/index/tts', { data: text })
      .then((response) => {
        // 请求成功处理逻辑
        // console.log('请求成功',response.data);
        resolve(response.data)
      })
      .catch((error) => {
        // 请求失败处理逻辑
        console.error('请求失败', error)
        reject(error)
      })
  })
}

// 创建音频对象的数组
const audioElements = reactive([])

let ttslength = 0
let ttsLoadFlag = 0
let lengthFlag = 0
let tts = props.tts ? JSON.parse(props.tts) : []
let ttsplay = true
async function loadTTS() {
  lengthFlag = tts.length
  if (tts.length > ttslength && audioElements.length == ttslength && ttsLoadFlag == 0) {
    for (let i = ttslength; i < lengthFlag; i++) {
      console.log(`加载了${i + 1}次`, tts[i])
      ttsLoadFlag = 1
      if (tts[i] == '')
        break
      try {
        const res = await http(tts[i])
        const a = new Audio(res.data.mp3)
        a.addEventListener('ended', () => {
          onAudioEnd()
        })
        audioElements.push(a)
        if (ttsplay) {
          ttsplay = false
          playAudio()
        }
        if (i == tts.length - 1) {
          ttslength = tts.length
          ttsLoadFlag = 0
        }
      }
      catch (error) {
        console.log(error)
      }
    }
  }
}

// 监听到文本数据改变
const watchTTS = watch(() => props.tts, async (n: any, o: any) => {
  if (props.tts == '') {
    for (let i = 0; i < audioElements.length; i++) {
      audioElements[i].pause()
      if (i + 1 == audioElements.length)
        audioElements.length = 0
    }
  }
  tts = props.tts ? JSON.parse(props.tts) : []
  loadTTS()
}, { immediate: true, deep: true })
// 停止播放音频
const stopPlay = () => {
  console.log('停止播放音频')
  watchTTS()
  audioElements.forEach((item: any) => {
    item.pause()
    item.currentTime = 0
  })
  audioElements.length = 0
}

onUpdated(() => {
  if (!props.autoplay) {
    stopPlay()
    watchTTS()// 取消监听
  }
})

console.log('组件渲染 ', props.text, props.tts)

const playStatus = ref('end')
let playing = 0
let sok = false
async function radioPlay() {
  // return console.log('播放', props.tts[0])
  audioElements.length = 0
  emit('changeNowStatus', 'loding')
  const socket = new WebSocket('wss://chat.lihaink.cn/zhanti/tts')
  sok = true

  const promise = () => {
    return new Promise((resolve, reject) => {
      // 监听WebSocket连接打开事件
      socket.onopen = () => {
        console.log('socket已连接')
        playing = 0
        resolve(null)
      }
    })
  }

  await promise()

  // 监听WebSocket关闭事件
  socket.onclose = (event: any) => {
    sok = false
    console.log('连接已关闭: ', event, sok)
    console.log('sok状态', sok)
  }

  // socket.send(JSON.stringify({
  //   data: '快。科。技0月12。日消息。不宣而发的。华为Mate 60在上架官方商城后。直到现在都处于一机难求的状态。由于Mate 60系列的爆火。华为也是将明年的预计手机出货量翻倍到了7000万台。',
  // }))
  socket.send(JSON.stringify({
    data: props.text,
  }))
  playStatus.value = 'playing'
  let i = 0
	 // 监听WebSocket接收消息事件
	 socket.onmessage = (event: any) => {
    const msg = JSON.parse(event.data)
    console.log(i, msg.mp3)
    if (msg.key == 'false')
      return socket.close()

    const a = new Audio(msg.mp3)
			  a.addEventListener('ended', () => {
			    onAudioEnd()
			  })
			  audioElements.push(a)
    if (i == 0)
      playAudio(i)
    i++
  }

  // for (let i = 0; i < props.mp3.length; i++) {
  //   const a = new Audio(props.mp3[i])
  //   a.addEventListener('ended', () => {
  //     onAudioEnd(i)
  //   })
  //   audioElements.push(a)
  // }

  // playAudio()
}

let playFlag = false
const onAudioEnd = (index: any) => {
  console.log('播放完 ', playing)

  playing++
  playFlag = false

  if (playing < audioElements.length) {
    playFlag = true
    audioElements[playing].play()
    emit('changeNowStatus', 'playing')
  }
  else {
    playStatus.value = 'end'
    emit('changeNowStatus', 'input')
  }
}
// 播放音频
const playAudio = (playIndex: any) => {
  // for (let i = 0; i < audioElements.length; i++)
  try {
    audioElements[0]!.play()
    emit('changeNowStatus', 'playing')
  }
  catch (error) {
    alert('请检查浏览器是否支持音频播放')
  }
}

watch(() => audioElements.length, (newVal, oldVal) => {
  if (playStatus.value == 'pause')
    return
  if (newVal > playing && playFlag == false && newVal > 0) {
    audioElements[playing]!.play()
    playStatus.value = 'playing'
  }
})

let pauseIndex = -1
// 暂停音频
const pauseAudio = () => {
  pauseIndex = playing + 0
  playStatus.value = 'pause'
  emit('changeNowStatus', 'input')
  for (let i = 0; i < audioElements.length; i++)
    audioElements[i].pause()
}
const noPauseAudio = () => {
  playStatus.value = 'playing'
  emit('changeNowStatus', 'playing')
  audioElements[pauseIndex]?.play()
  pauseIndex = -1
}

defineExpose({
  stopPlay,
  pauseAudio,
  noPauseAudio,
  radioPlay,
})
</script>

<template>
  <div
    ref="messageRef"
    class="flex w-full mb-6 overflow-hidden"
    :class="[{ 'flex-row-reverse': inversion }]"
  >
    <div
      class="flex items-center justify-center flex-shrink-0 h-8 overflow-hidden rounded-full basis-8"
      :class="[inversion ? 'ml-2' : 'mr-2']"
    >
      <AvatarComponent :image="inversion" />
    </div>
    <div class="overflow-hidden text-sm " :class="[inversion ? 'items-end' : 'items-start']">
      <p class="text-xs text-[#b4bbc4]" :class="[inversion ? 'text-right' : 'text-left']">
        {{ dateTime }}
      </p>
      <div
        class=" items-end gap-1 mt-2"
        :class="[inversion ? 'flex-row-reverse' : 'flex-row']"
      >
        <TextComponent
          ref="textRef"
          :inversion="inversion"
          :error="error"
          :text="text"
          :loading="loading"
          :as-raw-text="asRawText"
        />
        <div class="flex-col btn">
          <button
            v-if="!inversion && sok == false && playStatus == 'end'"
            class="mr-2 transition text-neutral-300 hover:text-neutral-800 dark:hover:text-neutral-300"
            @click="radioPlay"
          >
            播放
            <!-- <SvgIcon icon="ri:restart-line" /> -->
          </button>
          <button

            v-if="!inversion && playStatus == 'playing'"
            class="mr-2 transition text-neutral-300 hover:text-neutral-800 dark:hover:text-neutral-300"
            @click="pauseAudio"
          >
            暂停
            <!-- <SvgIcon icon="ri:restart-line" /> -->
          </button>
          <button
            v-if="!inversion && playStatus == 'pause'"
            class="mr-2 transition text-neutral-300 hover:text-neutral-800 dark:hover:text-neutral-300"
            @click="noPauseAudio"
          >
            继续
            <!-- <SvgIcon icon="ri:restart-line" /> -->
          </button>
          <!-- <button
            v-if="!inversion"
            class="mr-2 transition text-neutral-300 hover:text-neutral-800 dark:hover:text-neutral-300"
            @click="handleRegenerate"
          >
            <SvgIcon icon="ri:restart-line" />
          </button> -->
          <NDropdown
            v-if="!inversion"
            :trigger="isMobile ? 'click' : 'hover'"
            :placement="!inversion ? 'right' : 'left'"
            :options="options"
            @select="handleSelect"
          >
            <button class="transition text-neutral-300 hover:text-neutral-800 dark:hover:text-neutral-200">
              <SvgIcon icon="ri:more-2-fill" />
            </button>
          </NDropdown>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.btn{
	padding: 10px;
}
</style>
