<template>
	<view class="fish-chat-page">
		<view class="nav" :style="{ paddingTop: statusBarTop + 'px' }">
			<view class="back" @tap="back"><uni-icons type="left" size="24" color="#15362f"></uni-icons></view>
			<view class="nav-main">
				<view class="nav-title">{{peerName}}</view>
				<view class="nav-sub">校园二手咨询 · 独立聊天</view>
			</view>
		</view>

		<view class="goods-card" v-if="goods" @tap="goGoods">
			<image class="goods-cover" v-if="goodsCover" :src="goodsCover" mode="aspectFill"></image>
			<view class="goods-cover no-cover" v-else>求购</view>
			<view class="goods-main">
				<view class="goods-title">{{goods.title}}</view>
				<view class="goods-price">{{isWant ? '预算 ¥' : '¥'}}{{displayPrice}}</view>
			</view>
			<view class="goods-link">查看 ›</view>
		</view>

		<scroll-view
			class="message-scroll"
			scroll-y
			:scroll-into-view="scrollIntoView"
			:scroll-with-animation="true"
			@scrolltoupper="loadOlder"
		>
			<view class="load-tip" v-if="loadingOlder">加载中...</view>
			<view class="safe-tip">请勿发送涉黄涉暴、辱骂诈骗等内容，文字和图片会进行内容审核。</view>
			<view
				class="message-row"
				:class="{ mine: item.mine }"
				v-for="item in messages"
				:key="item.id"
				:id="'msg-' + item.id"
			>
				<image class="avatar" :src="defaultAvatar" mode="aspectFill"></image>
				<view class="message-main">
					<view class="bubble text-bubble" :class="{ failed: item.localState == 'failed' }" v-if="item.messageType == 1">{{item.content}}</view>
					<view class="bubble image-bubble" :class="{ failed: item.localState == 'failed' }" v-else>
						<image :src="item.content" mode="aspectFill" @tap="previewImage(item.content)"></image>
					</view>
					<view class="message-status" v-if="item.mine && item.localState">{{statusText(item)}}</view>
				</view>
			</view>
			<view id="chat-bottom" class="bottom-anchor"></view>
		</scroll-view>

		<view class="input-bar">
			<view class="image-btn" @tap="chooseImage">
				<uni-icons type="image" size="24" color="#61736e"></uni-icons>
			</view>
			<input
				v-model.trim="draft"
				confirm-type="send"
				placeholder="请输入消息..."
				@confirm="sendText"
			/>
			<view class="send-btn" @tap="sendText">发送</view>
		</view>
	</view>
</template>

