<template>
	<view class="ai-chat-page">
		<!-- 顶部区域 -->
		<view class="header-container">
			<!-- 宣传横幅 -->
			<view class="promo-banner">
				<view class="promo-content">
					<view class="promo-left">
						<text class="promo-title">慢生活守护健康</text>
						<text class="promo-sparkle">✦</text>
					</view>
					<text class="promo-subtitle">营养师专家入驻，在线答疑</text>
					</view>
					<view class="promo-right">
						<view class="tts-toggle-btn" @click="toggleTTS" :class="{ active: ttsEnabled }">
							<text class="tts-toggle-icon">🔊</text>
							<!--<text class="tts-toggle-text">{{ ttsEnabled ? '播报开' : '播报关' }}</text>-->
						</view>
						<view class="clear-btn" @click="clearChat">
							<text class="clear-icon">🗑️</text>
						</view>
						<image class="promo-avatar" src="https://uthink2025.oss-cn-shanghai.aliyuncs.com//crmebimage/public/content/2026/01/11/afcaba68d00b4fccaa49ad2a42c78e7fkk03hqv5vl.png" mode="aspectFit"></image>
					</view>
				</view>
		</view>
		
		<!-- 聊天消息列表 -->
		<scroll-view 
			class="chat-container" 
			scroll-y 
			:scroll-top="scrollTop"
			:scroll-with-animation="true"
		>
			<view class="message-list">
				<!-- AI欢迎消息 -->
				<view class="message-item ai-message">
					<view class="message-avatar">
						<text class="avatar-icon">🤖</text>
					</view>
					<view class="message-content">
						<view class="message-header">
							<text class="message-sender">AI营养师</text>
						</view>
						<view class="message-bubble ai-bubble">
							<text class="message-text">{{ welcomeMessage }}</text>
						</view>
					</view>
					</view>

				<!-- 消息列表 -->
				<view 
					v-for="(msg, index) in messageList" 
					:key="index"
					:class="['message-item', msg.role === 'user' ? 'user-message' : 'ai-message']"
				>
					<!-- AI头像 -->
					<view class="message-avatar" v-if="msg.role === 'ai'">
						<text class="avatar-icon">🤖</text>
					</view>
					
					<view class="message-content">
						<!-- AI发送者名称 -->
						<view class="message-header" v-if="msg.role === 'ai'">
							<text class="message-sender">AI营养师</text>
						</view>
						
					<!-- 消息气泡 -->
					<view :class="['message-bubble', msg.role === 'user' ? 'user-bubble' : 'ai-bubble']">
					<!-- AI 消息 loading 占位（等待回复时显示打字动画）-->
					<view v-if="msg.role === 'ai' && msg.loading" class="typing-indicator">
						<view class="typing-dot"></view>
						<view class="typing-dot"></view>
						<view class="typing-dot"></view>
					</view>
					<text v-else-if="msg.type !== 'image'" class="message-text">{{ msg.content }}<text v-if="msg.streaming" class="streaming-cursor">|</text></text>
						<image
							v-else
							:src="msg.imageUrl"
							mode="widthFix"
							class="message-image"
							@click="previewImage(msg.imageUrl)"
						></image>
					</view>
					<!-- AI 回复操作按钮组 -->
					<view
						v-if="msg.role === 'ai' && !msg.loading && !msg.streaming && msg.content && msg.type !== 'image'"
						class="msg-actions"
					>
						<!-- 复制 -->
						<view class="action-btn" @click="copyMessage(index)">
							<text class="action-icon">📋</text>
						</view>
						<!-- 重新生成（仅最后一条AI消息显示） -->
						<view class="action-btn" v-if="isLastAiMessage(index)" @click="regenerateMessage(index)">
							<text class="action-icon">🔄</text>
						</view>
						<!-- 语音朗读 -->
						<view class="action-btn" @click="ttsPlayingIndex === index ? stopTTS() : playTTS(index)">
							<text class="action-icon">{{ ttsPlayingIndex === index ? '⏹' : '▶' }}</text>
						</view>
						<!-- 删除 -->
						<view class="action-btn" @click="deleteMessage(index)">
							<text class="action-icon">🗑️</text>
						</view>
					</view>
				</view>
				</view>

				<!-- 加载中提示（仅在没有流式占位消息时显示） -->
				<view v-if="isLoading && !messageList.some(m => m.loading || m.streaming)" class="message-item ai-message">
					<view class="message-avatar">
						<text class="avatar-icon">🤖</text>
					</view>
					<view class="message-content">
						<view class="message-header">
							<text class="message-sender">AI营养师</text>
						</view>
						<view class="message-bubble ai-bubble">
							<view class="typing-indicator">
								<view class="typing-dot"></view>
								<view class="typing-dot"></view>
								<view class="typing-dot"></view>
							</view>
						</view>
					</view>
					</view>
				</view>
		</scroll-view>

		<!-- 快捷问题区域 -->
		<view class="quick-questions">
			<view class="quick-btn" @click="sendQuickQuestion('透析患者可以喝牛奶吗？')">
				<text>透析患者可以喝牛奶吗？</text>
				</view>
			<view class="quick-btn" @click="sendQuickQuestion('什么食物含磷比较低？')">
				<text>什么食物含磷比较低？</text>
				</view>
		</view>

		<!-- 输入区域 -->
		<view class="input-container">
			<!-- 图片预览区域 -->
			<view class="image-preview-area" v-if="pendingImages.length > 0">
				<view class="preview-item" v-for="(img, index) in pendingImages" :key="index">
					<image :src="img.path" mode="aspectFill" class="preview-img"></image>
					<view class="delete-btn" @click="removeImage(index)">
						<text class="delete-icon">×</text>
					</view>
					</view>
				<!-- 继续上传按钮 -->
				<view class="preview-item add-btn" @click="chooseImage" v-if="pendingImages.length < 3">
					<text class="add-icon">+</text>
					</view>
				</view>

			<view class="input-wrapper">
				<!-- 照片上传按钮 -->
				<view class="action-btn" @click="chooseImage">
					<image class="action-icon-svg" src="/static/images/icon-camera.svg" mode="aspectFit"></image>
					</view>
				
				<!-- 语音切换按钮 -->
				<view class="action-btn" @click="toggleVoiceMode">
					<image v-if="!isVoiceMode" class="action-icon-svg mic" src="/static/images/icon-mic.svg" mode="aspectFit"></image>
					<text v-else class="action-icon">⌨️</text>
					</view>
				
				<!-- 文本输入框 -->
				<input 
					v-if="!isVoiceMode"
					class="chat-input" 
					v-model="inputText" 
					placeholder="输入您的问题..."
					placeholder-style="color: #9fa5c0"
					confirm-type="send"
					@confirm="sendMessage"
					:focus="inputFocus"
					@focus="onInputFocus"
					@blur="onInputBlur"
				/>
				
				<!-- 语音按住按钮 -->
				<view 
					v-else 
					class="voice-hold-btn" 
					:class="{ recording: isRecording }"
					@touchstart="startRecord" 
					@touchend="stopRecord"
				>
					<text>{{ isRecording ? '松开 结束' : '按住 说话' }}</text>
					</view>
				
				<!-- 发送按钮 -->
				<view 
					class="send-btn" 
					:class="{ active: inputText.trim().length > 0 || pendingImages.length > 0 }"
					@click="sendMessage"
				>
					<image 
						v-if="inputText.trim().length > 0 || pendingImages.length > 0" 
						class="send-icon-svg" 
						src="/static/images/icon-send-active.svg" 
						mode="aspectFit"
					></image>
					<image 
						v-else 
						class="send-icon-svg" 
						src="/static/images/icon-send.svg" 
						mode="aspectFit"
					></image>
					</view>
				</view>
		</view>
	</view>
