<!-- 右侧聊天界面 -->
<template>
  <div class="box" ref="box">
    <div class="contentBox" ref="contentBox" v-if="myHistory.length > 0">
      <div v-for="item in myHistory" :key="item.id">
        <div class="userBox">
          <div class="content">
            <contentView :content="item.content" />
          </div>
          <div class="icon">
            {{ item.role }}
          </div>
        </div>
        <div v-if="item.answer" class="gptBox">
          <div class="icon">GPT</div>
          <div class="content">
            <contentView :content="item.answer" :ref="'contentView' + item.id" />
          </div>
        </div>
      </div>
    </div>
    <div class="initBox" ref="initBox" v-else>
      <div class="text">
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;你好，<br />
        我是人工智能语言模型<br />
        我可以回答你的问题，为你提供有用信息，帮助你完成创作。<br />
      </div>
    </div>
    <div class="inputBox">
      <el-input placeholder="请输入内容" v-model="inputContent" class="input-with-select" @keyup.enter.native="submit">
        <el-button slot="append" icon="el-icon-position" @click="submit"></el-button>
      </el-input>
      <el-button icon="el-icon-delete" style="margin-left: 10px" @click="clear" type="danger" plain></el-button>
    </div>
    <div id="network"></div>
  </div>
</template>

<script>
import { GetStreaming } from '@/utils/getStreaming.js'
// import codePreview from '../codePreview';
import contentView from '../contentView'

