<template>
  <div class="chat-content-ai">
    <div ref="chatArea" class="chat-message">
      <div v-if="showMore" class="more" @click="handleMoreHistory"> 查看历史记录 </div>
      <div class="chat-message-scroll" :class="{ 'is-full': isFull }">
        <!-- sender：发送人，replyer：回复人 -->
        <div
          v-for="(message, index) in messageList"
          :key="message.id || index"
          class="chat-message-box"
          :class="[message.author === 'sender' ? 'message-sender' : 'message-replyer']"
        >
          <div v-if="message.showTime" class="other time">
            <span>{{ dateFormatter(message.showTime, 'yyyy年M月d日 HH:mm:ss', true) }}</span>
          </div>
          <!-- 双方头像 -->
          <div class="chat-message-avatar">
            <img :src="message.author === 'sender' ? ManAvatar : AiAvatar" alt="" />
          </div>
          <div class="chat-message-content">
            <!-- 消息主内容 -->
            <div class="chat-message-content__message">
              <!-- 消息部分 -->
              <template v-if="message.body.component">
                <component :is="message.body.component" v-bind="message" />
              </template>
              <div v-else class="message-text" v-html="message.body.text"></div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <!-- 聊天框下方操作部分 -->
    <ChatBottom @handleSend="handleOutboundMessage" />
  </div>
</template>

<script lang="ts" setup>
import { dateFormatter } from '../utils'
import ManAvatar from '../img/chat/man-avatar.png'
import AiAvatar from '../img/chat/ai-avatar-base.png'
import ChatBottom from './ChatBottom.vue'
import Thinking from './Thinking.vue'
import { marked } from 'marked'

import { ref, watch, onUnmounted, nextTick } from 'vue'
import {Recordable} from "vite-plugin-mock";

const props = defineProps({
  isOpen: {
    type: Boolean,
    default: false
  }
})
const emit = defineEmits(['handleSend'])

const aiName = 'AI机器人'

const isInit = ref(false) // 判断是否已经初始化 否则不监听滚动
watch(
  () => props.isOpen,
  (val) => {
    // 打开弹窗时 有历史记录 则需要自动滚动到最下面
    if (val) {
      init()
      setTimeout(() => {
        isInit.value = false
      }, 500)
    } else reset()
  }
)
// 销毁（按需）
onUnmounted(() => {
  document.querySelector('.chat-message')?.removeEventListener('scroll', () => {})
})

const isFull = ref(false) // 消息是否充满一页 用于首次加载滚动
const showMore = ref(false) // 点击可以查看更多

const messageList = ref<Recordable[]>([]) // 消息列表
const historyList = ref<Recordable[]>([]) // 历史消息列表

// 整理、移除历史记录 仅记录7天内
const trimHistory = () => {
  const historyChat =
    localStorage.getItem('chat') && JSON.parse(localStorage.getItem('chat') as string)
  if (historyChat && historyChat.length) {
    const findIndex = historyChat.findIndex((i) => {
      const time = i.date
      const now = new Date().getTime()
      const day = 24 * 60 * 60 * 1000 * 7
      return now - time > day
    })
    if (findIndex !== -1) {
      const list = historyChat.slice(0, findIndex)
      localStorage.setItem('chat', JSON.stringify(list))
      return list
    } else return historyChat
  } else return []
}

const init = () => {
  historyList.value = trimHistory()

  // 有历史记录，默认展示historyLimit条
  if (historyList.value.length) {
    showMore.value = true
    isFull.value = true
  }
  // 最新的一条记录(欢迎语）， 所以记录这个时间
  const newRecordTime = Date.now()
  // 新增欢迎语 每次初始化都来一次（但不计入历史记录中）
  const record = {
    body: {
      text: 'Hi~我是AI机器人，我可以帮你什么？'
    },
    date: newRecordTime,
    showTime: newRecordTime,
    author: 'replyer',
    ignore: true
  }
  messageList.value.push(record)
  setTimeout(() => {
    messageScroll()
  })

  // 监听滚动事件 到顶部自动加载历史记录
  document.querySelector('.chat-message')?.addEventListener('scroll', (e) => {
    handleScroll(e, newRecordTime)
  })
}