</template>

<script>
import api from '@/api/models-api.js';
import { mapGetters } from 'vuex';

export default {
	computed: {
		...mapGetters(['userInfo','uid'])
	},
	data() {
		return {
			botId: '7591133240535449654',
			conversationId: '',
			scrollTop: 0,
			lastScrollTop: 0, // 用于动态滚动
			inputText: '',
			inputFocus: false,
			isLoading: false,
			isVoiceMode: false,
			isRecording: false,
			recorderManager: null,
			recordDuration: 0,
			recordTimer: null,
			pendingImages: [], // 待发送图片
			welcomeMessage: `👋您好！我是您的AI营养师助手。

我可以帮您：
• 解答饮食疑问
• 评估食物适宜性
• 提供烹饪建议
• 解读检验报告

有什么想问的吗？`,
			messageList: [],
			// TTS
			ttsEnabled: false,
			ttsPlaying: false,
			ttsPlayingIndex: -1,
			innerAudioContext: null
		}
	},
	onLoad() {
		// 页面加载时滚动到底部
		this.$nextTick(() => {
			this.scrollToBottom()
		});
		this.initRecorder();
		this.initAudioContext();
	},
	onUnload() {
		this.stopRecordTimer();
		if (this.isRecording && this.recorderManager) {
			this.recorderManager.stop();
		}
		if (this._streamCtrl) {
			this._streamCtrl.abort();
			this._streamCtrl = null;
		}
		if (this.innerAudioContext) {
			this.innerAudioContext.destroy();
			this.innerAudioContext = null;
		}
	},
	methods: {
		// 初始化录音管理器
		initRecorder() {
			// #ifdef MP-WEIXIN || APP-PLUS
			this.recorderManager = uni.getRecorderManager();
			
			this.recorderManager.onStart(() => {
				console.log('录音开始');
				this.recordDuration = 0;
				this.startRecordTimer();
			});
			
			this.recorderManager.onStop((res) => {
				console.log('录音结束', res);
				this.stopRecordTimer();
				
				if (res.duration < 1000) {
					uni.showToast({
						title: '录音时间太短',
						icon: 'none'
					});
					return;
				}
				
				this.handleRecordResult(res);
			});
			
			this.recorderManager.onError((err) => {
				console.error('录音错误:', err);
				this.isRecording = false;
				this.stopRecordTimer();
				uni.showToast({
					title: '录音失败，请重试',
					icon: 'none'
				});
			});
			// #endif
		},
		
		startRecordTimer() {
			this.recordTimer = setInterval(() => {
				this.recordDuration++;
				if (this.recordDuration >= 60) {
					this.stopRecord();
				}
			}, 1000);
		},
		
		stopRecordTimer() {
			if (this.recordTimer) {
				clearInterval(this.recordTimer);
				this.recordTimer = null;
			}
			this.recordDuration = 0;
		},
		
		toggleVoiceMode() {
			this.isVoiceMode = !this.isVoiceMode;
			if (this.isVoiceMode) {
				// 隐藏键盘
				uni.hideKeyboard();
			} else {
				// 聚焦输入框
				this.$nextTick(() => {
					this.inputFocus = true;
				});
			}
		},
		
		startRecord() {
			// #ifdef H5
			uni.showModal({
				title: '提示',
				content: 'H5环境暂不支持语音输入，请使用小程序或APP',
				showCancel: false
			});
			return;
			// #endif
			
			// #ifdef MP-WEIXIN || APP-PLUS
			if (!this.recorderManager) {
				uni.showToast({
					title: '录音功能初始化失败',
					icon: 'none'
				});
				return;
			}
			
			uni.authorize({
				scope: 'scope.record',
				success: () => {
					this.isRecording = true;
					this.recorderManager.start({
						duration: 60000,
						sampleRate: 16000,
						numberOfChannels: 1,
						encodeBitRate: 96000,
						format: 'mp3'
					});
				},
				fail: () => {
					uni.showModal({
						title: '需要录音权限',
						content: '请在设置中开启录音权限',
						confirmText: '去设置',
						success: (res) => {
							if (res.confirm) {
								uni.openSetting();
							}
						}
					});
				}
			});
			// #endif
		},
		
		stopRecord() {
			if (!this.isRecording) return;
			this.isRecording = false;
			// #ifdef MP-WEIXIN || APP-PLUS
			if (this.recorderManager) {
				this.recorderManager.stop();
			}
			// #endif
			uni.showToast({
				title: '正在识别...',
				icon: 'loading',
				duration: 2000
			});
		},
		
		async handleRecordResult(res) {
			try {
				const uploadRes = await api.uploadFile(res.tempFilePath, {
					model: 'audio',
					pid: '8'
				});
				
				if (!uploadRes || uploadRes.code !== 200) {
					throw new Error('录音文件上传失败');
				}
				
				const audioUrl = uploadRes.data.fullUrl;
				const recognizedText = await this.recognizeSpeech(audioUrl);
				
				if (recognizedText) {
					this.inputText = this.inputText ? (this.inputText + ' ' + recognizedText) : recognizedText;
					
					// Auto-switch back to text mode so user can see the recognized text
					if (this.isVoiceMode) {
						this.isVoiceMode = false;
						this.$nextTick(() => {
							this.inputFocus = true;
						});
					}
				} else {
					uni.showToast({
						title: '未能识别出语音内容',
						icon: 'none'
					});
				}
			} catch (error) {
				console.error('语音识别流程失败:', error);
				uni.showToast({
					title: '识别失败，请重试',
					icon: 'none'
				});
			}
		},
		
		async recognizeSpeech(audioUrl) {
			try {
				const createTaskRes = await api.createAsrTask({
					url: audioUrl,
					engineModelType: '16k_zh',
					channelNum: 1,
					resTextFormat: 0,
					sourceType: 0
				});
				
				if (!createTaskRes || createTaskRes.code !== 200) {
					throw new Error('创建识别任务失败');
				}
				
				const taskId = createTaskRes.data.taskId;
				const result = await this.pollAsrResult(taskId);
				return this.parseAsrResult(result);
			} catch (error) {
				console.error('语音识别请求失败:', error);
				throw error;
			}
		},
		
		async pollAsrResult(taskId, maxAttempts = 30, interval = 2000) {
			let attempts = 0;
			while (attempts < maxAttempts) {
				attempts++;
				try {
					const statusRes = await api.queryAsrStatus(taskId);
					if (!statusRes || statusRes.code !== 200) throw new Error('查询识别状态失败');
					
					const status = statusRes.data.status;
					if (status === 2) return statusRes.data;
					else if (status === 3) throw new Error(statusRes.data.errorMsg || '识别失败');
					
					await new Promise(resolve => setTimeout(resolve, interval));
				} catch (error) {
					console.error('查询识别状态出错:', error);
					if (attempts >= maxAttempts) throw error;
					await new Promise(resolve => setTimeout(resolve, interval));
				}
			}
			throw new Error('识别超时，请重试');
		},
		
		parseAsrResult(data) {
			if (!data || !data.result) return '';
			let text = data.result;
			text = text.replace(/\[\d+:\d+\.\d+,\d+:\d+\.\d+\]\s*/g, '');
			text = text.replace(/\n+/g, ' ').trim();
			return text.replace(/\s+/g, ' ');
		},
		
		chooseImage() {
			uni.chooseImage({
				count: 3 - this.pendingImages.length, // 限制最多选择3张
				sizeType: ['compressed'],
				sourceType: ['album', 'camera'],
				success: async (res) => {
					uni.showLoading({ title: '处理中...' });
					try {
						// 遍历处理所有选择的图片
						for (const filePath of res.tempFilePaths) {
							// 使用新的 Coze 上传接口
							const uploadRes = await api.cozeUploadFile(filePath);
							
							if (uploadRes && (uploadRes.code === 0 || uploadRes.code === 200)) {
								const fileInfo = uploadRes.data;
								// 添加到待发送列表
								this.pendingImages.push({
									path: filePath,
									fileInfo: fileInfo
								});
							} else {
								throw new Error('上传失败: ' + (uploadRes?.msg || uploadRes?.message || '服务器返回异常'));
							}
						}
					} catch (error) {
						console.error('上传图片失败:', error);
						uni.showToast({ 
							title: error.message || '上传图片失败', 
							icon: 'none',
							duration: 3000
						});
					} finally {
						uni.hideLoading();
					}
				}
			});
		},
		
		removeImage(index) {
			this.pendingImages.splice(index, 1);
		},
		
		/** 将本地图片转为 data URL，用于 KieAI 多模态合并请求 */
		readFileAsDataUrl(filePath) {
			return new Promise((resolve, reject) => {
				const fs = uni.getFileSystemManager();
				const isJpeg = /\.(jpe?g|jfif)$/i.test(filePath);
				fs.readFile({
					filePath,
					encoding: 'base64',
					success: (res) => {
						const mime = isJpeg ? 'image/jpeg' : 'image/png';
						resolve(`data:${mime};base64,${res.data}`);
					},
					fail: reject
				});
			});
		},
		
		sendImageMessage(imageUrl, fileInfo) {
			// 该方法已废弃，逻辑合并到 sendMessage
		},
		
		previewImage(url) {
			uni.previewImage({
				urls: [url]
			});
		},

		sendQuickQuestion(question) {
			this.inputText = question;
			this.$nextTick(() => {
				this.sendMessage();
			});
		},
		// ---------- 消息操作方法（复制/删除/重新生成） ----------

		copyMessage(index) {
			const msg = this.messageList[index]
			if (!msg || !msg.content) return
			uni.setClipboardData({
				data: msg.content,
				success: () => {
					uni.showToast({ title: '已复制', icon: 'success' })
				}
			})
		},

		deleteMessage(index) {
			uni.showModal({
				title: '提示',
				content: '确定删除这条消息吗？',
				success: (res) => {
					if (res.confirm) {
						this.messageList.splice(index, 1)
						this.messageList = [...this.messageList]
					}
				}
			})
		},

		regenerateMessage(index) {
			// 找到该 AI 消息对应的上一条用户消息
			let userMsgIndex = index - 1
			while (userMsgIndex >= 0 && this.messageList[userMsgIndex].role !== 'user') {
				userMsgIndex--
			}
			if (userMsgIndex < 0) return
			const userMsg = this.messageList[userMsgIndex]
			// 移除当前 AI 回复
			this.messageList.splice(index, 1)
			this.messageList = [...this.messageList]
			// 重新发送
			this.sendToAI(userMsg.content, userMsg.type || 'text')
		},

		isLastAiMessage(index) {
			for (let i = this.messageList.length - 1; i >= 0; i--) {
				if (this.messageList[i].role === 'ai' && !this.messageList[i].loading) {
					return i === index
				}
			}
			return false
		},

		clearChat() {
			uni.showModal({
				title: '提示',
				content: '确定要清空对话吗？',
				success: (res) => {
					if (res.confirm) {
						if (this._streamCtrl) {
							this._streamCtrl.abort();
							this._streamCtrl = null;
						}
						this.isLoading = false;
						this.messageList = [];
						this.conversationId = '';
					}
				}
			})
		},
		showCommonQuestions() {
			uni.showToast({
				title: '常见问题功能开发中',
				icon: 'none'
			})
		},
		onInputFocus() {
			this.inputFocus = true
			// 延迟滚动到底部，等待键盘弹出
			setTimeout(() => {
				this.scrollToBottom()
			}, 300)
		},
		onInputBlur() {
			this.inputFocus = false
		},
		async sendMessage() {
			if (!this.inputText.trim() && this.pendingImages.length === 0) {
				return
			}
			
			const imagesToSend = [...this.pendingImages];
			const text = this.inputText.trim();
			this.pendingImages = [];
			this.inputText = '';
			
			// 先展示用户消息到界面
			for (const img of imagesToSend) {
				this.messageList.push({
					role: 'user',
					type: 'image',
					imageUrl: img.path,
					content: '[图片]'
				});
			}
			if (text) {
				this.messageList.push({ role: 'user', content: text, type: 'text' });
			}
			this.scrollToBottom();
			
			// 合并为一次多模态请求：图片(base64) + 文字，统一走 KieAI Gemini
			const contentParts = [];
			try {
				for (const img of imagesToSend) {
					const dataUrl = await this.readFileAsDataUrl(img.path);
					contentParts.push({ type: 'image_url', image_url: { url: dataUrl } });
				}
				if (text) {
					contentParts.push({ type: 'text', text });
				} else if (contentParts.length > 0) {
					contentParts.push({ type: 'text', text: '请描述或分析这张图片' });
				}
			} catch (e) {
				console.error('读取图片失败:', e);
				this.messageList.push({ role: 'ai', content: '读取图片失败，请重试。' });
				this.scrollToBottom();
				return;
			}
			
			if (contentParts.length === 0) return;
			// 仅文字时走纯文字接口；否则走多模态（图+文）一次请求
			if (contentParts.length === 1 && contentParts[0].type === 'text') {
				await this.sendToAI(contentParts[0].text, 'text');
			} else {
				await this.sendToAI(contentParts, 'multimodal');
			}
			this.scrollToBottom();
		},
		
		/**
		 * CommonResult.data 应为 OpenAI 形态；若网关多包一层 data，则取内层含 choices/candidates 的对象。
		 * 展示内容仍只来自模型字段 choices[0].message.content（或经后端规范后的同路径）。
		 */
		getGeminiPayload(data) {
			if (!data || typeof data !== 'object') return null;
			if (Array.isArray(data.choices) && data.choices.length > 0) return data;
			if (Array.isArray(data.candidates) && data.candidates.length > 0) return data;
			const inner = data.data;
			if (inner && typeof inner === 'object') {
				if (Array.isArray(inner.choices) && inner.choices.length > 0) return inner;
				if (Array.isArray(inner.candidates) && inner.candidates.length > 0) return inner;
			}
			return data;
		},

		/** BUG-005: 从 KieAI Gemini 响应 data.choices[0].message.content 提取展示文本（支持 string / parts 数组 / { parts } / { text } / { content }） */
		extractReplyContent(content) {
			if (content == null) return '';
			if (typeof content === 'number' || typeof content === 'boolean') return String(content);
			if (typeof content === 'string') return content;
			if (Array.isArray(content)) {
				return content.map(part => (part && part.text) ? part.text : '').filter(Boolean).join('');
			}
			if (typeof content === 'object') {
				if (Array.isArray(content.parts)) {
					return content.parts.map(part => (part && part.text) ? part.text : '').filter(Boolean).join('');
				}
				if (typeof content.text === 'string') return content.text;
				if (typeof content.content === 'string') return content.content;
			}
			return '';
		},

		/** 工具方法：sleep ms 毫秒 */
		sleep(ms) {
			return new Promise(resolve => setTimeout(resolve, ms));
		},

		buildGeminiMessages(content, type) {
			if (type === 'text') {
				return [{ role: 'user', content: typeof content === 'string' ? content : String(content) }];
			}
			const parts = Array.isArray(content) ? content : [{ type: 'text', text: String(content) }];
			return [{ role: 'user', content: parts }];
		},

		/**
		 * BUG-005：主路径为 response.data.choices[0].message.content（models-api 已尽量规范 data 形态）。
		 * 兼容上游 candidates、或根级即 completion 等变体；仅使用接口返回字段，
		 * 成功路径不使用 api/tool.js 的 getAIResponse 或本地关键词话术。
		 */
		getGeminiReplyFromResponse(response) {
			if (!response || typeof response !== 'object') return '';
			const looksLikeCompletion = (o) =>
				o && typeof o === 'object' &&
				(Array.isArray(o.choices) || Array.isArray(o.candidates));
			let data = response.data;
			if (typeof data === 'string') {
				try {
					data = JSON.parse(data);
				} catch (e) {
					return '';
				}
			}
			// 主路径：OpenAI 形态 choices[0].message.content（与需求 data.choices[0].message.content 一致）
			if (data && typeof data === 'object' && Array.isArray(data.choices) && data.choices.length > 0) {
				const choice0 = data.choices[0];
				const msg = choice0 && choice0.message;
				if (msg && typeof msg === 'object') {
					const fromMsg = this.extractReplyContent(msg.content);
					if (fromMsg.trim()) return fromMsg;
				}
				const delta = choice0 && choice0.delta;
				if (delta && typeof delta === 'object') {
					const fromDelta = this.extractReplyContent(delta.content);
					if (fromDelta.trim()) return fromDelta;
				}
				if (choice0 && typeof choice0.text === 'string' && choice0.text.trim()) {
					return choice0.text;
				}
			}
			if (!looksLikeCompletion(data) && looksLikeCompletion(response)) {
				data = response;
			}
			if (!data || typeof data !== 'object') return '';
			const payload = this.getGeminiPayload(data);
			if (!payload || typeof payload !== 'object') return '';
			const choices = payload.choices;
			if (Array.isArray(choices) && choices.length > 0) {
				const choice0 = choices[0];
				const msg = choice0 && choice0.message;
				if (msg && typeof msg === 'object') {
					const fromMsg = this.extractReplyContent(msg.content);
					if (fromMsg.trim()) return fromMsg;
				}
				const delta = choice0 && choice0.delta;
				if (delta && typeof delta === 'object') {
					const fromDelta = this.extractReplyContent(delta.content);
					if (fromDelta.trim()) return fromDelta;
				}
				if (choice0 && typeof choice0.text === 'string' && choice0.text.trim()) {
					return choice0.text;
				}
			}
			const cands = payload.candidates;
			if (Array.isArray(cands) && cands.length > 0) {
				const c0 = cands[0];
				if (c0 && typeof c0 === 'object') {
					const fromCand = this.extractReplyContent(c0.content);
					if (fromCand.trim()) return fromCand;
					if (typeof c0.text === 'string' && c0.text.trim()) return c0.text;
				}
			}
			return '';
		},

		async sendToAI(content, type) {
			this.isLoading = true;
			const aiMsg = { role: 'ai', content: '', loading: true, streaming: false };
			this.messageList.push(aiMsg);
			this.scrollToBottom();

			const messages = this.buildGeminiMessages(content, type);

			// 优先尝试流式输出以改善响应速度感知
			try {
				await new Promise((resolve, reject) => {
					const ctrl = api.kieaiGeminiChatStream({ messages })
					this._streamCtrl = ctrl

					// 收到第一个 chunk 后切换为 streaming 状态
					ctrl.onMessage((deltaText) => {
						if (aiMsg.loading) {
							aiMsg.loading = false
							aiMsg.streaming = true
						}
						aiMsg.content += deltaText
						this.messageList = [...this.messageList]
						this.scrollToBottom()
					})

					ctrl.onError((err) => {
						console.warn('[sendToAI] 流式请求失败，降级为非流式:', err)
						this._streamCtrl = null
						reject(err)
					})

					ctrl.onComplete(() => {
						this._streamCtrl = null
						resolve()
					})
				})

				// 流式完成，检查内容
				if (!aiMsg.content.trim()) {
					aiMsg.content = '模型未返回有效内容，请稍后重试。'
				}
			} catch (streamError) {
				// 流式失败，降级为非流式请求
				try {
					const response = await api.kieaiGeminiChat({
						messages,
						stream: false
					});
					const reply = this.getGeminiReplyFromResponse(response);
					aiMsg.content = reply.trim() ? reply : '模型未返回有效内容，请稍后重试。';
				} catch (error) {
					console.error('KieAI Gemini 对话失败:', error);
					const errText = error && error.message ? String(error.message) : '';
					aiMsg.content = errText || '抱歉，处理您的请求时出现错误，请稍后再试。';
				}
			} finally {
				aiMsg.loading = false;
				aiMsg.streaming = false;
				this.isLoading = false;
				this.messageList = [...this.messageList];
				this.scrollToBottom();
				if (this.ttsEnabled && aiMsg.content) {
					const aiIdx = this.messageList.indexOf(aiMsg);
					if (aiIdx !== -1) {
						this.$nextTick(() => this.playTTS(aiIdx));
					}
				}
			}
		},

		// ---------- TTS 方法 ----------

		initAudioContext() {
			// #ifdef MP-WEIXIN || APP-PLUS
			const ctx = uni.createInnerAudioContext()
			ctx.onEnded(() => {
				this.ttsPlaying = false
				this.ttsPlayingIndex = -1
			})
			ctx.onError((e) => {
				console.error('[TTS] 播放出错', e)
				this.ttsPlaying = false
				this.ttsPlayingIndex = -1
			})
			this.innerAudioContext = ctx
			// #endif
		},

		toggleTTS() {
			this.ttsEnabled = !this.ttsEnabled
			if (!this.ttsEnabled && this.ttsPlaying) {
				this.stopTTS()
			}
		},

		async playTTS(index) {
			const msg = this.messageList[index]
			if (!msg || !msg.content) return

			if (this.ttsPlaying) {
				this.stopTTS()
			}

			try {
				const tempPath = await api.cozeTextToSpeech({ input: msg.content })
				if (!this.innerAudioContext) {
					console.warn('[TTS] innerAudioContext 未初始化')
					return
				}
				this.ttsPlayingIndex = index
				this.ttsPlaying = true
				this.innerAudioContext.src = tempPath
				this.innerAudioContext.play()
			} catch (e) {
				console.error('[TTS] 合成失败', e)
				uni.showToast({ title: '语音合成失败', icon: 'none' })
				this.ttsPlaying = false
				this.ttsPlayingIndex = -1
			}
		},

		stopTTS() {
			if (this.innerAudioContext) {
				this.innerAudioContext.stop()
			}
			this.ttsPlaying = false
			this.ttsPlayingIndex = -1
		},

		// ---------- 滚动 ----------

		scrollToBottom() {
			this.$nextTick(() => {
				// 动态切换 scrollTop 值以触发滚动更新
				this.lastScrollTop = this.lastScrollTop === 99998 ? 99999 : 99998
				this.scrollTop = this.lastScrollTop
			})
		}
	}
}
</script>

