<template>
	<view class="content">
		<!-- 礼物显示列表（最多3个，主播端） -->
		<view class="gift-display-list" v-if="displayGiftList.length > 0">
			<view 
				v-for="(item, index) in displayGiftList" 
				:key="item.id"
				class="gift-display-item"
				:class="{ 'gift-display-item-fade-out': item.isFadingOut }"
				:style="getGiftItemStyle(item)"
			>
				<view class="gift-display-content">
					<text class="gift-display-name">{{ item.userName }}</text>
					<image 
						class="gift-display-image" 
						:src="item.giftImage" 
						mode="aspectFit"
					></image>
					<text class="gift-display-count">✖{{ item.count }}</text>
				</view>
			</view>
		</view>
		<div class="wanlive-pusher">
			<!-- #ifndef H5 -->
			<live-pusher
				id="livePusher"
				ref="livePusher"
				class="livePusher"
				:url="liveData.pushurl"
				:style="{width: screenWidth + 'px',height: screenHeight + 'px'}"
				mode="HD"
				aspect="9:16"
				@statechange="statechange"
				@netstatus="netstatus"
				@error="error"
			/>
			<!-- #endif -->
		</div>
		<!-- 仅一个 vue 子组件，避免多个全屏白底 webview 盖住 live-pusher -->
		<live-broadcast-overlay
			ref="overlay"
			:shopdata="shopData"
			:state="state"
			:online="liveStatis.online"
			:like="liveStatis.like"
			:liveId="liveData.id"
			:statusBarHeight="statusBarHeight"
			:statusFootHeight="statusFootHeight"
			:screenHeight="screenHeight"
			:screenWidth="screenWidth"
			@back="onHeaderBack"
			@send="sendLive"
			@change="handleLive"
		/>
		<!-- 打赏动画 - SVGA（H5 无 c-svga 组件，不渲染） -->
		<view class="gift-svga-container" v-if="currentGift.show && currentGift.svgaUrl">
			<!-- #ifndef H5 -->
			<!-- #ifndef APP-NVUE -->
			<c-svga 
				ref="giftSvga"
				:key="giftCanvasId"
				:canvasId="giftCanvasId"
				:src="currentGift.svgaUrl"
				:loops="1"
				:auto-play="true"
				width="750rpx"
				height="750rpx"
				@finished="onGiftAnimationEnd"
				@loaded="onGiftSvgaLoaded"
			></c-svga>
			<!-- #endif -->
			<!-- #endif -->
			<!-- #ifdef APP-NVUE -->
			<!-- nvue：仅当已配置 tx-svga 原生插件时渲染，避免未打包插件导致整页黑屏 -->
			<tx-svga
				v-if="svgaNativeReady"
				ref="giftTxSvga"
				class="gift-tx-svga"
				:style="{height: screenHeight + 'px'}"
				@statechange="onGiftTxSvgaStateChange"
			></tx-svga>
			<!-- #endif -->
			<view class="gift-text-overlay" v-if="currentGift.text">
				<text class="gift-text">{{ currentGift.text }}</text>
			</view>
		</view>
	</view>
</template>