export default {
  data() {
    return {
      templateController: undefined,
      isPromptTemplate: true,
      inputContent: null,
      myHistory: [],
      heistoryRotate: null, // 历史轮数
      param: {
        query: null,
        history: [],
        prompt_template: '',
        stream: true,
        model_name: '',
        temperature: 0.7
      }
    }
  },
  props: ['chatForm'],
  components: { contentView },

  computed: {},

  mounted() {
    this.$bus.$on('isPromptTemplate', (data) => {
      this.isPromptTemplate = data
    })
  },
  beforeDestroy() {
    // 移除监听事件 "share"
    this.$bus.$off('isPromptTemplate')
  },

  methods: {
    clear() {
      this.param.query = null
      this.param.history = []
      this.inputContent = null
      this.myHistory = []
    },
    submit() {
      this.param.model_name = this.chatForm.model_name
      this.param.temperature = this.chatForm.temperature
      this.heistoryRotate = this.chatForm.heistoryRotate
      this.param.prompt_template = this.isPromptTemplate ? this.chatForm.prompt_template : undefined
      let apiUrl
      if (this.chatForm.pattern === 0) {
        apiUrl = '/2api/chat/chat'
      } else if (this.chatForm.pattern === 1) {
        apiUrl = '/2api/chat/knowledge_base_chat'
        this.param = { ...this.param, ...this.chatForm.knowledgeConfige }
      } else if (this.chatForm.pattern === 2) {
        apiUrl = '/2api/chat/search_engine_chat'
        this.param = { ...this.param, ...this.chatForm.searchConfige }
      } else if (this.chatForm.pattern === 3) {
        apiUrl = '/2api/chat/file_chat'
        this.param = { ...this.param, ...this.chatForm.fileConfige }
        if (!this.chatForm.fileConfige.knowledge_id) {
          this.$message.error('请先上传文件')
          return
        }
      } else if (this.chatForm.pattern === 4) {
        apiUrl = '/2api/chat/mapping_knowledge'
        this.param = { ...this.param, ...this.chatForm.knowledgeGraphConfige }
        if (!this.chatForm.knowledgeGraphConfige.knowledge_graph_name) {
          this.$message.error('请先请选择知识图谱')
          return
        }
      }
      if (!this.inputContent) return
      this.param.query = this.inputContent
      this.myHistory.push({
        role: 'user',
        content: this.inputContent,
        answer: '',
        excludeReferenceAnswer: '',
        id: this.generateRandomString(8)
      })

      this.inputContent = null
      this.param.history = []
      this.myHistory
        .slice(0, -1)
        .slice(-this.heistoryRotate)
        .forEach((item) => {
          this.param.history.push(
            ...[
              { role: item.role, content: item.content },
              { role: 'assistant', content: item.excludeReferenceAnswer }
            ]
          )
        })
      this.$emit('submit')

      let getData = new GetStreaming(apiUrl, this.param, this.onmessage, this.setSuccess)
      getData.initeventSource()
    },
    setSuccess() {
      this.$nextTick(() => {
        let nowChat = this.myHistory[this.myHistory.length - 1]
        this.$refs['contentView' + nowChat.id][0].isSuccess()
      })
    },

    onmessage(data) {
      let nowChat = this.myHistory[this.myHistory.length - 1]
      if (this.chatForm.pattern === 0) {
        let temporary = JSON.parse(`[${data}]`.replace(/}{/g, '},{'))
        // 通用智能问答
        // data = data.replace(/^data:\s+|\s+$/g, '')
        // nowChat.excludeReferenceAnswer += data.answer
        // nowChat.answer += data.answer
        temporary.forEach((item) => {
          this.modifyContent(item) // 修改返回内容
          nowChat.excludeReferenceAnswer += item.answer || '' // 排除回答中的引用内容
          nowChat.answer += item.answer || '\n' + (item.docs || '')
        })
      } else if (this.chatForm.pattern === 1) {
        // 专业知识库问答
        // console.log(data)
        let temporary = JSON.parse(`[${data}]`.replace(/}{/g, '},{'))
        temporary.forEach((item) => {
          this.modifyContent(item) // 修改返回内容
          nowChat.excludeReferenceAnswer += item.answer || '' // 排除回答中的引用内容
          nowChat.answer += item.answer || '\n' + (item.docs || '')
        })
      } else if (this.chatForm.pattern === 2) {
        // 搜索引擎问答
      } else if (this.chatForm.pattern === 3) {
        // 基于文件问答
        let temporary = JSON.parse(`[${data}]`.replace(/}{/g, '},{'))

        temporary.forEach((item) => {
          this.modifyContent(item) // 修改返回内容
          nowChat.excludeReferenceAnswer += item.answer || '' // 排除回答中的引用内容
          nowChat.answer += item.answer || '\n' + (item.docs || '')
        })
      } else if (this.chatForm.pattern === 4) {
        let temporary = JSON.parse(`[${data}]`.replace(/}{/g, '},{'))
        temporary.forEach((item) => {
          this.modifyContent(item) // 修改返回内容
          nowChat.excludeReferenceAnswer += item.answer || '' // 排除回答中的引用内容
          nowChat.answer += item.answer || '\n' + (item.docs.join() || '')
          // nowChat.answer += item.answer += '\n' + item.docs.join()
        })
      }

      this.$nextTick(() => {
        this.$refs.contentBox.scrollTo(0, this.$refs.contentBox.scrollHeight)
      })
    },
    modifyContent(item) {
      // 修改返回内容
      if (item.docs) {
        item.docs = item.docs.map((item2) => {
          let tagUrl = ((item2?.match(/\]\((.*?)\)/g) || [])[0] || '').slice(2, -1)
          let url = ''
          // item2 = item2.replace(/\n/g, '<br>')
          if (tagUrl.indexOf('https') !== -1) {
            url = tagUrl.replace(/^https:\/\/[^/]+\//, `https://${window.location.hostname}:${window.location.port}/`)
            item2 = item2.replace(/\]\((.*?)\)/g, `](${url})`)
          } else {
            url = tagUrl.replace(/^http:\/\/[^/]+\//, `http://${window.location.hostname}:${window.location.port}/`)
            item2 = item2.replace(/\]\((.*?)\)/g, `](${url})`)
          }
          /// ////////出处内容包装div
          var match = (item2.match(/出处(.*)\)/) || [])[0]
          // 查找指定字符串的位置
          var indexOfSpecifiedString = item2.indexOf(match)
          if (indexOfSpecifiedString !== -1) {
            // 使用 slice 获取指定字符串后面的所有内容
            var contentAfterSpecifiedString = item2.slice(indexOfSpecifiedString + match.length)
            item2 = item2.replace(contentAfterSpecifiedString, `<div class="describe-view">${contentAfterSpecifiedString}</div>`)
          } else {
            console.log('未找到匹配的指定字符串')
          }
          /// ////////特殊标签后包装div
          // var specifiedString = '<a class="show_detail"></a>';
          // var match2 = item2.match(new RegExp(specifiedString + '(.*)')) || [];
          const match2 = item2.match(/<a\s+class="show_detail"\s+data="([^"]*)"><\/a>([\s\S]*)/)
          if (match2 && match2.length === 3) {
            var textAfterSpecifiedString = match2[2]
            var data = match2[1]
            // 修改文本内容
            textAfterSpecifiedString = `<div class="describe-view" data="${data}" >${textAfterSpecifiedString}</div>`
            item2 = item2.replace(match2[2], textAfterSpecifiedString)
          }
          return item2.replace('出处', '知识溯源')
        })
      }
    },
    generateRandomString(length) {
      const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
      const charactersLength = characters.length
      const randomValues = new Uint32Array(length)

      crypto.getRandomValues(randomValues)

      let result = ''
      for (let i = 0; i < length; i++) {
        result += characters.charAt(randomValues[i] % charactersLength)
      }

      return result
    }
  }
}
</script>
<style scoped>
.contentBox {
  height: 100%;
  box-sizing: border-box;
  padding-bottom: 20px;
  overflow-y: auto;
  overflow-x: hidden;
}

.initBox .text {
  background-color: #f3f3f3;
  padding: 10px;
  border-radius: 10px;
  width: 100%;
  line-height: 23px;
  font-size: 14px;
}

.initBox {
  height: 100%;
  box-sizing: border-box;
  padding-bottom: 20px;
}

.box {
  height: 100%;
  padding: 10px;
  position: relative;
  display: flex;
  flex-direction: column;
  background-image: url('~@/assets/img/ai.png');
  background-repeat: no-repeat;
  background-position: center center;
  background-size: auto 50%;
}

.icon {
  height: 32px;
  width: 32px;
  border-radius: 50%;
  text-align: center;
  overflow: hidden;
  line-height: 32px;

  flex-shrink: 0;
  color: #fff;
}

.userBox,
.gptBox {
  display: flex;
  margin-bottom: 20px;
  width: 100%;
}

.userBox {
  justify-content: flex-end;
}

.userBox .icon {
  margin-left: 10px;
  background-color: #4754b1;
}

.gptBox .icon {
  margin-right: 10px;
  background-color: #363c6e;
}

.content {
  background-color: #f3f3f3;
  padding: 10px;
  border-radius: 10px;
  height: fit-content;
}

.inputBox {
  display: flex;
}
</style>