// 消息内容发出
const handleOutboundMessage = (message) => {
  if (!message) {
    return
  }
  let body = {}
  if (typeof message === 'string') {
    body = {
      text: message
    }
  } else if (Object.prototype.toString.call(message) === '[object Object]') {
    body = message
  }
  handleMessageReceived({
    body,
    date: Date.now(),
    author: 'sender'
  })
}

const chatArea = ref()
// 实时滚动
const messageScroll = () => {
  chatArea.value.scrollTop = chatArea.value.scrollHeight
}
// 模拟思考
const setThinking = (bol: boolean = true) => {
  if (bol) {
    messageList.value.push({
      body: { component: Thinking },
      author: 'replyer'
    })
  } else messageList.value.pop()
}
const sessionId = ref('')

// 发送消息
const handleMessageReceived = async (message) => {
  // 展示用户问题
  await addMessage(message)
  setTimeout(() => {
    messageScroll()
  })

  // 及时来一个loading 模拟思考
  setThinking()

  emit('handleSend', message)

  // 移除loading
  // setThinking(false)

  // handleMessageResponse({ text })

  // else { // demo部分
  //   // 无答案话术
  //   this.handleMessageResponse({
  //     text: '很抱歉，您问的问题暂未找到答案，请尝试其他问题'
  //   })
  // }

  // setTimeout(() => {
  //   messageScroll()
  // })
}
// 回答
const handleMessageResponse = async (message) => {
  console.log(message)

  if (Object.prototype.toString.call(message) !== '[object Object]') {
    return new Error('得是object格式')
  }

  // 移除loading
  setThinking(false)

  const record = {
    body: { ...message },
    date: new Date().getTime(),
    author: 'replyer'
  }
  await addMessage(record)

  setTimeout(() => {
    messageScroll()
  })
}

// 开启新对话
const newChat = () => {
  sessionId.value = ''
  messageList.value.push({
    body: { text: '已开启新对话～' },
    author: 'replyer',
    ignore: true
  })
}

const timer: any = ref(null) // 文字处理定时器
const preRecord: any = ref(null) // 上一次的记录

// 记录消息
const addMessage = (record) => {
  return new Promise((resolve) => {
    // 第一条消息 自动加上时间
    // 两分钟以内连续发的消息 不显示时间 由 showTime 来判断显示与否
    const curList = messageList.value.filter((item) => !item.ignore)
    if (!curList.length || record.date - curList.slice(-1)[0].date > 2 * 60 * 1000) {
      record.showTime = record.date
    }

    // 消息来自用户 或者 回复内容包含组件 直接给出 message 内容
    // 否则纯文本类型的走 GPT动效
    if (record.author === 'sender' || record.body.component) {
      // 继续提问时 有正在进行的输出 提前结束回答
      if (preRecord.value) {
        clearInterval(timer.value)
        timer.value = null
        saveHistory(preRecord.value)
      }

      messageList.value.push(record)
      saveHistory(record)
      resolve(true)
      return
    }

    preRecord.value = { ...record }
    // **** ai的回答 纯文字的要做 GPT 文字效果 ****
    // 用tempText 先存一下字段
    const body = preRecord.value.body
    const { text, messageProps } = body
    if (record.author === 'replyer') {
      if (text) {
        body.tempText = text
        body.text = ''
      }
      if (messageProps?.text) {
        messageProps.tempText = messageProps.text
        messageProps.text = ''
      }
    }

    // 先  消息列表增加
    messageList.value.push(preRecord.value)

    // 再  处理消息效果
    if (preRecord.value.author === 'replyer' && (body.tempText || messageProps?.tempText)) {
      // 记录本次的记录 用于防止在文字演进效果时 没存入记录时  被上一条记录替换掉
      const tempText = body.tempText || messageProps.tempText
      const len = tempText.length
      let num = 0
      let string = ''
      timer.value = setInterval(() => {
        if (num >= len) {
          clearInterval(timer.value)
          timer.value = null
          saveHistory(preRecord.value)
          resolve(true)
          return
        }
        // 持续保持滚动最底
        if (num % 10 === 0) {
          setTimeout(() => {
            messageScroll()
          })
        }
        string = `${string}${tempText[num]}`
        if (body.tempText) {
          body.text = marked(string)
        } else {
          body.messageProps.text = marked(string)
        }
        num++
      }, 20)
    }
  })
}
// 将对话存入缓存
const saveHistory = (record) => {
  if (!record) return
  // GTP对话 将对话内容还原
  const body = record.body
  const { messageProps } = body
  if (body.tempText) {
    delete body.tempText
  } else if (messageProps?.tempText) {
    delete messageProps.tempText
  }

  setTimeout(() => {
    messageScroll()
  })
  // 增加历史记录
  const historyChat = localStorage.getItem('chat')
  if (historyChat) {
    const list = JSON.parse(historyChat)
    list.push(record)
    localStorage.setItem('chat', JSON.stringify(list))
  } else {
    localStorage.setItem('chat', JSON.stringify([record]))
  }
  // 清除记录
  if (preRecord.value) preRecord.value = null
}