<style lang="scss" scoped>
.ai-chat-page {
	height: 100vh;
	display: flex;
	flex-direction: column;
	background-color: #f4f5f7;
}

/* 顶部区域 */
.header-container {
	background: #ffffff;
	box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1), 0 2rpx 4rpx rgba(0, 0, 0, 0.06);
	flex-shrink: 0;
}

.nav-bar {
	display: flex;
	align-items: center;
	justify-content: space-between;
	height: 98rpx;
	padding: 0 24rpx;
	border-bottom: 1rpx solid #d0dbea;
}

.nav-back {
	width: 56rpx;
	height: 56rpx;
	display: flex;
	align-items: center;
	justify-content: center;
	
	.back-icon {
		font-size: 40rpx;
		color: #2e3e5c;
		font-weight: 300;
	}
	
	&:active {
		opacity: 0.6;
	}
}

.nav-title {
	font-size: 32rpx;
	color: #2e3e5c;
	font-weight: 400;
	letter-spacing: -0.5rpx;
}

.nav-placeholder {
	width: 56rpx;
}

/* 宣传横幅 */
.promo-banner {
	height: 192rpx;
	background: linear-gradient(135deg, #fdfaff 0%, #fcf5ff 50%, #ffffff 100%);
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 0 32rpx;
	position: relative;
	overflow: hidden;
}

.promo-content {
	display: flex;
	flex-direction: column;
	gap: 8rpx;
	z-index: 1;
}

.promo-left {
	display: flex;
	align-items: center;
	gap: 12rpx;
}

.promo-title {
	font-size: 36rpx;
	font-weight: 600;
	color: #ff6b35;
	letter-spacing: 1rpx;
}

.promo-sparkle {
	font-size: 28rpx;
	color: #ff6b35;
}

.promo-subtitle {
	font-size: 28rpx;
	color: #5c6bc0;
	font-weight: 400;
}

.promo-right {
	display: flex;
	align-items: center;
	gap: 20rpx;
	z-index: 1;
}

.clear-btn {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	padding: 16rpx;
	background: rgba(255, 255, 255, 0.9);
	border-radius: 16rpx;
	box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
	transition: all 0.3s ease;
}

.clear-btn:active {
	transform: scale(0.95);
	background: rgba(255, 255, 255, 1);
	box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
}

.clear-icon {
	font-size: 32rpx;
	margin-bottom: 4rpx;
}

.clear-text {
	font-size: 22rpx;
	color: #ff6b35;
	font-weight: 500;
}

.tts-toggle-btn {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	padding: 16rpx;
	background: rgba(255, 255, 255, 0.9);
	border-radius: 16rpx;
	box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
	transition: all 0.3s ease;
	margin-right: 12rpx;

	&.active {
		background: rgba(76, 175, 80, 0.15);
		box-shadow: 0 2rpx 8rpx rgba(76, 175, 80, 0.3);
	}
}

.tts-toggle-btn:active {
	transform: scale(0.95);
}

.tts-toggle-icon {
	font-size: 32rpx;
	margin-bottom: 4rpx;
}

.tts-toggle-text {
	font-size: 20rpx;
	color: #4caf50;
	font-weight: 500;
}

/* 消息操作按钮组 */
.msg-actions {
	display: flex;
	gap: 16rpx;
	margin-top: 8rpx;
	align-items: center;
}

.action-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 48rpx;
	height: 48rpx;
	background: rgba(76, 175, 80, 0.12);
	border-radius: 50%;
	cursor: pointer;
}