<script>
var system = uni.getSystemInfoSync();
import LiveBroadcastOverlay from '@/components/live-room/broadcast-overlay.vue';
import { startBroadcastRoom } from '@/features/live/broadcast-gateway';
// #ifndef H5
// #ifndef APP-NVUE
import cSvga from '@/uni_modules/c-svga/components/c-svga/c-svga.vue';
// #endif
// #endif
export default {
	components: {
		LiveBroadcastOverlay
		// #ifndef H5
		// #ifndef APP-NVUE
		,cSvga
		// #endif
		// #endif
	},
	data() {
		return {
			statusBarHeight: system.safeAreaInsets.top,
			statusFootHeight: system.safeAreaInsets.bottom,
			screenHeight: system.screenHeight,
			screenWidth: system.screenWidth,
			state: 0,
			publish: true, // 推流状态
			startTimer: null,
			liveStatis: {
				online: 0,
				like: 0
			},
			liveData: {
				id: 0,
				liveid: ''
			},
			shopData: {
				shopname: ''
			},
			goodsData: [],
			goodsSelected: [],
			// 当前打赏礼物
			currentGift: {
				type: 'default',
				image: '',
				text: '',
				svgaUrl: '',
				show: false
			},
			// SVGA Canvas ID
			giftCanvasId: 'gift-svga-' + Date.now(),
			// 礼物队列
			giftQueue: [],
			// 是否正在播放礼物动画
			isPlayingGift: false,
			// 礼物动画超时定时器（防止队列被阻塞）
			giftAnimationTimeout: null,
			// 礼物显示列表（最多3个，主播端）
			displayGiftList: [],
			// 礼物ID计数器（用于生成唯一ID）
			giftIdCounter: 0,
			// 当前正在播放的礼物显示ID
			currentPlayingGiftId: null,
			// nvue：是否已集成 tx-svga 原生插件（未集成时不渲染 tx-svga 组件）
			svgaNativeReady: false,
			_svgaModel: null
		};
	},
	onLoad() {
		// #ifdef APP-NVUE
		try {
			const m = uni.requireNativePlugin('tx-svgamodel');
			if (m) {
				this._svgaModel = m;
				this.svgaNativeReady = true;
			}
		} catch (e) {
			console.warn('tx-svgamodel 未配置，礼物 SVGA 动画将跳过', e);
		}
		// #endif
		if (this.$appKit && !this.$appKit.requireLogin()) {
			setTimeout(() => {
				uni.navigateBack({ fail: () => uni.switchTab({ url: '/pages/mine/index' }) })
			}, 300)
			return
		}
		if (this.$store) this.$store.dispatch('im/resume').catch(() => {})
	},
	// 监听页面显示（手机解锁后）
	onShow() {
		if (this.$store) this.$store.dispatch('im/resume').catch(() => {})
		console.log('页面显示，检查推流和WebSocket连接状态');
		// 检查并重连 WebSocket
		this.checkAndReconnectSocket();
		// 如果正在推流，检查推流状态并尝试恢复
		if (this.publish && this.state == 1 && this.context) {
			// 延迟一下，确保页面完全显示
			setTimeout(() => {
				this.checkAndResumePusher();
			}, 500);
		}
	},
	// 监听页面隐藏（手机黑屏时）
	onHide() {
		console.log('页面隐藏（可能黑屏）');
		// 记录状态，但不停止推流（让系统处理）
		// 如果需要在黑屏时停止推流，可以在这里调用 this.context.pause()
	},
	// 监听页面卸载
	onUnload() {
		this.publish = false;
		this.pageUnload();
		// 清空礼物队列
		this.giftQueue = [];
		this.isPlayingGift = false;
		// 清空显示列表
		this.displayGiftList = [];
		this.currentPlayingGiftId = null;
		// 清除超时定时器
		if (this.giftAnimationTimeout) {
			clearTimeout(this.giftAnimationTimeout);
			this.giftAnimationTimeout = null;
		}
	},
	// 监听设备返回键：先弹框，确认后才退出
	onBackPress() {
		this.publish = false;
		if (this.liveData.id == 0) {
			this.safeBack();
		} else {
			this.showExitLiveConfirm();
		}
	},
	onReady() {
		// #ifdef H5
			uni.showToast({title: 'H5不支持直播推流', icon: 'error'});
		// #endif
		// #ifndef H5
		setTimeout(() => {
			this.context = uni.createLivePusherContext('livePusher', this);
			this.context.startPreview();
		}, 100);
		// 保持屏幕常亮 1.0.5升级
		uni.setKeepScreenOn({
			keepScreenOn: true
		});
		// #endif
		// 监听直播消息
		uni.$on('onLiveMessage', this.onMessage);
	},
	methods: {
		// 未开播时也要确保可退出：TabBar 页面用 switchTab 兜底
		safeBack() {
			uni.navigateBack({
				fail: () => uni.switchTab({ url: '/pages/mine/index' })
			})
		},
		// 先弹框确认，用户点击确定后才结束直播并跳转；取消则留在当前直播界面
		showExitLiveConfirm() {
			const lang = uni.getLocale();
			let modalTitle, modalContent;
			if (lang === 'zh-cn') {
				modalTitle = '提示';
				modalContent = '确定要结束直播吗？';
			} else if (lang === 'zh-Hant') {
				modalTitle = '提示';
				modalContent = '確定要結束直播嗎？';
			} else {
				modalTitle = 'Tips';
				modalContent = 'Are you sure you want to end the live broadcast?';
			}
			uni.showModal({
				title: modalTitle,
				content: modalContent,
				success: (res) => {
					if (res.confirm) {
						this.handleLive('stop');
						uni.redirectTo({
							url: `/pages/live/end?id=${this.liveData.id}`,
							fail: () => uni.switchTab({ url: '/pages/live/hall' })
						});
					}
				}
			});
		},
		// 点击头部返回按钮：先弹框，确认后才退出，取消则留在直播界面
		onHeaderBack() {
			if (this.liveData.id == 0) {
				this.safeBack();
			} else {
				this.showExitLiveConfirm();
			}
		},
		// 接受直播群组消息
		onMessage(msg) {
			const overlay = this.$refs.overlay;
			console.log('主播端 onMessage 收到消息，state:', this.state, 'overlay存在:', !!overlay, '消息类型:', msg.message?.type);
			// 处理所有消息类型，包括 coming
			if (this.state == 1 && overlay) {
				this.$nextTick(() => {
					if (this.$refs.overlay) {
						console.log('传递消息给 wanl-live-message 组件');
						this.$refs.overlay.onMessage(msg);
					} else {
						console.warn('$nextTick 后 message 组件不存在');
					}
				});
			} else {
				// 组件未准备好，记录详细日志
				console.warn('消息无法传递给组件：state=' + this.state + ', overlay存在=' + !!overlay, '消息类型:', msg.message?.type);
				// 如果是 coming 消息，尝试延迟重试
				if(msg.message && msg.message.type == 'coming'){
					setTimeout(() => {
						if (this.state == 1 && this.$refs.overlay) {
							console.log('延迟重试传递 coming 消息');
							this.$refs.overlay.onMessage(msg);
						}
					}, 500);
				}
			}
			
			if(this.liveData.liveid == msg.group){
				console.log('主播端 WebSocket 消息:'+JSON.stringify(msg));
				const innerMsg = msg.message && typeof msg.message === 'object' ? msg.message : null;
				if(innerMsg && innerMsg.type == 'publish'){ // 推流成功
					uni.hideLoading();
					clearTimeout(this.startTimer);
				}else if(innerMsg && innerMsg.type == 'publish_done'){ // 中断
					// 如果不是手动关闭，自动恢复直播推流
					if (this.publish) {
						// 获取当前语言
						const lang = uni.getLocale();
						// 根据语言设置文本
						let toastTitle;
						if (lang === 'zh-cn') {
							// 简体中文
							toastTitle = '网络连接不佳';
						} else if (lang === 'zh-Hant') {
							// 繁体中文
							toastTitle = '網絡連接不佳';
						} else {
							// 其他语言（默认英文）
							toastTitle = 'Poor network connection';
						}
						let intervalID = setInterval(() => {
							uni.showToast({
									title: toastTitle,
								icon: 'error'
							});
							this.context.resume({
							    success: (a) => {
									clearInterval(intervalID);
							    }
							});
						}, 1500);
					}
				}else if(innerMsg && innerMsg.type == 'ban'){ // 封禁
					//封禁直播跳转结束页
				}
				
				// 处理礼物消息（兼容两种结构）
				// 1) msg.type == 'tip_done'
				// 2) msg.message.type == 'tip_done'
				const isTipDone =
					(msg && msg.type === 'tip_done') ||
					(msg && msg.message && msg.message.type === 'tip_done');
				
				if (isTipDone) {
					// 统一读取 messageText / fileUrl / giftId / price
					const messageObj = (msg && msg.message && typeof msg.message === 'object') ? msg.message : null;
					const messageText =
						(messageObj && (messageObj.msg || messageObj.message)) ?
							(String(messageObj.msg || messageObj.message)) :
						(typeof msg.message === 'string' ? msg.message : '');
					const fileUrl =
						(messageObj && messageObj.file_url) ? messageObj.file_url :
						(msg && msg.file_url) ? msg.file_url :
						'';
					const giftId =
						(messageObj && (messageObj.id || messageObj.gift_id)) ? (messageObj.id || messageObj.gift_id) :
						(msg && msg.gift_id) ? msg.gift_id :
						0;
					
					// 读取 is_svga：优先 message.is_svga，然后是 msg.is_svga，默认 0（表示没有 SVGA）
					const isSvga = 
						(messageObj && messageObj.is_svga !== undefined && messageObj.is_svga !== null) ? Number(messageObj.is_svga) :
						(msg && msg.is_svga !== undefined && msg.is_svga !== null) ? Number(msg.is_svga) :
						0;
					
					// 从 "xxx tipped a Rose" 提取礼物名
					let giftName = '禮物';
					if (messageText) {
						const match = messageText.match(/tipped a (.+)$/i);
						if (match && match[1]) giftName = match[1].trim();
					}
					
					// 读取礼物数量：优先 message.number，然后是 message.count/num，最后是 msg.count，默认 1
					const giftCount = 
						(messageObj && messageObj.number !== undefined && messageObj.number !== null) ? Number(messageObj.number) :
						(messageObj && (messageObj.count !== undefined || messageObj.num !== undefined)) ? Number(messageObj.count || messageObj.num) :
						(msg && msg.count !== undefined && msg.count !== null) ? Number(msg.count) :
						1;
					
					const giftData = {
						gift_id: giftId,
						gift_name: giftName,
						gift_image: (messageObj && messageObj.image) ? messageObj.image : (msg.gift_image || ''),
						gift_type: msg.gift_type || 'default',
						count: giftCount,
						user_name: msg.form?.nickname || '用戶',
						// 新结构下：file_url 在 msg.message.file_url
						file_url: fileUrl,
						svga: fileUrl
					};
					console.log('主播端处理 tip_done 消息（统一格式），礼物数据:', giftData, 'is_svga:', isSvga, 'fileUrl:', fileUrl);
					
					// 判断是否需要播放 SVGA 动画
					// 如果 is_svga == 1 或 file_url == null，则只显示在左上角列表，不播放动画
					const shouldPlayAnimation = isSvga !== 1 && fileUrl !== null && fileUrl !== undefined && fileUrl.trim() !== '';
					
					if (shouldPlayAnimation) {
						// 队列需要播放 giftCount 次，但显示列表只能显示 1 条（✖giftCount），并且等全部播放完才淡出
						// 约定：队列里的每一条都按 count=1 播放；显示层用 displayDelta 累加总数与剩余播放次数
						const baseGiftForQueue = { ...giftData, count: 1 };
						// 第一次：创建/更新显示项（displayDelta = giftCount）+ 入队一次
						this.addGiftToQueue(baseGiftForQueue, { displayDelta: giftCount });
						// 其余：只入队，不更新显示（displayDelta = 0）
						for (let i = 1; i < giftCount; i++) {
							this.addGiftToQueue(baseGiftForQueue, { displayDelta: 0 });
						}
					} else {
						// 只更新显示列表，不播放动画
						console.log('主播端 is_svga==1 或 file_url==null，只显示在左上角列表，不播放 SVGA 动画');
						this.addGiftToDisplayOnly(giftData, giftCount);
					}
				}
				
				this.liveStatis.like = msg.like;
				this.liveStatis.online = msg.online;
			}
		},
		async sendLive(e) {
			switch (e.type) {
				case 'msg':
					await uni.request({
						url: '/wanlshop/live/send',
						data: {
							message: e.message,
							group: this.liveData.liveid
						}
					});
					break;
				case 'like':
					this.liveStatis.like += 1;
					await uni.request({
						url: '/wanlshop/live/like',
						data: {
							id: this.liveData.id
						}
					});
					break;
			}
		},
		// 监听页面是否卸载，只卸载已播放
		async pageUnload(){
			if (this.state == 1) {
				await uni.request({
					url: '/wanlshop/live/unload',
					data: { 
						group: this.liveData.liveid,
						type: 'rtmp'
					}
				});
			}
		},
		// 操作直播
		async handleLive(method, data = []) {
			// #ifdef H5
				uni.showToast({title: 'H5不支持直播推流', icon: 'error'});
			// #endif
			// #ifndef H5
			switch(method)
			{
				// 开始推流
				case 'start': {
					const lang = uni.getLocale();
					let loadingTitle = 'Live starting';
					if (lang === 'zh-cn') loadingTitle = '启动中';
					else if (lang === 'zh-Hant') loadingTitle = '啟動中';
					const clearStartTimer = () => {
						if (this.startTimer) {
							clearTimeout(this.startTimer);
							this.startTimer = null;
						}
					};
					uni.showLoading({ title: loadingTitle, mask: true });
					this.startTimer = setTimeout(() => {
						clearStartTimer();
						uni.hideLoading();
						if (this.state !== 1) {
							uni.showToast({ title: 'API Time out', icon: 'error' });
							const errorMsg = 'API直播回调超时：请检查、1.推流地址是否正确，2.直播是否回调到API服务器，3.检查IM是否正常启动';
							console.error(errorMsg);
							this.saveError('startLive_Timeout', errorMsg);
							uni.$emit('liveBroadcastStartFail');
						}
					}, 10000);
					const requestData = {
						...data,
						is_paid: data.is_paid || 0,
						paid_money: data.paid_money || 0,
						free_look_time: data.free_look_time || 0
					};
					try {
						const room = await startBroadcastRoom(requestData);
						clearStartTimer();
						uni.hideLoading();
						if (!room || (!room.pushurl && !room.liveid && !room.id)) {
							uni.$emit('liveBroadcastStartFail');
							break;
						}
						this.liveData = room;
						this.state = 1;
						setTimeout(() => {
							if (!this.context) return;
							this.context.start({
								success: (e) => this.startlive(e)
							});
						}, 100);
						uni.$emit('liveBroadcastStartOk');
					} catch (err) {
						clearStartTimer();
						uni.hideLoading();
						console.error('startLive failed', err);
						this.saveError('startLive_API', (err && err.message) ? err.message : String(err));
						uni.$emit('liveBroadcastStartFail');
					}
					break;
				}
				// 停止推流
				case 'stop':
				    this.context.stop();
				    break;
				// 翻转相机
			    case 'switch':
			        this.context.switchCamera();
			        break;
				// 快照：截图并保存到系统相册（iOS 与 Android 回调字段可能不一致，需统一解析）
				case 'snapshot': {
					let snapshotSaved = false;
					let snapshotFailed = false;
					const trySaveSnapshot = (raw, from) => {
						if (snapshotSaved || snapshotFailed) return;
						const tempPath = this.resolveLiveSnapshotPath(raw);
						if (!tempPath) return;
						snapshotSaved = true;
						console.log('live snapshot path from', from, tempPath);
						this.saveLiveSnapshotToAlbum(tempPath);
					};
					this.context.snapshot({
						success: (e) => {
							console.log('live snapshot success:', e);
							trySaveSnapshot(e, 'success');
						},
						fail: (err) => {
							snapshotFailed = true;
							console.error('live snapshot fail:', err);
							uni.showToast({ title: '截图失败', icon: 'none' });
						},
						complete: (e) => {
							// iOS 等：路径有时只在 complete 或嵌套在 message 里
							if (!snapshotSaved && !snapshotFailed) {
								trySaveSnapshot(e, 'complete');
							}
							if (!snapshotSaved && !snapshotFailed) {
								uni.showToast({ title: '未获取到截图文件', icon: 'none' });
							}
						}
					});
					break;
				}
				// 更改导航栏
				case 'user':
				    this.shopData = data;
				    break;
			    default:
			}
			// #endif
		},
		/**
		 * 从 snapshot 回调对象中解析临时图片路径（兼容 App-iOS / Android / 小程序差异）
		 */
		resolveLiveSnapshotPath(e) {
			if (!e || typeof e !== 'object') return '';
			const tryStr = (v) => (v && typeof v === 'string' && v.length > 0 ? v : '');
			const list = [
				tryStr(e.tempImagePath),
				tryStr(e.tempFilePath),
				tryStr(e.tempPath),
				tryStr(e.path),
				e.detail && tryStr(e.detail.tempImagePath),
				e.detail && tryStr(e.detail.tempFilePath),
				// iOS App 常见：路径在 message 对象内
				e.message && typeof e.message === 'object' && tryStr(e.message.tempImagePath),
				e.message && typeof e.message === 'object' && tryStr(e.message.tempFilePath)
			];
			for (let i = 0; i < list.length; i++) {
				if (list[i]) return list[i];
			}
			// 部分版本 message 为 JSON 字符串
			if (e.message && typeof e.message === 'string') {
				try {
					const o = JSON.parse(e.message);
					if (o && typeof o === 'object') {
						const p = tryStr(o.tempImagePath) || tryStr(o.tempFilePath);
						if (p) return p;
					}
				} catch (err) {
					// ignore
				}
			}
			return '';
		},
		/** 将直播截图临时文件写入系统相册（getImageInfo 可修正 iOS 临时路径格式） */
		saveLiveSnapshotToAlbum(filePath) {
			const runSave = (path) => {
				uni.saveImageToPhotosAlbum({
					filePath: path,
					success: () => {
						uni.showToast({ title: '已保存到相册', icon: 'success' });
					},
					fail: (err) => {
						console.error('saveImageToPhotosAlbum:', err);
						const msg = ((err && err.errMsg) || '').toLowerCase();
						const needAuth =
							msg.indexOf('auth') !== -1 ||
							msg.indexOf('deny') !== -1 ||
							msg.indexOf('permission') !== -1 ||
							msg.indexOf('authorize') !== -1;
						if (needAuth) {
							uni.showModal({
								title: '提示',
								content: '保存图片需要相册权限，请在设置中开启',
								confirmText: '去设置',
								cancelText: '取消',
								success: (res) => {
									if (res.confirm) {
										uni.openSetting({});
									}
								}
							});
						} else {
							uni.showToast({ title: '保存失败', icon: 'none' });
						}
					}
				});
			};
			uni.getImageInfo({
				src: filePath,
				success: (img) => runSave(img.path || filePath),
				fail: () => runSave(filePath)
			});
		},
		startlive(e){
			
			// 如果启动失败，上报错误
			if (e.errMsg && e.errMsg.indexOf('fail') !== -1) {
			console.log('开启直播事件:' + JSON.stringify(e));
				this.saveError('startlive', JSON.stringify(e));
			}
		},
		statechange(e) {
			console.log('推流状态变化:', e.detail);
			// 如果状态异常，上报错误
			if (e.detail.code && e.detail.code < 0) {
			console.log('状态变化事件:' + e.detail.message);
				this.saveError('statechange', JSON.stringify(e.detail));
				// 如果正在推流且状态异常，尝试恢复
				if (this.publish && this.state == 1) {
					console.log('推流状态异常，尝试恢复');
					this.checkAndResumePusher();
				}
			}
		},
		netstatus(e) {
			
			// 如果网络状态异常，上报错误
			if (e.detail.videoBitrate === 0 || e.detail.audioBitrate === 0) {
			console.log('网络状态通知:' + JSON.stringify(e));
				this.saveError('netstatus', JSON.stringify(e.detail));
			}
		},
		error(e) {
			console.error('渲染错误事件:' + JSON.stringify(e));
			// 上报错误
			this.saveError('livePusher_error', JSON.stringify(e.detail || e));
		},
		// 检查并重连 WebSocket（新工程 im 模块）
		checkAndReconnectSocket() {
			if (this.$store) {
				this.$store.dispatch('im/resume').catch(() => {})
			}
		},
		// 检查并恢复推流
		checkAndResumePusher() {
			if (!this.context) {
				console.warn('推流上下文不存在，无法恢复');
				return;
			}
			if (!this.publish || this.state != 1) {
				console.log('不在推流状态，无需恢复');
				return;
			}
			
			// 同时检查并重连 WebSocket
			this.checkAndReconnectSocket();
			
			// 获取当前语言
			const lang = uni.getLocale();
			let toastTitle;
			if (lang === 'zh-cn') {
				toastTitle = '正在恢复推流...';
			} else if (lang === 'zh-Hant') {
				toastTitle = '正在恢復推流...';
			} else {
				toastTitle = 'Resuming live stream...';
			}
			
			console.log('尝试恢复推流');
			// 尝试恢复推流
			this.context.resume({
				success: (res) => {
					console.log('推流恢复成功:', res);
					uni.showToast({
						title: toastTitle,
						icon: 'success',
						duration: 2000
					});
				},
				fail: (err) => {
					console.error('推流恢复失败:', err);
					// 如果 resume 失败，尝试重新启动推流
					setTimeout(() => {
						console.log('resume 失败，尝试重新启动推流');
						if (this.liveData.pushurl) {
							this.context.start({
								success: (e) => {
									console.log('推流重新启动成功:', e);
								},
								fail: (e) => {
									console.error('推流重新启动失败:', e);
									this.saveError('resume_failed', JSON.stringify(e));
								}
							});
						}
					}, 1000);
				}
			});
		},
		// 保存错误信息到服务器
		async saveError(title, error) {
			try {
				await uni.request({
					url: '/index/saveerror',
					method: 'POST',
					data: {
						title: title,
						error: error
					}
				});
			} catch (err) {
				console.error('上报错误失败:', err);
			}
		},
		// 将礼物加入队列（带显示层聚合逻辑）
		addGiftToQueue(gift, options = {}) {
			console.log('主播端 addGiftToQueue called with:', gift);
			const displayDeltaRaw = options && options.displayDelta !== undefined ? options.displayDelta : undefined;
			
			// 处理不同的数据格式，提取显示信息
			let giftData = {};
			if (gift.gift_id || gift.gift_name || gift.file_url) {
				giftData = {
					image: gift.gift_image || gift.image || '',
					name: gift.gift_name || gift.name || '禮物',
					userName: gift.user_name || gift.form?.nickname || '用戶',
					svga: gift.file_url || gift.svga || gift.show_image || gift.animation || ''
				};
			} else {
				giftData = {
					image: gift.image || '',
					name: gift.name || '禮物',
					userName: gift.userName || gift.user_name || '用戶',
					svga: gift.svga || gift.show_image || gift.animation || ''
				};
			}
			
			const displayDelta = typeof displayDeltaRaw === 'number' ? displayDeltaRaw : 1;
			const giftKey = `${giftData.userName}__${giftData.name}__${giftData.image}`;
			const existingItem = this.displayGiftList.find(item => item && item.giftKey === giftKey && !item.isFadingOut);
			
			let displayItemId;
			if (existingItem) {
				displayItemId = existingItem.id;
				if (displayDelta > 0) {
					this.$set(existingItem, 'count', (Number(existingItem.count || 0) || 0) + displayDelta);
					this.$set(existingItem, 'remainingPlays', (Number(existingItem.remainingPlays || 0) || 0) + displayDelta);
				}
			} else {
				const initDelta = displayDelta > 0 ? displayDelta : 1;
				if (this.displayGiftList.length >= 3) {
					this.displayGiftList.shift();
				}
				displayItemId = ++this.giftIdCounter;
				this.displayGiftList.push({
					id: displayItemId,
					giftKey,
					userName: giftData.userName,
					giftImage: giftData.image,
					count: initDelta,
					remainingPlays: initDelta,
					isAnimating: false,
					isFadingOut: false
				});
			}
			
			// 入队（带 displayId 用于回调扣 remainingPlays）
			const giftWithId = { ...gift, _displayId: displayItemId };
			this.giftQueue.push(giftWithId);
			console.log('礼物已加入队列，当前队列长度:', this.giftQueue.length);
			if (!this.isPlayingGift) {
				this.processGiftQueue();
			}
		},
		// 只更新显示列表，不播放动画（用于 is_svga==1 或 file_url==null 的情况）
		addGiftToDisplayOnly(gift, giftCount) {
			console.log('主播端 addGiftToDisplayOnly called with:', gift, 'giftCount:', giftCount);
			
			// 处理不同的数据格式，提取显示信息
			let giftData = {};
			if (gift.gift_id || gift.gift_name || gift.file_url) {
				// WebSocket 推送的格式
				giftData = {
					type: gift.gift_type || gift.type || 'default',
					image: gift.gift_image || gift.image || '',
					name: gift.gift_name || gift.name || '禮物',
					count: giftCount || 1,
					userName: gift.user_name || gift.form?.nickname || '用戶',
					svga: gift.file_url || gift.svga || gift.show_image || gift.animation || ''
				};
			} else {
				// 本地发送的格式
				giftData = {
					type: gift.type || 'default',
					image: gift.image || '',
					name: gift.name || '禮物',
					count: giftCount || 1,
					userName: gift.userName || gift.user_name || '用戶',
					svga: gift.svga || gift.show_image || gift.animation || ''
				};
			}
			
			// 显示层聚合 Key：同一用户 + 同一礼物（用礼物名 + 图片双保险）
			const giftKey = `${giftData.userName}__${giftData.name}__${giftData.image}`;
			
			// 查找同 Key 且未淡出的显示项
			const existingItem = this.displayGiftList.find(item =>
				item &&
				item.giftKey === giftKey &&
				!item.isFadingOut
			);
			
			if (existingItem) {
				// 复用现有显示项：更新显示数量
				const nextCount = (Number(existingItem.count || 0) || 0) + giftCount;
				this.$set(existingItem, 'count', nextCount);
				// 不播放动画，所以 remainingPlays 设为 0，立即淡出
				this.$set(existingItem, 'remainingPlays', 0);
				console.log('主播端复用显示项，ID:', existingItem.id, 'count:', existingItem.count);
				
				// 立即标记为淡出（因为不播放动画）
				this.$set(existingItem, 'isFadingOut', true);
				// 1秒后移除
				setTimeout(() => {
					const index = this.displayGiftList.findIndex(item => item && item.id === existingItem.id);
					if (index !== -1) {
						this.displayGiftList.splice(index, 1);
						console.log('主播端淡出完成，已移除item，剩余显示项数量:', this.displayGiftList.length);
						// #ifdef APP-NVUE
						this.displayGiftList = [...this.displayGiftList];
						// #endif
						this.$nextTick(() => this.$forceUpdate());
					}
				}, 1000);
			} else {
				// 如果不存在：创建新显示项
				// 添加到显示列表（最多3个）
				if (this.displayGiftList.length >= 3) {
					// 如果已满，移除最旧的（第一个）
					this.displayGiftList.shift();
				}
				
				// 生成唯一ID
				const displayItemId = ++this.giftIdCounter;
				
				// 添加到显示列表：count 为显示数量；remainingPlays 为 0（不播放动画）
				const newItem = {
					id: displayItemId,
					giftKey,
					giftName: giftData.name,
					userName: giftData.userName,
					giftImage: giftData.image,
					count: giftCount,
					remainingPlays: 0, // 不播放动画，所以设为 0
					isAnimating: false,
					isFadingOut: false,
					svgaUrl: giftData.svga
				};
				this.displayGiftList.push(newItem);
				console.log('主播端创建新显示项（仅显示），ID:', displayItemId, 'count:', giftCount);
				
				// 3秒后自动淡出（因为没有动画，所以显示一段时间后自动消失）
				setTimeout(() => {
					const index = this.displayGiftList.findIndex(item => item && item.id === displayItemId);
					if (index !== -1) {
						const item = this.displayGiftList[index];
						this.$set(item, 'isFadingOut', true);
						// 1秒后移除
						setTimeout(() => {
							const removeIndex = this.displayGiftList.findIndex(i => i && i.id === displayItemId);
							if (removeIndex !== -1) {
								this.displayGiftList.splice(removeIndex, 1);
								console.log('主播端淡出完成，已移除item，剩余显示项数量:', this.displayGiftList.length);
								// #ifdef APP-NVUE
								this.displayGiftList = [...this.displayGiftList];
								// #endif
								this.$nextTick(() => this.$forceUpdate());
							}
						}, 1000);
					}
				}, 3000);
			}
		},
		// 处理礼物队列
		processGiftQueue() {
			// 如果队列为空或正在播放，直接返回
			if (this.giftQueue.length === 0) {
				console.log('队列为空，无需处理');
				return;
			}
			if (this.isPlayingGift) {
				console.log('正在播放动画，等待完成后处理队列，当前队列长度:', this.giftQueue.length);
				return;
			}
			// 从队列中取出第一个礼物
			const gift = this.giftQueue.shift();
			console.log('从队列中取出礼物，剩余队列长度:', this.giftQueue.length, '礼物信息:', gift);
			// 播放礼物动画
			this.playGiftAnimation(gift);
		},
		// 播放打赏动画
		// gift 参数可能是：
		// 1. 从 WebSocket 消息来的：{ gift_id, gift_name, gift_image, gift_type, count, user_name, svga }
		playGiftAnimation(gift) {
			console.log('主播端 playGiftAnimation called with:', gift);
			// 标记当前播放的礼物显示ID
			if (gift && gift._displayId) {
				this.currentPlayingGiftId = gift._displayId;
				const idx = this.displayGiftList.findIndex(x => x && x.id === gift._displayId);
				if (idx !== -1) {
					this.$set(this.displayGiftList[idx], 'isAnimating', true);
				}
			}
			// 标记正在播放
			this.isPlayingGift = true;
			// 清除之前的超时定时器（如果有）
			if (this.giftAnimationTimeout) {
				clearTimeout(this.giftAnimationTimeout);
				this.giftAnimationTimeout = null;
			}
			// 设置超时保护：如果 5 秒后还没有收到动画结束回调，强制重置状态
			this.giftAnimationTimeout = setTimeout(() => {
				console.warn('礼物动画超时（5秒），强制重置状态，队列长度:', this.giftQueue.length);
				this.onGiftAnimationEnd();
			}, 5000);
			
			// 处理不同的数据格式
			let giftData = {}
			
			if (gift.gift_id || gift.gift_name || gift.file_url) {
				// WebSocket 推送的格式
				giftData = {
					type: gift.gift_type || gift.type || 'default',
					image: gift.gift_image || gift.image || '',
					name: gift.gift_name || gift.name || '禮物',
					count: gift.count || 1,
					userName: gift.user_name || gift.form?.nickname || '',
					svga: gift.file_url || gift.svga || gift.show_image || gift.animation || ''
				}
			} else {
				// 本地发送的格式
				giftData = {
					type: gift.type || 'default',
					image: gift.image || '',
					name: gift.name || '禮物',
					count: gift.count || 1,
					svga: gift.svga || gift.show_image || gift.animation || ''
				}
			}
			
			// 构建显示文本（不显示数量；数量只在左上角列表显示）
			let displayText = `${giftData.name}`
			if (giftData.userName) {
				displayText = `${giftData.userName} 送出了 ${displayText}`
			}
			
			// 获取 SVGA 文件 URL
			let svgaUrl = '';
			if (giftData.svga) {
				// 如果已经是完整URL，直接使用
				if (/^(http|https):\/\/.+/.test(giftData.svga)) {
					svgaUrl = giftData.svga;
				} else {
					// 否则拼接 CDN URL
					const cdnUrl = getApp().globalData.config?.cdnurl || '';
					svgaUrl = cdnUrl ? (cdnUrl + giftData.svga) : giftData.svga;
				}
			}
			
			// 每次播放时生成新的 canvasId，确保组件重新创建
			this.giftCanvasId = 'gift-svga-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
			
			// 先隐藏，然后设置新的数据
			this.currentGift.show = false;
			this.$nextTick(() => {
				this.currentGift = {
					type: giftData.type,
					image: giftData.image,
					text: displayText,
					svgaUrl: svgaUrl,
					show: !!svgaUrl // 只有有 SVGA URL 时才显示
				};
				console.log('主播端 Processed giftData:', giftData);
				console.log('主播端 currentGift set to:', this.currentGift);
				console.log('主播端 SVGA URL:', svgaUrl);
				console.log('主播端 New canvasId:', this.giftCanvasId);
				
				// 如果有 SVGA URL，显示容器，等待加载完成后自动播放
				if (svgaUrl) {
					// #ifndef APP-NVUE
					// 非 nvue 环境：SVGA 组件会在加载完成后触发 @loaded 事件，然后自动播放
					console.log('主播端 SVGA URL set, waiting for load');
					// 确保组件渲染
					this.$nextTick(() => {
						console.log('主播端 After nextTick, giftSvga ref:', this.$refs.giftSvga);
						console.log('主播端 Container should be visible:', this.currentGift.show && this.currentGift.svgaUrl);
					});
					// #endif
					// #ifdef APP-NVUE
					// nvue 环境：使用 tx-svgamodel 下载并播放
					console.log('主播端 nvue 环境：下载并播放 SVGA:', svgaUrl);
					this.$nextTick(() => {
						if (this._svgaModel && this.$refs.giftTxSvga) {
							// 本地 static 资源可直接播放；网络资源必须先 downloadUrls 得到 filePath
							if (typeof svgaUrl === 'string' && svgaUrl.indexOf('/static/') === 0) {
								try {
									const iconPath = plus.io.convertLocalFileSystemURL(svgaUrl);
									console.log('主播端 nvue 本地SVGA 直接播放:', iconPath);
									this.$refs.giftTxSvga.playAnimation({ "filePath": iconPath });
								} catch (e) {
									console.error('主播端 nvue 本地SVGA 播放失败:', e);
									setTimeout(() => this.onGiftAnimationEnd(), 2000);
								}
								return;
							}
							
							console.log('主播端 nvue downloadUrls start:', svgaUrl);
							this._svgaModel.downloadUrls(svgaUrl, result => {
								console.log('主播端 nvue SVGA downloadUrls result:', result);
								if (result && result.filePath) {
									console.log('主播端 nvue SVGA playAnimation filePath:', result.filePath);
									this.$refs.giftTxSvga.playAnimation({ "filePath": result.filePath });
								} else {
									console.error('主播端 nvue SVGA 下载失败（无 filePath）:', result);
									uni.showToast({ title: 'SVGA下载失败', icon: 'none' });
									// 下载失败，延迟隐藏
									setTimeout(() => {
										this.onGiftAnimationEnd();
									}, 2000);
								}
							});
						} else {
							console.error('主播端 nvue 环境：svgaModel 或 giftTxSvga ref 不可用');
							setTimeout(() => {
								this.onGiftAnimationEnd();
							}, 2000);
						}
					});
					// #endif
				} else {
					console.warn('主播端 No SVGA URL available for gift animation');
					// 如果没有 SVGA，延迟隐藏
					setTimeout(() => {
						this.onGiftAnimationEnd();
					}, 2000);
				}
			});
		},
		// SVGA 加载完成
		onGiftSvgaLoaded() {
			// #ifndef APP-NVUE
			console.log('主播端 SVGA loaded event triggered, starting animation');
			console.log('主播端 giftSvga ref:', this.$refs.giftSvga);
			// 加载完成后自动播放
			this.$nextTick(() => {
				if (this.$refs.giftSvga) {
					console.log('主播端 Calling startAnimation on giftSvga');
					// 使用 call 方法调用 startAnimation
					try {
						this.$refs.giftSvga.call('startAnimation');
						console.log('主播端 startAnimation called successfully');
					} catch (error) {
						console.error('主播端 Error calling startAnimation:', error);
					}
				} else {
					console.error('主播端 giftSvga ref is not available!');
				}
			});
			// #endif
			// #ifdef APP-NVUE
			// nvue 环境：SVGA 不支持，延迟隐藏
			setTimeout(() => {
				this.onGiftAnimationEnd();
			}, 3000);
			// #endif
		},
		// 打赏动画结束
		onGiftAnimationEnd() {
			console.log('主播端 Gift animation finished, 队列剩余长度:', this.giftQueue.length);
			// 清除超时定时器
			if (this.giftAnimationTimeout) {
				clearTimeout(this.giftAnimationTimeout);
				this.giftAnimationTimeout = null;
			}
			// 停止动画
			// #ifndef APP-NVUE
			if (this.$refs.giftSvga) {
				this.$refs.giftSvga.call('stopAnimation');
			}
			// #endif
			// #ifdef APP-NVUE
			if (this.$refs.giftTxSvga) {
				this.$refs.giftTxSvga.cancelAnimation();
			}
			// #endif
			// 重置礼物数据
			this.currentGift = {
				type: 'default',
				image: '',
				text: '',
				svgaUrl: '',
				show: false
			};
			
			// 显示列表：每播放完 1 次就 remainingPlays - 1；为 0 才淡出移除
			const finishedDisplayId = this.currentPlayingGiftId;
			if (finishedDisplayId) {
				const idx = this.displayGiftList.findIndex(x => x && x.id === finishedDisplayId);
				if (idx !== -1) {
					const item = this.displayGiftList[idx];
					const nextRemain = Math.max((Number(item.remainingPlays || 0) || 0) - 1, 0);
					this.$set(item, 'remainingPlays', nextRemain);
					this.$set(item, 'isAnimating', false);
					if (nextRemain <= 0) {
						this.$set(item, 'isFadingOut', true);
						setTimeout(() => {
							const removeIdx = this.displayGiftList.findIndex(x => x && x.id === finishedDisplayId);
							if (removeIdx !== -1) this.displayGiftList.splice(removeIdx, 1);
						}, 1000);
					}
				}
				this.currentPlayingGiftId = null;
			}
			
			// 标记播放结束
			this.isPlayingGift = false;
			// 延迟一小段时间后处理队列中的下一个礼物（确保动画完全结束）
			setTimeout(() => {
				console.log('主播端 准备处理下一个礼物，队列长度:', this.giftQueue.length);
				this.processGiftQueue();
				// 队列为空时清空残留显示项（兜底）
				if (this.giftQueue.length === 0 && !this.isPlayingGift) {
					this.clearAllDisplayItems();
				}
			}, 100);
		},
		// 队列为空时清空显示项（兜底）
		clearAllDisplayItems() {
			const itemsToFadeOut = this.displayGiftList.filter(item => item && !item.isFadingOut);
			itemsToFadeOut.forEach(item => {
				this.$set(item, 'isFadingOut', true);
				setTimeout(() => {
					const idx = this.displayGiftList.findIndex(x => x && x.id === item.id);
					if (idx !== -1) this.displayGiftList.splice(idx, 1);
				}, 1000);
			});
		},
		// 动态样式（nvue 也可用）
		getGiftItemStyle(item) {
			if (item && item.isFadingOut) {
				return { opacity: 0, transform: 'translateX(-50rpx)' };
			}
			return { opacity: 1, transform: 'translateX(0)' };
		},
		// #ifdef APP-NVUE
		// tx-svga 状态变化回调
		onGiftTxSvgaStateChange(e) {
			console.log('主播端 tx-svga state change:', e);
			const result = e.detail || e;
			// 如果动画播放完成，隐藏礼物
			if (result && (result.state === 'finished' || result.state === 'end')) {
				this.onGiftAnimationEnd();
			}
		},
		// #endif
	}
};
</script>