const historyPage = ref(1) // 分页
const historyLimit = ref(10) // 每页数量
// 查看历史记录
const handleMoreHistory = () => {
  const lastPageList = historyList.value.splice(
    -(historyLimit.value * historyPage.value),
    historyLimit.value
  )

  messageList.value = [...lastPageList, ...messageList.value]
  historyPage.value++
  // 看完了
  if (!historyList.value.length) {
    showMore.value = false
  }
  nextTick(() => {
    // 获取查看历史记录后的信息列表
    const messageList = document.querySelectorAll('.chat-message-box') as NodeListOf<HTMLElement>
    // 获取这一批次历史记录最后一个距离文档顶部的高度
    const lastOffsetTop = messageList[lastPageList.length - 1]?.offsetTop || 0
    // 获取这一批次历史记录最后一个的高度
    const lastH = messageList[lastPageList.length - 1]?.clientHeight || 0
    // 滚动位置
    chatArea.value.scrollTop = lastOffsetTop + lastH
  })
}
// 滚动事件 自动加载
const handleScroll = (e, time) => {
  if (isInit.value) return
  if (Date.now() - time > 1000 && isFull.value) {
    isFull.value = false
  }
  const step = e.target.scrollTop
  if (step <= 30 && showMore.value) {
    handleMoreHistory()
  }
}

const reset = () => {
  isInit.value = true
  messageList.value = []
  historyPage.value = 1
  historyLimit.value = 10
  preRecord.value = null
}

defineExpose({ newChat, handleMessageResponse })
</script>

<style lang="less">
@import '../styles/variables.less';
.chat-content-ai {
  display: flex;
  flex-direction: column;
  .is-expend & {
    background: transparent;
  }
  .more {
    text-align: center;
    color: @color-primary;
    padding: 8px 0;
    cursor: pointer;
  }
  .chat-message {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 0 12px;
    margin-top: 10px;
    &-scroll {
      &.is-full {
        min-height: 100%;
      }
    }
    &-box {
      display: flex;
      position: relative;
      &:has(.other) {
        padding-top: 30px;
      }
      .other {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        font-size: 12px;
        text-align: center;
        &.time {
          color: #999;
        }
      }
      .divider {
        border-bottom: 1px solid #ddd;
        span {
          display: inline-block;
          position: relative;
          padding: 0 8px;
          top: 8px;
          background-color: #f4f9fd;
        }
      }
      .chat-message-avatar {
        img {
          width: 40px;
        }
      }
      .chat-message-content {
        width: 100%;
        margin-left: 4px;
        &__message {
          display: inline-block;
          max-width: 85%;
          width: auto;
          min-width: 20px;
          border-radius: 8px;
          font-weight: 400;
          line-height: 22px;
          padding: 12px;
          margin-bottom: 20px;
          position: relative;
          word-break: break-all;
          .message-text {
            > *:first-child {
              margin-top: 0;
            }
            > *:last-child {
              margin-bottom: 0;
            }
          }
        }
      }
    }

    .message-sender {
      justify-content: flex-end;

      .chat-message-avatar {
        order: 2;
      }

      .chat-message-content {
        order: 1;
        text-align: right;
        margin-right: 4px;
      }

      .chat-message-content__message {
        background: @color-primary;
        color: #ffffff;
        text-align: left;
        // 发送文件类型时 白底
        &:has(.white-bg) {
          background-color: #fff;
          color: #333;
        }
      }
    }

    .message-replyer {
      color: #333;
      text-align: left;

      .chat-message-content__message {
        background: #fff;
        color: #333;
        width: 96%;
        max-width: 96%;
      }
    }
  }
}
</style>