.action-btn:active {
	transform: scale(0.9);
}

.action-icon {
	font-size: 24rpx;
	color: #4caf50;
}

.tts-play-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	margin-top: 8rpx;
	width: 48rpx;
	height: 48rpx;
	background: rgba(76, 175, 80, 0.12);
	border-radius: 50%;
	cursor: pointer;
}

.tts-play-btn:active {
	transform: scale(0.9);
}

.tts-play-icon {
	font-size: 24rpx;
	color: #4caf50;
}

.promo-avatar {
	width: 180rpx;
	height: 180rpx;
}

/* 聊天容器 */
.chat-container {
	flex: 1;
	overflow-y: auto;
}

.message-list {
	padding: 32rpx;
	display: flex;
	flex-direction: column;
	gap: 32rpx;
}

.message-item {
	display: flex;
	gap: 16rpx;
	
	&.ai-message {
		justify-content: flex-start;
	}
	
	&.user-message {
		justify-content: flex-end;
	}
}

.message-avatar {
	width: 64rpx;
	height: 64rpx;
	border-radius: 50%;
	background: linear-gradient(180deg, #4facfe 0%, #00f2fe 100%);
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	
	.avatar-icon {
		font-size: 28rpx;
	}
}

.message-content {
	flex: 1;
	max-width: 75%;
	display: flex;
	flex-direction: column;
	gap: 8rpx;
}

.message-header {
	display: flex;
	align-items: center;
	padding-left: 16rpx;
	
	.message-sender {
		font-size: 24rpx;
		color: #9fa5c0;
	}
}

.message-bubble {
	border-radius: 32rpx;
	padding: 24rpx 32rpx;
	word-wrap: break-word;
	transition: all 0.3s ease;
	
	&.ai-bubble {
		background: #ffffff;
		border-top-left-radius: 8rpx;
		box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1), 0 2rpx 4rpx rgba(0, 0, 0, 0.06);
		
		.message-text {
			color: #3e5481;
		}
	}
	
	&.user-bubble {
		background: linear-gradient(135deg, #ff8c52 0%, #ff6b35 100%);
		border-top-right-radius: 8rpx;
		box-shadow: 0 4rpx 12rpx rgba(255, 107, 53, 0.3);

		.message-text {
			color: #ffffff;
		}
	}
	
	.message-text {
		font-size: 28rpx;
		line-height: 1.65;
		white-space: pre-wrap;
		letter-spacing: -0.2rpx;
	}
}

/* 流式输出闪烁光标 */
.streaming-cursor {
	animation: blink 0.8s step-end infinite;
	color: #4facfe;
	font-weight: 600;
}

@keyframes blink {
	0%, 100% { opacity: 1; }
	50% { opacity: 0; }
}

/* 打字指示器 */
.typing-indicator {
	display: flex;
	gap: 10rpx;
	align-items: center;
	padding: 8rpx 0;
}

.typing-dot {
	width: 14rpx;
	height: 14rpx;
	border-radius: 50%;
	background: #4facfe;
	animation: typing 1.4s infinite;
	
	&:nth-child(2) {
		animation-delay: 0.2s;
	}
	
	&:nth-child(3) {
		animation-delay: 0.4s;
	}
}

@keyframes typing {
	0%, 60%, 100% {
		transform: translateY(0);
		opacity: 0.4;
	}
	30% {
		transform: translateY(-10rpx);
		opacity: 1;
	}
}

/* 消息图片 */
.message-image {
	max-width: 400rpx;
	border-radius: 16rpx;
}

/* 快捷问题区域 */
.quick-questions {
	display: flex;
	gap: 16rpx;
	padding: 20rpx 32rpx;
	flex-shrink: 0;
}

.quick-btn {
	flex: 1;
	height: 84rpx;
	background: #ffffff;
	border: 1rpx solid #e5e7eb;
	border-radius: 24rpx;
	display: flex;
	align-items: center;
	justify-content: center;
	box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1), 0 2rpx 4rpx rgba(0, 0, 0, 0.06);
	
	text {
		font-size: 24rpx;
		color: #3e5481;
		letter-spacing: -0.2rpx;
	}
	
	&:active {
		background: #f4f5f7;
		transform: scale(0.98);
	}
}