<script>
	import fishMarketSocket from '@/common/fishMarketSocket.js'
	import {
		getFishGoodsDetail,
		getFishWantDetail,
		getFishChatSession,
		pageFishChatMessages,
		uploadFishImage
	} from '@/common/fishMarketApi.js'

	export default {
		data() {
			return {
				statusBarTop: 24,
				sessionId: '',
				goodsId: '',
				wantId: '',
				session: null,
				goods: null,
				messages: [],
				pageNum: 1,
				pageSize: 20,
				totalPages: 1,
				loadingOlder: false,
				draft: '',
				scrollIntoView: 'chat-bottom',
				selfId: String(uni.getStorageSync('id') || ''),
				defaultAvatar: '/static/images/img/songshu.png',
				socketReady: false
			}
		},
		computed: {
			imageList() {
				if (!this.goods || !this.goods.imageList) return []
				return this.goods.imageList
			},
			isWant() {
				return !!this.wantId
			},
			goodsCover() {
				if (!this.goods) return ''
				return this.goods.coverImage || this.imageList[0] || ''
			},
			displayPrice() {
				if (!this.goods) return 0
				return this.isWant ? (this.goods.expectedPrice || 0) : (this.goods.price || 0)
			},
			peerName() {
				if (!this.goods || !this.session) return '校园同学'
				if (this.isWant) {
					return this.selfId == String(this.session.buyerId) ? '供货同学' : (this.goods.userName || '求购同学')
				}
				return this.selfId == String(this.session.sellerId) ? '买家同学' : (this.goods.userName || '卖家同学')
			},
			peerAvatar() {
				return this.defaultAvatar
			}
		},
		onLoad(options) {
			this.sessionId = options && options.sessionId ? options.sessionId : ''
			this.goodsId = options && options.goodsId ? options.goodsId : ''
			this.wantId = options && options.wantId ? options.wantId : ''
			if (uni.getMenuButtonBoundingClientRect) {
				this.statusBarTop = uni.getMenuButtonBoundingClientRect().top || this.statusBarTop
			}
			this.load()
		},
		onUnload() {
			if (this.sessionId) {
				fishMarketSocket.unsubscribeSession(this.sessionId)
			}
			fishMarketSocket.resetHandlers()
			fishMarketSocket.close()
		},
		methods: {
			back() {
				uni.navigateBack()
			},
			load() {
				if (!this.sessionId) return
				getFishChatSession(this.sessionId).then(session => {
					this.session = session
					const goodsId = this.goodsId || (session && session.goodsId)
					const wantId = this.wantId || (session && session.wantId)
					if (goodsId) {
						this.goodsId = goodsId
						getFishGoodsDetail(goodsId).then(goods => {
							this.goods = goods
						})
					} else if (wantId) {
						this.wantId = wantId
						getFishWantDetail(wantId).then(want => {
							this.goods = want
						})
					}
				})
				this.loadMessages(true).then(() => {
					this.initSocket()
				})
			},
			loadMessages(reset) {
				if (reset) {
					this.pageNum = 1
					this.totalPages = 1
				}
				return pageFishChatMessages(this.sessionId, this.pageNum, this.pageSize).then(page => {
					const records = page && (page.records || page.content) ? (page.records || page.content) : []
					const normalized = records.reverse().map(this.normalizeMessage)
					this.totalPages = Number(page && (page.pages || page.totalPages)) || 1
					this.messages = reset ? normalized : normalized.concat(this.messages)
					this.scrollToBottom()
					if (reset) {
						uni.$emit('fishUnreadChange')
					}
				})
			},
			loadOlder() {
				if (this.loadingOlder || this.pageNum >= this.totalPages) return
				this.loadingOlder = true
				this.pageNum += 1
				this.loadMessages(false).finally(() => {
					this.loadingOlder = false
				})
			},
			normalizeMessage(item) {
				return {
					...item,
					mine: String(item.senderId) == this.selfId,
					localState: item.localState || ''
				}
			},
			initSocket() {
				if (!this.sessionId) return
				fishMarketSocket.resetHandlers()
				fishMarketSocket.onMessage((message) => {
					if (!message || String(message.sessionId) !== String(this.sessionId)) return
					this.handleSocketMessage(message)
				})
				fishMarketSocket.connect().then(() => {
					this.socketReady = fishMarketSocket.subscribeSession(this.sessionId)
				}).catch(() => {
					this.socketReady = false
				})
			},
			handleSocketMessage(message) {
				if (message.type == 'audit_failed' || Number(message.auditStatus) == 2) {
					this.markMessageFailed(message.clientMsgId, message.auditReason || '内容审核未通过')
					uni.showToast({ title: message.auditReason || '内容审核未通过，请修改后重试', icon: 'none', duration: 3000 })
					return
				}
				this.appendMessage(message)
			},
			appendMessage(message) {
				if (!message || !message.id) return
				if (message.clientMsgId) {
					const local = this.messages.find(item => item.clientMsgId && item.clientMsgId == message.clientMsgId)
					if (local) {
						Object.assign(local, this.normalizeMessage(message), { localState: '' })
						this.scrollToBottom()
						return
					}
				}
				const exists = this.messages.some(item => String(item.id) == String(message.id))
				if (exists) return
				this.messages.push(this.normalizeMessage(message))
				this.scrollToBottom()
			},
			statusText(item) {
				if (item.localState == 'failed') return item.auditReason || '内容审核未通过'
				if (item.localState == 'sending') return '发送中'
				return ''
			},
			pushLocalMessage(messageType, content, clientMsgId) {
				this.messages.push({
					id: clientMsgId,
					clientMsgId,
					sessionId: this.sessionId,
					senderId: this.selfId,
					messageType,
					content,
					mine: true,
					localState: 'sending'
				})
				this.scrollToBottom()
			},
			markMessageFailed(clientMsgId, reason) {
				const local = this.messages.find(item => item.clientMsgId && item.clientMsgId == clientMsgId)
				if (!local) return
				local.localState = 'failed'
				local.auditStatus = 2
				local.auditReason = reason || '内容审核未通过'
			},
			sendText() {
				if (!this.draft) return
				const content = this.draft
				this.draft = ''
				this.sendMessage(1, content)
			},
			sendMessage(messageType, content) {
				const clientMsgId = 'fish-' + Date.now() + '-' + Math.random().toString(16).slice(2)
				this.pushLocalMessage(messageType, content, clientMsgId)
				const payload = {
					sessionId: this.sessionId,
					messageType,
					content,
					clientMsgId
				}
				if (fishMarketSocket.sendMessage(payload)) return
				this.markMessageFailed(clientMsgId, '聊天连接未就绪，请稍后重试')
				uni.showToast({ title: '聊天连接未就绪，请稍后重试', icon: 'none' })
			},
			chooseImage() {
				uni.chooseImage({
					count: 1,
					sizeType: ['compressed'],
					sourceType: ['album', 'camera'],
					success: async (res) => {
						const path = res.tempFilePaths && res.tempFilePaths[0]
						if (!path) return
						const url = await uploadFishImage(path)
						if (url) this.sendMessage(2, url)
					}
				})
			},
			scrollToBottom() {
				this.$nextTick(() => {
					this.scrollIntoView = ''
					setTimeout(() => {
						this.scrollIntoView = 'chat-bottom'
					}, 30)
				})
			},
			previewImage(url) {
				if (!url) return
				uni.previewImage({ urls: [url], current: url })
			},
			goGoods() {
				if (this.isWant) {
					if (!this.wantId) return
					uni.navigateTo({ url: '/package2/IdleTrad/detail?type=want&id=' + this.wantId })
					return
				}
				if (this.goodsId) {
					uni.navigateTo({ url: '/package2/IdleTrad/detail?id=' + this.goodsId })
				}
			}
		}
	}