<style>
	@import '@/static/style/wanlnvue.css';

	.content {
		flex: 1;
		position: relative;
		background-color: #0c0d2b;
	}
		
	/* 直播播放	 */
	.wanlive-pusher {
		position: fixed;
		z-index: 0;
		top: 0;
		right: 0;
		bottom: 0;
		left: 0;
		background: #0c0d2b;
	}
	
	/* 提示 */
	.wanlive-tip{
		position: fixed;
		z-index: 20;
		top: 0;
		right: 0;
		bottom: 0;
		left: 0;
	}
	
	/* 导航栏 */
	.wanlive-header {
		position: fixed;
		z-index: 30;
		right: 0;
		left: 0;
	}
	
	/* 公告栏 */
	.wanlive-bulletin {
		position: fixed;
		z-index: 40;
	}

	/* 滚动消息 */
	.wanlive-message {
		position: fixed;
		z-index: 50;
		right: 0;
		left: 0;
	}
	
	/* 发送消息 */
	.wanlive-control {
		position: fixed;
		z-index: 60;
		right: 0;
		left: 0;
	}
	
	/* SVGA 礼物动画容器 */
	.gift-svga-container {
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		z-index: 100;
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex-direction: column;
		align-items: center;
		justify-content: center;
		pointer-events: none;
	}
	
	/* 礼物文字覆盖层 */
	.gift-text-overlay {
		position: absolute;
		bottom: 200rpx;
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		align-items: center;
		justify-content: center;
		pointer-events: none;
	}
	
	.gift-text {
		color: #ffffff;
		font-size: 32rpx;
		font-weight: bold;
		text-shadow: 2rpx 2rpx 4rpx rgba(0, 0, 0, 0.8);
		/* #ifndef APP-NVUE */
		display: block;
		/* #endif */
	}
	
	/* #ifdef APP-NVUE */
	.gift-tx-svga {
		width: 750rpx;
		height: 100%;
	}
	/* #endif */
	
	/* 礼物显示列表（主播端） */
	.gift-display-list {
		position: fixed;
		top: 200rpx;
		left: 20rpx;
		z-index: 25;
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex-direction: column;
		align-items: flex-start;
		justify-content: flex-start;
		pointer-events: none;
	}
	.gift-display-item {
		margin-bottom: 20rpx;
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		align-items: center;
		opacity: 1;
		transform: translateX(0);
		/* #ifndef APP-NVUE */
		transition: opacity 1s ease-out, transform 1s ease-out;
		/* #endif */
	}
	.gift-display-item-fade-out {
		opacity: 0;
		transform: translateX(-50rpx);
	}
	.gift-display-content {
		background-color: rgba(0, 0, 0, 0.6);
		border-radius: 50rpx;
		padding: 12rpx 24rpx;
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex-direction: row;
		align-items: center;
		justify-content: flex-start;
	}
	.gift-display-name {
		color: #ffffff;
		font-size: 24rpx;
		margin-right: 12rpx;
		max-width: 200rpx;
		/* #ifndef APP-NVUE */
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
		/* #endif */
	}
	.gift-display-image {
		width: 48rpx;
		height: 48rpx;
		margin-right: 8rpx;
	}
	.gift-display-count {
		color: #ffd700;
		font-size: 24rpx;
		font-weight: bold;
	}
</style>