/* 图片预览区域 */
.image-preview-area {
	display: flex;
	gap: 20rpx;
	padding-bottom: 20rpx;
	overflow-x: auto;
	white-space: nowrap;
	
	.preview-item {
		position: relative;
		width: 120rpx;
		height: 120rpx;
		flex-shrink: 0;
		
		.preview-img {
			width: 100%;
			height: 100%;
			border-radius: 16rpx;
			background: #f5f5f5;
		}
		
		.delete-btn {
			position: absolute;
			top: -10rpx;
			right: -10rpx;
			width: 36rpx;
			height: 36rpx;
			background: rgba(0, 0, 0, 0.5);
			border-radius: 50%;
			display: flex;
			align-items: center;
			justify-content: center;
			z-index: 10;
			
			.delete-icon {
				color: #ffffff;
				font-size: 24rpx;
				line-height: 1;
				margin-top: -2rpx;
			}
		}
		
		&.add-btn {
			background: #f5f5f5;
			border-radius: 16rpx;
			display: flex;
			align-items: center;
			justify-content: center;
			border: 2rpx dashed #d0dbea;
			
			.add-icon {
				font-size: 48rpx;
				color: #9fa5c0;
				line-height: 1;
				margin-top: -4rpx;
			}
			
			&:active {
				background: #e0e0e0;
			}
		}
	}
}