</script>

<style lang="scss">
	page {
		width: 100%;
		height: 100%;
		background: #f7f9f7;
	}

	.fish-chat-page {
		width: 100%;
		height: 100%;
		display: flex;
		flex-direction: column;
		background: linear-gradient(180deg, #e8fbf3 0%, #f7f9f7 360rpx);
		color: #15362f;
	}

	.nav {
		padding-left: 24rpx;
		padding-right: 24rpx;
		display: flex;
		align-items: center;
		background: rgba(255, 255, 255, 0.88);
		box-shadow: 0 8rpx 20rpx rgba(20, 68, 52, 0.06);
	}

	.back {
		width: 68rpx;
		height: 68rpx;
		display: flex;
		align-items: center;
	}

	.nav-main {
		flex: 1;
	}

	.nav-title {
		font-size: 30rpx;
		font-weight: 900;
	}

	.nav-sub {
		margin-top: 4rpx;
		color: #7d8b86;
		font-size: 20rpx;
	}

	.goods-card {
		margin: 18rpx 24rpx 0;
		padding: 16rpx;
		display: flex;
		align-items: center;
		border-radius: 24rpx;
		background: #fff;
		box-shadow: 0 8rpx 22rpx rgba(20, 60, 48, 0.06);
	}

	.goods-cover {
		width: 104rpx;
		height: 104rpx;
		border-radius: 18rpx;
		background: #edf4f1;
	}

	.no-cover {
		display: flex;
		align-items: center;
		justify-content: center;
		color: #7f8d88;
		font-size: 22rpx;
		font-weight: 900;
	}

	.goods-main {
		flex: 1;
		min-width: 0;
		margin-left: 16rpx;
	}

	.goods-title {
		overflow: hidden;
		font-size: 26rpx;
		font-weight: 900;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	.goods-price {
		margin-top: 8rpx;
		color: #ef4c3f;
		font-size: 28rpx;
		font-weight: 900;
	}

	.goods-link {
		color: #7f8d88;
		font-size: 22rpx;
	}

	.message-scroll {
		flex: 1;
		min-height: 0;
		padding: 20rpx 24rpx;
		box-sizing: border-box;
	}

	.safe-tip,
	.load-tip {
		margin: 10rpx auto 24rpx;
		color: #8a9994;
		font-size: 21rpx;
		text-align: center;
	}

	.message-row {
		margin-bottom: 26rpx;
		display: flex;
		align-items: flex-start;
	}

	.message-row.mine {
		flex-direction: row-reverse;
	}

	.avatar {
		width: 70rpx;
		height: 70rpx;
		border-radius: 50%;
		background: #edf4f1;
	}

	.message-main {
		display: flex;
		flex-direction: column;
		align-items: flex-start;
	}

	.mine .message-main {
		align-items: flex-end;
	}

	.bubble {
		max-width: 520rpx;
		margin: 0 16rpx;
		border-radius: 24rpx;
		font-size: 27rpx;
		line-height: 42rpx;
		word-break: break-all;
	}

	.text-bubble {
		padding: 18rpx 22rpx;
		background: #fff;
		color: #253b35;
	}

	.mine .text-bubble {
		background: linear-gradient(135deg, #37c990, #19af7e);
		color: #fff;
	}

	.bubble.failed {
		opacity: .58;
	}

	.image-bubble {
		overflow: hidden;
		background: #fff;
	}

	.image-bubble image {
		width: 260rpx;
		height: 260rpx;
		display: block;
	}

	.message-status {
		max-width: 520rpx;
		margin: 8rpx 18rpx 0;
		color: #ef4c3f;
		font-size: 21rpx;
		line-height: 30rpx;
	}

	.bottom-anchor {
		height: 20rpx;
	}

	.input-bar {
		padding: 16rpx 22rpx calc(16rpx + env(safe-area-inset-bottom));
		display: flex;
		align-items: center;
		background: rgba(255, 255, 255, 0.96);
		box-shadow: 0 -8rpx 22rpx rgba(20, 60, 48, 0.06);
	}

	.image-btn {
		width: 70rpx;
		height: 70rpx;
		display: flex;
		align-items: center;
		justify-content: center;
	}

	.input-bar input {
		flex: 1;
		height: 72rpx;
		padding: 0 22rpx;
		border-radius: 38rpx;
		background: #f2f7f4;
		color: #213832;
		font-size: 26rpx;
	}

	.send-btn {
		width: 104rpx;
		height: 72rpx;
		margin-left: 14rpx;
		border-radius: 38rpx;
		background: linear-gradient(135deg, #37c990, #19af7e);
		color: #fff;
		font-size: 26rpx;
		font-weight: 900;
		text-align: center;
		line-height: 72rpx;
	}
</style>
