<template>
	<view class="w100 h100 flex">
		<!-- 侧边栏 -->
		<navbar :chatAsset="chatAsset" @click="getInputList" @history="getHistory" @delete="msgList = []" />
		<!-- 主体 -->
		<view class="w100 relative flex direction">
			<!-- 标题栏 -->
			<view class="title w100 flex item-center content-center size-large">
				{{ `AirSmartChat(${this.model})` }}
			</view>
			<!-- 			<view v-else class="flex item-center content-center p-10">
				<view class="background-color-gray p-5 gap radius flex">
					<button style="width: 400rpx;" v-for="item in chatgptVersion" :key="item"
						:class="['change-button radius button-none', item === model ? 'background-color-white' : 'background-color-gray']"
						@click="handleChangeVersion(item)">{{ item }}</button>
				</view>
			</view> -->
			<!-- 聊天区 -->
			<view class="chat size-large" style="flex:1">
				<block v-for="(x, i) in msgList" :key="i">
					<!-- 用户消息 -->
					<view v-if="x.my && x.type === 'msg'" class="chat-item " style="background-color: rgba(247,247,248,0.7);">
						<view class="chat-content">
							<view class="flex gap">
								<img class="flex direction item-end" src="../../static/logo-100.png" width="24" height="24" />
								<text>{{ x.msg }}</text>
							</view>
						</view>
					</view>
					<!-- AI消息 -->
					<view v-if="!x.my && x.type === 'msg'" class="chat-item">
						<view class="chat-content">
							<view class="flex gap">
								<img class="flex direction item-end" src="/static/chatgpt.png" width="24" height="24" />
								<img v-if="x.pic" :src="x.msg" />
								<text v-else>{{x.msg}}</text>
								<uni-icons class="ml-auto cursor-pointer" type="download" color="#acacbe"
									@click="x.msg && $squni.copy(x.msg)" />
							</view>
						</view>
					</view>
					<!-- 报错信息 -->
					<view v-if="x.type === 'error'" class="chat-item">
						<view class="chat-content flex gap item-center">
							<text class="cuIcon-roundclosefill text-red">{{ x.msg }}</text>
						</view>
					</view>
				</block>
			</view>
			<!-- 底部 -->
			<view class="w100 p-20 flex direction item-center background-color-white">
				<view class="w100">
					<view class="list flex wrap content-around">
						<uni-transition v-for="item in inputTipList" :key="item.type" :duration="500" show
							:mode-class="['fade', 'zoom-in']" @click="handleSendMsg(item)" :title="item.name"
							custom-class="item box-shadow p-15 border-gray radius cursor-pointer space-nowrap overflow-ellipsis overflow-hidden hover">
							{{ item.name }}
						</uni-transition>
					</view>
				</view>
				<!-- 输入框 -->
				<view class="w100">
					<view class="send-message relative box-shadow mt-10 radius border-gray">
						<textarea v-model="msg" auto-height maxlength="5000" cursor-spacing="10" :adjust-position="false"
							:placeholder="loading ? 'AirSmart is loading...' : 'Send a message'" @confirm="sendMsg"
							@input="handleInput" @keydown.tab.native.prevent="handleTab" />
						<uni-icons class="send-btn absolute cursor-pointer" type="paperplane-filled" size="24"
							:color="msg ? '#19c37d' : '#4559a480'" @click="sendMsg" />
					</view>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
	import {
		dateFormat,
		interval
	} from '@/util/squ.js'
	import squni, {
		scrollToBottom
	} from '@/util/squni.js'
	import websocket from '@/util/websocket'
	import {
		sendMsgApi,
		getUserChatAssetApi,
		startNewChatApi,
		findPromptListApi,
		sendMsgPic,
		msgList
	} from '@/api/chat.js'
	export default {
		data() {
			return {
				loading: false,
				userId: null,
				msgList: [],
				msgContent: "",
				msg: "",
				msgType: 0,
				chatAsset: {},
				assetType: 'n',
				msgLoading: false,
				promptId: null,
				curPrompt: {},
				inputTipList: [],
				model: 'gpt-4',
				// chatgpt版本
				chatgptVersion: ['gpt-3.5', 'gpt-4']
			}
		},
		watch: {
			loading(n, o) {
				if (n !== o && !n) {
					let last = this.msgList[this.msgList.length - 1]
					if (!last.my) {
						this.addHistory(last)
					}
				}
			},
		},
		async onShow() {
			await this.$ready

			this.userId = this.$store.getters.userId
			if (!this.userId) {
				this.$squni.toast('请先进行登录哦')
				return
			}

			this.promptId = this.$squni.getCurQuery('promptId')
			if (this.promptId) {
				findPromptListApi({
					id: this.promptId
				}).then(res => {
					if (res.status === 'success') {
						this.curPrompt = res.data

						if (this.ready && HELLO_MSG.msg.indexOf('提示词') < 0) {
							let prompt = ''
							if (this.curPrompt && this.curPrompt.title) {
								prompt = `（提示词：${this.curPrompt.title}）`
							}
							HELLO_MSG.msg += prompt
						}
					}
				})
			}

			getUserChatAssetApi().then(res => {
				if (res.status === 'success') {
					this.chatAsset = res.data
				}
			})

			try {
				//建立socket连接
				websocket.connectSocket(this.$config.wssUrl + '/tools/chat/user/' + this.userId, msg => {
					this.recvMsg(msg)
				}, () => {
					//如果连接成功则发送心跳检测
					this.heartBeatTest()
				})
			} catch (error) {
				console.log('websocket connectSocket error:' + error)
			}
		},
		onLoad() {
			msgList().then(res => {
				if (res.code === 20000) {
					this.inputTipList = res.data
				}
			})
		},
		onHide() {
			this.closeSocket()
		},
		methods: {
			logout() {
				this.loginLoading = true
				doLoginApi('H5', {
					username: this.email || this.phone || this.username,
					password: this.password
				}).then(res => {
					this.loginLoading = false
					if (res === LoginSuccess) {
						uni.showToast({
							title: '登录成功'
						})
						// 跳转到主页发现已经登录，会自动重新获取用户信息，此处无需获取
						window.location.href = getUrlQuery('_originHref')
					} else {
						uni.showToast({
							title: (res && res.data && res.data.message) || '登录失败',
							icon: 'none'
						})
						this.createCode(4)
					}
				})
			},
			sendMsg() {
				if (this.msg == "") {
					this.$squni.toast('请先输入您的问题哦')
					return
				}
				let msg = this.msg
				this.putMsg(this.msg, true)
				this.msgLoading = true
				this.loading = true

				// ======== 开发环境模拟回复 ========
				// return this.mockReply()
				// ======== 开发环境模拟回复 ========

				if (this.calcAsset() === false) {
					this.loading = false
					return
				}

				if (this.msgType === 2) {
					sendMsgPic({
						userId: this.$squni.getStorageSync('userId'),
						question: msg,
						type: this.msgType
					}).then(res => {
						if (res.code === 20000) {
							JSON.parse(res.data.ack).map(i => {
								this.putMsg(i.url, false, 'msg', true)
								this.loading = false
								this.msgLoading = false
								this.msgType = 0
							})
						} else {
							this.putMsgError('机器人被拔网线了，请稍后再试~')
						}
					})
				} else {
					// 发送消息: M1/M2
					websocket.sendMessage(JSON.stringify({
						model: this.model,
						msg: msg,
						platform: this.$squni.getStorageSync('platform'),
						openid: this.$squni.getStorageSync('openid'),
						promptId: this.promptId
					}), null, () => {
						this.putMsgError('机器人被拔网线了，请稍后再试~')
					})
				}
			},
			recvMsg(msg) {
				this.msgLoading = false
				if (!msg) {
					this.putMsgError('机器人开小差了，请稍后再试~')
					return
				}
				// 发送消息
				// 1+1
				// 收到消息
				// {"role":"assistant","content":null}
				// {"role":null,"content":"2"}
				// {"role":null,"content":null}
				// [DONE]
				if (msg === '[DONE]') {
					this.loading = false
				} else {
					try {
						let msgJson = JSON.parse(msg)
						if (msgJson.role === 'sqchat') {
							let content = msgJson.content
							if (msgJson.codeKey) {
								content += `[${msgJson.codeKey}]`
								if (msgJson.codeKey === 'chat.asset_short') {

								} else if (msgJson.codeKey.indexOf('chat.asset_') >= 0) {
									this.chatAsset[this.assetType]++
								}
							}
							this.putMsgError(content)
						}
						if (msgJson.role === 'assistant') {
							this.putMsg('', false)
						} else if (msgJson.role == null && msgJson.content) {
							this.msgList[this.msgList.length - 1].msg += msgJson.content
							scrollToBottom()
						}
					} catch (error) {
						this.putMsgError(msg)
					}
				}
			},
			startNewChat() {
				HELLO_MSG.date = dateFormat(new Date(), 'yyyy年MM月dd日 hh:mm')
				this.msgList = [HELLO_MSG]
				startNewChatApi()
			},
			calcAsset() {
				if (this.chatAsset.dfn > 0) {
					this.chatAsset.dfn--
					this.assetType = 'dfn'
				} else if (this.chatAsset.n > 0) {
					this.chatAsset.n--
					this.assetType = 'n'
				} else {
					this.$squni.toast('剩余次数不足')
					return false
				}
			},
			putMsg(msg, my = false, type = 'msg', pic = false) {
				let item = {
					type: type,
					msg: msg,
					my: my,
					pic: pic,
					date: dateFormat(new Date(), 'yyyy年MM月dd日 hh:mm')
				}
				this.msgList.push(item)
				scrollToBottom()
				if (my) {
					this.addHistory(item)
					// 清除消息
					this.msg = ''
					this.msgReply = ''
				}
			},
			putMsgError(msg) {
				this.putMsg(msg, false, 'error')
				this.msgLoading = false
				this.loading = false
			},
			addHistory(item) {
				if (item.type === 'msg') {
					let chatHistory = this.$squni.getStorageSync('chatHistory')
					if (!chatHistory) {
						chatHistory = []
					}
					if (chatHistory.length >= 50) {
						chatHistory.splice(0, 1)
					}
					chatHistory.push(item)
					this.$squni.setStorageSync('chatHistory', chatHistory)
				}
			},
			//心跳检测
			heartBeatTest() {
				let globalTimer = null
				//清除定时器
				clearInterval(globalTimer)
				//开启定时器定时检测心跳
				globalTimer = setInterval(() => {
					//发送消息给服务端
					websocket.sendMessage('PING', null, () => {
						//如果失败则清除定时器
						clearInterval(globalTimer)
					})
				}, 10000)
			},
			closeSocket() {
				websocket.closeSocket()
			},
			// 对话输入框输入'/'显示提示信息
			handleInput(e) {
				if (e.detail.value === '') {
					this.msgType = 0
				}
			},
			// 支持tab输入空格
			insertInputTxt(className, insertTxt) {
				var elInput = document.getElementsByClassName(className)
				var startPos = elInput[0].selectionStart
				var endPos = elInput[0].selectionEnd
				if (startPos === undefined || endPos === undefined) return
				var txt = elInput[0].value
				var result = txt.substring(0, startPos) + insertTxt + txt.substring(endPos)
				this.msg = elInput[0].value = result
				// elInput[0].focus()
				// elInput[0].selectionStart = startPos + insertTxt.length
				// elInput[0].selectionEnd = startPos + insertTxt.length
			},
			handleTab() {
				this.insertInputTxt('uni-textarea-textarea', '\t')
			},
			handleSendMsg(e) {
				this.msgType = e.type
				this.msg = e.name
			},
			mockReply() {
				// 开发环境模拟回复
				if (process.env.NODE_ENV === 'development') {
					setTimeout(() => {
						this.putMsg('这是模拟返回消息: ' + new Date(), false)
						this.loading = false
					}, 1000)
					return
				}
			},

			getInputList(e) {
				this.inputTipList = e
				this.msgList = []
			},

			// 切換chatgpt版本
			handleChangeVersion(e) {
				this.model = e
			},

			getHistory(e) {
				if (e) {
					this.msgList = e.children
				}
			}
		}
	}
</script>

<style lang="scss" scoped>
	uni-page-body {
		height: 100%;
		background-color: #FFF;
	}

	.title {
		height: 128rpx;
		border-bottom: 1px solid rgba(0, 0, 0, 0.1);
	}

	.chat {
		overflow-y: auto;

		.chat-item {
			width: 100%;
			border-bottom: 1px solid rgba(0, 0, 0, 0.1);

			.chat-content {
				max-width: 1600rpx;
				margin: 0 auto;
				padding: 62rpx 40rpx;
				cursor: text;
				user-select: text;
			}

			.date {
				margin-top: 10px;
			}
		}
	}

	.item {
		width: 24%;
	}

	.list,
	.send-message {
		max-width: 1560rpx;
		margin: 10px auto 0;
	}

	uni-textarea {
		width: calc(100% - 30px);
		max-height: 400rpx;
		overflow-y: auto;
		padding: 15px 20px;
		line-height: 1.6;
	}

	.send-btn {
		right: 30rpx;
		top: 50%;
		transform: translate(0, -50%) rotate(45deg);
	}
</style>