/* 输入区域 */
.input-container {
	background: #f4f5f7;
	padding: 16rpx 32rpx;
	padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
	flex-shrink: 0;
}

.input-wrapper {
	display: flex;
	align-items: center;
	gap: 16rpx;
}

/* 操作按钮 */
.action-btn {
	width: 80rpx;
	height: 80rpx;
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	
	.action-icon {
		font-size: 44rpx;
	}
	
	.action-icon-svg {
		width: 48rpx;
		height: 48rpx;
		
		&.mic {
			width: 40rpx;
			height: 40rpx;
		}
	}
	
	&:active {
		opacity: 0.7;
	}
}

.nav-back-hover {
	opacity: 0.6;
}

/* 语音按住按钮 */
.voice-hold-btn {
	flex: 1;
	height: 88rpx;
	background: #ffffff;
	border: 1rpx solid #e5e7eb;
	border-radius: 100rpx;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 30rpx;
	color: #3e5481;
	font-weight: 500;
	transition: all 0.2s;
	box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1), 0 2rpx 4rpx rgba(0, 0, 0, 0.06);
	
	&.recording {
		background: linear-gradient(135deg, #ff8c52 0%, #ff6b35 100%);
		color: #ffffff;
		border-color: transparent;
		transform: scale(0.98);
	}
	
	&:active {
		background: #f4f5f7;
	}
}

.chat-input {
	flex: 1;
	height: 88rpx;
	background: #ffffff;
	border: 1rpx solid #e5e7eb;
	border-radius: 100rpx;
	padding: 0 96rpx 0 32rpx;
	font-size: 32rpx;
	color: #3e5481;
	box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1), 0 2rpx 4rpx rgba(0, 0, 0, 0.06);
}

.send-btn {
	width: 80rpx;
	height: 80rpx;
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
	background: transparent;
	
	&.active {
		background: rgba(255, 107, 53, 0.1);
		
		.send-icon-svg {
			transform: scale(1.1);
		}
		
		&:active {
			transform: scale(0.9);
			background: rgba(255, 107, 53, 0.2);
		}
	}
	
	.send-icon-svg {
		width: 40rpx;
		height: 40rpx;
		transition: all 0.3s;
	}
}
</style>
