<template>
	<view class="video-call-container" @touchend="handleGlobalTouchEnd" @touchcancel="handleGlobalTouchCancel">
		<!-- 顶部导航 -->
		<view class="header">
			
			<text class="header-title">视频通话</text>
			<view class="placeholder"></view>
		</view>

		<!-- 选择视频阶段 -->
		<view v-if="!callStarted" class="select-video-section">
			
			
			<!-- 加载状态 -->
			<view v-if="loading" class="loading-box">
				<text class="loading-text">⏳ 加载视频列表...</text>
			</view>

			<!-- 视频列表 -->
			<scroll-view v-else-if="videos.length > 0" scroll-y class="video-list">
				<view class="list-header">
					<text class="list-title">选择要通话的视频</text>
					<text class="list-count">共 {{ videos.length }} 个</text>
				</view>
				
				<view 
					v-for="video in videos" 
					:key="video.id" 
					class="video-item"
					@click="startVideoCall(video)"
				>
					<!-- 左侧封面 -->
					<view class="item-cover">
						<image 
							v-if="video.photo_url"
							:src="video.photo_url" 
							class="cover-img"
							mode="aspectFill"
						></image>
						<view v-else class="cover-placeholder">
							<text class="placeholder-icon">📹</text>
						</view>
						<!-- 隐藏的视频预加载组件 -->
						<video 
							v-if="video.edited_video_url || video.videoUrl || video.local_video_path || video.video_url || video.localVideoPath"
							:id="'preload-video-' + video.id"
							:src="video.edited_video_url || video.videoUrl || video.local_video_path || video.video_url || video.localVideoPath" 
							class="preload-video"
							:muted="true"
							:autoplay="false"
							:controls="false"
							:show-center-play-btn="false"
							:show-play-btn="false"
							:enable-progress-gesture="false"
							@loadedmetadata="onVideoPreloaded(video.id)"
						></video>
					</view>
					
					<!-- 中间信息 -->
					<view class="item-info">
						<view class="info-name-row">
							<text class="info-name">{{ video.name || '复活视频' }}</text>
							<!-- 预加载状态标识 -->
							<view v-if="videoPreloadStatus[video.id]" class="cache-badge">
								<text class="cache-badge-text completed">✓ 已就绪</text>
							</view>
							<!-- 缓存状态标识 -->
							<view v-else-if="cacheProgress[video.id]" class="cache-badge">
								<text v-if="cacheProgress[video.id].status === 'completed'" class="cache-badge-text completed">✓ 已缓存</text>
								<text v-else-if="cacheProgress[video.id].status === 'downloading'" class="cache-badge-text downloading">{{ cacheProgress[video.id].progress }}%</text>
							</view>
						</view>
						<text class="info-text">{{ video.text || '暂无描述' }}</text>
					</view>
					
					<!-- 右侧按钮 -->
					<view class="item-action">
						<view class="call-icon">
							<text class="icon-text">📞</text>
						</view>
					</view>
				</view>
			</scroll-view>

			<!-- 空状态 -->
			
		</view>

		<!-- 通话中阶段 -->
		<view v-else class="call-active-section">
			<!-- 接通中遮罩层 -->
			<view v-if="isConnecting" class="connecting-overlay">
				<!-- 背景模糊头像 -->
				<view class="connecting-bg">
					<image 
						v-if="selectedPhotoUrl"
						:src="selectedPhotoUrl" 
						class="bg-avatar"
						mode="aspectFill"
					></image>
				</view>
				
				<!-- 接通中内容 -->
				<view class="connecting-content">
					<!-- 头像 -->
					<view class="avatar-container">
						<image 
							v-if="selectedPhotoUrl"
							:src="selectedPhotoUrl" 
							class="avatar-img"
							mode="aspectFill"
						></image>
						<view v-else class="avatar-placeholder">📹</view>
					</view>
					
					<!-- 名称 -->
					<text class="connecting-name">{{ selectedVideoName }}</text>
					
					<!-- 状态文字 -->
					<view class="connecting-status">
						<view class="status-dots">
							<view class="dot dot-1"></view>
							<view class="dot dot-2"></view>
							<view class="dot dot-3"></view>
						</view>
						<text class="status-text">{{ connectingText }}</text>
					</view>
				</view>
			</view>
			
			<!-- 全屏视频背景 -->
			<view class="video-fullscreen" :class="{ 'video-hidden': isConnecting }">
				<video 
					id="callVideo"
					v-if="selectedVideoUrl"
					:src="localVideoUrl || selectedVideoUrl" 
					class="video-player"
					:loop="true"
					:muted="true"
					:autoplay="false"
					:controls="false"
					:show-center-play-btn="false"
					:show-play-btn="false"
					:show-fullscreen-btn="false"
					:show-progress="false"
					:enable-progress-gesture="false"
					preload="auto"
					object-fit="cover"
					@error="onVideoError"
					@loadeddata="onVideoLoaded"
					@loadedmetadata="onVideoMetadataLoaded"
					@play="onVideoPlay"
					@pause="onVideoPause"
				></video>
				<view v-else class="video-placeholder">
					<text class="placeholder-icon">📹</text>
					<text class="placeholder-text">视频加载中...</text>
				</view>

				<!-- AI生成提示标签 -->
				<view class="ai-tag">
					<text class="ai-tag-text">AI生成</text>
				</view>
			</view>
			
			<!-- 顶部状态栏 -->
			<view class="call-status-bar">
				<view class="status-info">
					<text class="call-name">{{ selectedVideoName }}</text>
					<text class="call-time">{{ callDuration }}</text>
					<view
						v-if="isRecording || isProcessing"
						class="listening-chip listening-active"
					>
						<view class="listening-icon">💗</view>
						<text class="listening-text">TA在听你说</text>
					</view>
				</view>
			</view>
			
			<!-- 底部控制区 -->
			<view class="call-controls">
				<view v-if="selectedVoiceId && selectedVoiceId.startsWith('cosyvoice-v3-plus-')" class="form-section" style="position: relative; z-index: 1002;">
					<view class="form-label">方言</view>
					<picker mode="selector" :range="dialectOptions" @change="onDialectChange">
						<view class="picker-large">
							{{ selectedDialect || '请选择方言（可选）' }}
						</view>
					</picker>
				</view>

				<view v-if="selectedVoiceId && selectedVoiceId.startsWith('cosyvoice-v3-plus-')" class="form-section" style="position: relative; z-index: 1002;">
					<view class="form-label">语言提示（可选）</view>
					<picker mode="selector" :range="languageHintOptions" @change="onLanguageHintChange">
						<view class="picker-large">
							{{ selectedLanguageHintLabel || '请选择语言（可选）' }}
						</view>
					</picker>
					<view class="hint-text">💡 仅处理第一个值；不设置不生效</view>
				</view>

				<!-- 控制按钮组 -->
				<view class="control-buttons" style="position: relative; z-index: 1002;">
					<!-- 清空历史 -->
					<view class="control-btn" style="pointer-events: auto; z-index: 1003;" @tap="handleClearHistory">
						<view class="btn-circle">
							<view class="icon-delete"></view>
						</view>
						<text class="btn-label">清空</text>
					</view>
					
					<!-- 录音按钮 -->
					<view 
						class="control-btn mic-btn"
						style="pointer-events: auto; z-index: 1003;"
						@touchstart="handleTouchStart"
						@touchend="handleTouchEnd"
						@touchcancel="handleTouchCancel"
					>
						<view class="speak-button" :class="{ 'recording': isRecording, 'processing': isProcessing }">
							<!-- 外圈动画 -->
							<view v-if="isRecording" class="speak-ring"></view>
							<view v-if="isRecording" class="speak-ring-outer"></view>
							
							<!-- 按钮主体 -->
							<view class="speak-button-inner">
								<view v-if="!isRecording && !isProcessing" class="icon-mic-new"></view>
								<view v-if="isRecording" class="icon-wave-container">
									<view class="wave-bar" style="animation-delay: 0s;"></view>
									<view class="wave-bar" style="animation-delay: 0.1s;"></view>
									<view class="wave-bar" style="animation-delay: 0.2s;"></view>
									<view class="wave-bar" style="animation-delay: 0.15s;"></view>
									<view class="wave-bar" style="animation-delay: 0.05s;"></view>
								</view>
								<view v-if="isProcessing" class="icon-loading-new"></view>
							</view>
						</view>
						<text class="btn-label-new" :class="{ 'active': isRecording }">
							{{ isRecording ? '松开发送' : isProcessing ? '聆听中...' : '长按说话' }}
						</text>
					</view>
					
					<!-- 挂断 -->
					<view class="control-btn" style="pointer-events: auto; z-index: 1003;" @tap="endCall">
						<view class="btn-circle end-call">
							<view class="icon-hangup"></view>
						</view>
						<text class="btn-label">挂断</text>
					</view>
				</view>
			</view>
		</view>
		
		<!-- 通话设置弹窗 -->
		<view v-if="showSettingsDialog" class="settings-dialog-mask" @click="closeSettingsDialog">
			<view class="settings-dialog" @click.stop>
				<view class="dialog-header">
					<text class="dialog-title">💭 通话设置</text>
					<text class="dialog-close" @click="closeSettingsDialog">✕</text>
				</view>
				
				<view class="dialog-content">
					<!-- 记忆设定 -->
					<view class="dialog-section">
						<view class="section-label">💭 记忆设定（可选）</view>
						<text class="section-hint">设置对话记忆，让AI更真实地模拟对方</text>
						
						<view class="memory-item">
							<text class="memory-label">身份</text>
							<input 
								class="memory-input" 
								v-model="dialogMemoryIdentity" 
								placeholder="例如：我的母亲"
								maxlength="50"
							/>
						</view>
						
						<view class="memory-item">
							<text class="memory-label">性格特点</text>
							<textarea 
								class="memory-textarea" 
								v-model="dialogMemoryInfo" 
								placeholder="例如：温柔体贴，喜欢养花，做饭很拿手..."
								maxlength="200"
							/>
						</view>
						
					</view>
				</view>
				
				<view class="dialog-footer">
					<button class="dialog-btn cancel" @click="closeSettingsDialog">取消</button>
					<button class="dialog-btn confirm" @click="confirmSettings">开始通话</button>
				</view>
			</view>
		</view>
		
		<!-- 支付弹窗 -->
		<PaymentModal
			ref="paymentModal"
			:show="paymentModalData.show"
			:serviceType="paymentModalData.serviceType"
			:serviceName="paymentModalData.serviceName"
			:serviceDesc="paymentModalData.serviceDesc"
			:price="paymentModalData.price"
			:orderNo="paymentModalData.orderNo"
			:paymentTips="paymentModalData.paymentTips"
			@close="handlePaymentClose"
			@confirm="handlePaymentConfirm"
		/>
	</view>
</template>

<script>
import { API_BASE, API_ENDPOINTS, buildURL } from '@/config/api.js';
import PaymentModal from '@/components/PaymentModal.vue';
import { SERVICE_TYPES, showPaymentModal, handlePaymentConfirm as processPayment } from '@/utils/payment.js';

export default {
	components: {
		PaymentModal
	},
	data() {
		return {
			API_BASE,

			// 页面加载参数
			loadOptions: null,
			
			// 用户ID缓存（用于检测用户切换）
			lastUserId: null,

			// 视频选择
			loading: false,
			videos: [],
			selectedVideoId: '',
			selectedVideoName: '',
			selectedVideoUrl: '',
			selectedPhotoUrl: '',
			selectedVoiceId: '',
			selectedDialect: '',
			selectedLanguageHint: '',
			selectedLanguageHintLabel: '',
			languageHintOptions: ['中文(zh)', '英文(en)', '法语(fr)', '德语(de)', '日语(ja)', '韩语(ko)', '俄语(ru)'],
			dialectOptions: ['广东话', '东北话', '甘肃话', '贵州话', '河南话', '湖北话', '江西话', '闽南话', '宁夏话', '山西话', '陕西话', '山东话', '上海话', '四川话', '天津话', '云南话'],
			
			// 接通中状态
			isConnecting: false,
			connectingText: '正在接通...',

			// 视频缓存
			videoCache: {},
			downloadingVideos: {},
			localVideoUrl: '',
			// 缓存进度跟踪
			cacheProgress: {}, // { videoId: { progress: 0-100, status: 'downloading'|'completed'|'failed' } }
			preCachingInProgress: false,
			// 视频预加载状态
			videoPreloadStatus: {}, // { videoId: true/false }

			// 通话状态
			callStarted: false,
			callStatus: '视频通话中',
			callDuration: '00:00',
			callStartTime: null,
			durationTimer: null,

			// 对话
			messages: [],
			recognizingText: '', // 实时识别的文本（partial/final）
			scrollTop: 0,

			// 录音
			isRecording: false,
			isProcessing: false,
			isSpeaking: false, // AI是否正在说话
			processingText: '正在聆听...',
			recorderManager: null,
			audioFilePath: '',
			recordingStartTime: null,
			autoListen: false, // 禁用自动录音
			autoLoopTimer: null,
			longPressTimer: null, // 长按定时器
			isTouching: false, // 是否正在触摸
			stopFallbackTimer: null,
			stopFallbackRetries: 0,
			stopFallbackActive: false,

			// 语音活动检测（VAD）
			vadEnabled: true,
			vadVoiceThreshold: 0.004,
			vadSilenceMs: 600, // 静音0.6s自动停，快速响应
			vadMaxDurationMs: 15000, // 单次最长录音15s，避免截断长句
			vadSpeaking: false,
			vadSilenceStart: null,
			vadMaxTimer: null,
			vadLastSoundTime: null,
			vadLastFrameTime: null,
			vadWatchTimer: null,
			
			// 音频播放
			audioContext: null,
			audioCacheKey: '',
			currentAudioSrc: '',
			pendingAudioUrl: '',
			audioTriedFallback: false,
			audioSessionId: 0,
			audioPlayedInSession: false,
			audioPlayStartAt: 0,
			audioTriedRedownload: false,
			audioTriedRecreateContext: false,
			audioPlayInvokedInSession: false,
			audioPlayTargetSessionId: 0,
			audioDiagnosedInSession: false,
			videoContext: null,
			audioContextActivated: false, // 音频上下文是否已激活
			videoInitialPlayed: false, // 视频是否已进行初始播放（避免黑屏）
			
			// 超时控制
			processingTimeout: null,
		
			// 设置弹窗
			showSettingsDialog: false,
			dialogMemoryIdentity: '',
			dialogMemoryInfo: '',
			dialogMemoryCatchphrase: '',
			systemPrompt: '', // 保存用户设置的系统提示词
			
			// 支付相关
			paymentModalData: {
				show: false,
				serviceType: '',
				serviceName: '',
				serviceDesc: '',
				price: 0,
				orderNo: '',
				paymentTips: '点击确认支付后将开始视频通话'
			},
			_paymentResolve: null,
			_paymentReject: null,
			_paymentOnSuccess: null,
			_paymentOnFailed: null
		};
	},

	async onLoad(options) {
		console.log('[VideoCallNew] 页面加载', options);
		
		// 保存 options 供 onShow 使用
		this.loadOptions = options;
		
		// 记录当前用户ID
		const userId = uni.getStorageSync('userId');
		this.lastUserId = userId;
		
		// 提前加载缓存信息
		await this.loadVideoCache();
		
		// 如果有URL参数，直接进入通话模式
		if (options && options.videoId) {
			this.selectedVideoId = options.videoId;
			this.selectedVideoName = decodeURIComponent(options.videoName || '复活视频');
			this.selectedVideoUrl = decodeURIComponent(options.videoUrl || '');
			this.selectedVoiceId = options.voiceId || '';
			this.selectedDialect = '';
			
			console.log('[VideoCallNew] 从URL参数加载视频信息');
			console.log('[VideoCallNew] 视频ID:', this.selectedVideoId);
			console.log('[VideoCallNew] 视频名称:', this.selectedVideoName);
			console.log('[VideoCallNew] 视频URL:', this.selectedVideoUrl);
			console.log('[VideoCallNew] 音色ID:', this.selectedVoiceId);
			
			// 检查是否已有缓存
			await this.checkAndLoadLocalVideo(this.selectedVideoId, this.selectedVideoUrl);
			
			// 初始化
			this.initRecorder();
			this.initAudioContext();
			this.initVideoContext();
			this.startAutoLoop();
			
			// 直接显示支付弹窗
			setTimeout(() => {
				this.startVideoCallDirect();
			}, 300);
		} else {
			// 正常模式，加载视频列表
			this.loadVideos();
			this.initRecorder();
			this.initAudioContext();
			this.initVideoContext();
			this.startAutoLoop();
		}
	},

	onShow() {
		console.log('[VideoCallNew] 页面显示');
		
		// 检查用户是否切换
		const currentUserId = uni.getStorageSync('userId');
		const userChanged = currentUserId !== this.lastUserId;
		
		if (userChanged) {
			console.log('[VideoCallNew] 检测到用户切换');
			console.log('[VideoCallNew] 上次用户ID:', this.lastUserId, '当前用户ID:', currentUserId);
			this.lastUserId = currentUserId;
		}
		
		// 每次页面显示时都刷新视频列表（只要不在通话中且不是从URL参数直接进入）
		// 这样点击底部导航栏的"AI通话"时就会自动刷新
		if (!this.callStarted && (!this.loadOptions || !this.loadOptions.videoId)) {
			console.log('[VideoCallNew] 页面重新显示，刷新视频列表');
			this.loadVideos();
		}
	},

	onUnload() {
		this.cleanup();
	},

	methods: {
		isVideoUsableForCall(video) {
			if (!video) return false;
			const videoUrl = video.edited_video_url || video.videoUrl || video.local_video_path || video.video_url || video.localVideoPath;
			const voiceId = video.voice_id || video.voiceId;
			return !!videoUrl && !!voiceId;
		},

		// 加载视频列表
		async loadVideos() {
			this.loading = true;
			
			// 获取用户ID和Token
			const userId = uni.getStorageSync('userId') || '';
			const token = uni.getStorageSync('token') || '';
			
			// 加载缓存信息
			await this.loadVideoCache();
			
			uni.request({
				url: `${this.API_BASE}/api/photo-revival/videos`,
				method: 'GET',
				header: {
					'X-User-Id': userId,
					'Authorization': token ? `Bearer ${token}` : ''
				},
				success: (res) => {
					console.log('[VideoCallNew] 视频列表响应:', res.data);
					
					if (res.statusCode === 200) {
						// 处理不同的数据格式
						let videoList = [];
						
						if (Array.isArray(res.data)) {
							videoList = res.data;
						} else if (res.data && Array.isArray(res.data.data)) {
							videoList = res.data.data;
						} else if (res.data && res.data.videos && Array.isArray(res.data.videos)) {
							videoList = res.data.videos;
						} else if (res.data && res.data.success && Array.isArray(res.data.videos)) {
							videoList = res.data.videos;
						}
						this.videos = (videoList || []).filter(v => this.isVideoUsableForCall(v));
						console.log('[VideoCallNew] 加载视频列表成功:', this.videos.length);
					}
				},
				fail: (err) => {
					console.error('[VideoCallNew] 加载视频列表失败:', err);
					uni.showToast({
						title: '加载失败',
						icon: 'none'
					});
				},
				complete: () => {
					this.loading = false;
				}
			});
		},

		// 选择视频
		selectVideo(video) {
			this.selectedVideoId = video.id;
			this.selectedVideoName = video.name || '复活视频';
			// 兼容多种字段名格式
			this.selectedVideoUrl = video.edited_video_url || video.videoUrl || video.local_video_path || video.video_url || video.localVideoPath;
			this.selectedPhotoUrl = video.photoUrl || video.photo_url || video.photoPath || '';
			this.selectedVoiceId = video.voice_id || video.voiceId;
			this.selectedDialect = '';
			this.selectedLanguageHint = '';
			this.selectedLanguageHintLabel = '';
			console.log('[VideoCallNew] 选择视频:', this.selectedVideoId);
			console.log('[VideoCallNew] 视频URL:', this.selectedVideoUrl);
			console.log('[VideoCallNew] 照片URL:', this.selectedPhotoUrl);
			console.log('[VideoCallNew] 音色ID:', this.selectedVoiceId);
		},

		// 开始视频通话（从列表点击）
		async startVideoCall(video) {
			// 先选择视频
			this.selectVideo(video);
			
			if (!this.selectedVideoId || !this.selectedVoiceId) {
				uni.showToast({
					title: '该视频无法进行通话',
					icon: 'none'
				});
				return;
			}
			
			if (!this.selectedVideoUrl) {
				uni.showToast({
					title: '视频地址不存在',
					icon: 'none'
				});
				return;
			}

			console.log('[VideoCallNew] 准备视频通话，视频ID:', this.selectedVideoId);
			console.log('[VideoCallNew] 视频URL:', this.selectedVideoUrl);
			
			// 检查并加载本地缓存
			await this.checkAndLoadLocalVideo(this.selectedVideoId, this.selectedVideoUrl);
			
			// 先进行支付
			showPaymentModal(
				this,
				SERVICE_TYPES.VIDEO_CALL.type,
				() => {
					// 支付成功后显示设置弹窗
					this.showCallSettings();
				},
				(error) => {
					console.error('[Payment] 支付失败:', error);
				}
			);
		},
		
		// 直接开始视频通话（从URL参数进入）
		async startVideoCallDirect() {
			if (!this.selectedVideoId || !this.selectedVoiceId) {
				uni.showToast({
					title: '视频信息不完整',
					icon: 'none'
				});
				setTimeout(() => {
					uni.navigateBack();
				}, 1500);
				return;
			}
			
			if (!this.selectedVideoUrl) {
				uni.showToast({
					title: '视频地址不存在',
					icon: 'none'
				});
				setTimeout(() => {
					uni.navigateBack();
				}, 1500);
				return;
			}

			console.log('[VideoCallNew] 直接开始视频通话');
			console.log('[VideoCallNew] 视频ID:', this.selectedVideoId);
			console.log('[VideoCallNew] 当前使用URL:', this.localVideoUrl || this.selectedVideoUrl);
			
			// 先进行支付
			showPaymentModal(
				this,
				SERVICE_TYPES.VIDEO_CALL.type,
				() => {
					// 支付成功后显示设置弹窗
					this.showCallSettings();
				},
				(error) => {
					console.error('[Payment] 支付失败:', error);
					// 支付失败返回上一页
					setTimeout(() => {
						uni.navigateBack();
					}, 1000);
				}
			);
		},
		
		// 显示通话设置弹窗
		showCallSettings() {
			// 初始化弹窗数据
			this.dialogMemoryIdentity = '';
			this.dialogMemoryInfo = '';
			this.dialogMemoryCatchphrase = '';
			
			// 显示设置弹窗
			this.showSettingsDialog = true;
		},
		
		// 关闭设置弹窗
		closeSettingsDialog() {
			this.showSettingsDialog = false;
		},
		
		// 确认设置并开始通话
		confirmSettings() {
			console.log('[VideoCallNew] 开始视频通话');
			console.log('[VideoCallNew] 使用视频的音色ID:', this.selectedVoiceId);
			
			// 关闭弹窗
			this.showSettingsDialog = false;
			
			// 重置视频初始播放标志
			this.videoInitialPlayed = false;
			
			// 立即显示接通中状态（不使用loading，使用自定义接通界面）
			this.isConnecting = true;
			this.connectingText = '正在接通...';
			
			// 构建系统提示词并保存（与复活页一致的Role Play格式）
			if (this.dialogMemoryIdentity || this.dialogMemoryInfo) {
				if (this.dialogMemoryIdentity) {
					let prompt = `你是${this.dialogMemoryIdentity}，你已经去世。用户正在和你对话。`;
					prompt += '\n\n你的性格特点：\n\n亲切、真实、克制';
					prompt += '\n\n你的行事风格：\n\n直接回答，不绕弯';
					prompt += '\n\n你的语言特点：\n\n自然口语，语气温和';
					if (this.dialogMemoryInfo) {
						prompt += `\n\n补充设定（请严格遵循）：\n\n${this.dialogMemoryInfo}`;
					}
					prompt += '\n\n注意：不要使用括号（包括()和（））来描述动作、心理、背景。';
					prompt += '\n\n规则：\n\n1. 不要虚构不存在的事，不要编造记忆或经历。\n2. 不要回忆以前（除非用户明确提到并且你能基于已有设定回答）。\n3. 不要使用任何称呼（因为用户可能是任何身份）。\n4. 不要使用emoji/颜文字/特殊符号。\n5. 顺应用户话题，亲切回答问题。\n6. 时刻记住自己是逝者。\n7. 每次回复保持在20~50字之间。';
					this.systemPrompt = prompt;
				} else {
					let prompt = '你是一位已经去世的人。用户正在和你对话。';
					prompt += '\n\n你的性格特点：\n\n亲切、真实、克制';
					prompt += '\n\n你的行事风格：\n\n直接回答，不绕弯';
					prompt += '\n\n你的语言特点：\n\n自然口语，语气温和';
					if (this.dialogMemoryInfo) {
						prompt += `\n\n补充设定（请严格遵循）：\n\n${this.dialogMemoryInfo}`;
					}
					prompt += '\n\n注意：不要使用括号（包括()和（））来描述动作、心理、背景。';
					prompt += '\n\n规则：\n\n1. 不要虚构不存在的事，不要编造记忆或经历。\n2. 不要回忆以前（除非用户明确提到并且你能基于已有设定回答）。\n3. 不要使用任何称呼（因为用户可能是任何身份）。\n4. 不要使用emoji/颜文字/特殊符号。\n5. 顺应用户话题，亲切回答问题。\n6. 时刻记住自己是逝者。\n7. 每次回复保持在20~50字之间。';
					this.systemPrompt = prompt;
				}
			} else {
				let prompt = '你是一位已经去世的人。用户正在和你对话。';
				prompt += '\n\n你的性格特点：\n\n亲切、真实、克制';
				prompt += '\n\n你的行事风格：\n\n直接回答，不绕弯';
				prompt += '\n\n你的语言特点：\n\n自然口语，语气温和';
				prompt += '\n\n注意：不要使用括号（包括()和（））来描述动作、心理、背景。';
				prompt += '\n\n规则：\n\n1. 不要虚构不存在的事，不要编造记忆或经历。\n2. 不要回忆以前（除非用户明确提到并且你能基于已有设定回答）。\n3. 不要使用任何称呼（因为用户可能是任何身份）。\n4. 不要使用emoji/颜文字/特殊符号。\n5. 顺应用户话题，亲切回答问题。\n6. 时刻记住自己是逝者。\n7. 每次回复保持在20~50字之间。';
				this.systemPrompt = prompt;
			}
			console.log('[VideoCallNew] 系统提示词:', this.systemPrompt);
			
			// 立即开始通话
			this.callStarted = true;
			this.startCallTimer();
			
			// 等待DOM更新后重新初始化视频上下文
			this.$nextTick(() => {
				// 重新初始化视频上下文（因为DOM重新渲染了）
				this.videoContext = uni.createVideoContext('callVideo', this);
				
				console.log('[VideoCallNew] 重新初始化视频上下文');
				console.log('[VideoCallNew] 当前视频URL:', this.localVideoUrl || this.selectedVideoUrl);
				
				// #ifndef MP-WEIXIN
				// 非微信小程序：可做短暂自动播放避免黑屏
				setTimeout(() => {
					if (this.videoContext && !this.videoInitialPlayed) {
						console.log('[VideoCallNew] 视频上下文已初始化，自动播放一秒避免黑屏');
						this.videoInitialPlayed = true;
						this.videoContext.seek(0);
						this.videoContext.play();
						
						// 播放一秒后暂停
						setTimeout(() => {
							if (this.videoContext && !this.isSpeaking) {
								this.videoContext.pause();
								console.log('[VideoCallNew] 视频已播放一秒，暂停等待AI回复');
							}
						}, 1000);
					}
				}, 500);
				// #endif
			});
		},

		// 开始计时
		startCallTimer() {
			this.callStartTime = Date.now();
			this.durationTimer = setInterval(() => {
				const elapsed = Math.floor((Date.now() - this.callStartTime) / 1000);
				const minutes = Math.floor(elapsed / 60).toString().padStart(2, '0');
				const seconds = (elapsed % 60).toString().padStart(2, '0');
				this.callDuration = `${minutes}:${seconds}`;
				
				// 检查是否达到10分钟（600秒）
				if (elapsed >= 600) {
					console.log('[VideoCallNew] 通话时长已达10分钟，自动挂断');
					this.autoEndCall();
				}
			}, 1000);
		},

		// 初始化录音
		initRecorder() {
			this.recorderManager = uni.getRecorderManager();
			
			console.log('[VAD] RecorderManager 对象:', this.recorderManager);
			console.log('[VAD] onFrameRecorded 方法存在:', typeof this.recorderManager.onFrameRecorded);

			this.recorderManager.onStart(() => {
				console.log('[VideoCallNew] 开始录音');
			});

			this.recorderManager.onStop((res) => {
				console.log('[VideoCallNew] 录音完成:', res.tempFilePath);
				this.isRecording = false;
				this.isTouching = false;
				if (this.longPressTimer) {
					clearTimeout(this.longPressTimer);
					this.longPressTimer = null;
				}
				if (this.stopFallbackTimer) {
					clearTimeout(this.stopFallbackTimer);
					this.stopFallbackTimer = null;
				}
				this.stopFallbackActive = false;
				this.stopFallbackRetries = 0;
				this.resetVADState();

				const duration = res.duration || (Date.now() - (this.recordingStartTime || Date.now()));
				if (duration < 400) {
					console.warn('[VideoCallNew] 录音过短，已忽略');
					this.isProcessing = false;
					this.callStatus = '通话中';
					return;
				}

				this.audioFilePath = res.tempFilePath;
				this.processConversation();
			});

			// 语音活动检测
			if (typeof this.recorderManager.onFrameRecorded === 'function') {
				console.log('[VAD] ✅ onFrameRecorded 支持，已绑定帧回调');
				this.recorderManager.onFrameRecorded((res) => {
					console.log('[VAD] 🎯 收到帧数据！大小:', res.frameBuffer ? res.frameBuffer.byteLength : 0, 'bytes');
					if (!this.vadEnabled || this.isProcessing) {
						console.log('[VAD] VAD已禁用或正在处理，跳过');
						return;
					}
					this.handleVADFrame(res.frameBuffer);
				});
			} else {
				console.error('[VAD] ❌ onFrameRecorded 不支持，VAD功能将无法使用');
				console.error('[VAD] recorderManager 类型:', typeof this.recorderManager);
				console.error('[VAD] 可用方法:', Object.keys(this.recorderManager));
				this.vadEnabled = false;
			}

			this.recorderManager.onError((err) => {
				console.error('[VideoCallNew] 录音失败:', err);
				this.isRecording = false;
				this.resetVADState();
				
				// 判断是否为权限问题
				const isPermissionError = err.errMsg && (
					err.errMsg.includes('permission') || 
					err.errMsg.includes('authorize') ||
					err.errMsg.includes('denied') ||
					err.errMsg.includes('占用')
				);
				
				if (isPermissionError) {
					// 权限问题，引导用户去设置
					uni.showModal({
						title: '需要麦克风权限',
						content: '录音功能需要麦克风权限才能使用。\n\n请前往：\n手机设置 → 应用管理 → 时光意境 → 权限管理 → 开启麦克风权限',
						confirmText: '我知道了',
						showCancel: false
					});
				} else {
					uni.showToast({
						title: '录音失败: ' + (err.errMsg || '未知错误'),
						icon: 'none',
						duration: 3000
					});
				}
			});
		},

		// 初始化视频播放
		initVideoContext() {
			this.videoContext = uni.createVideoContext('callVideo', this);
		},

		// 初始化音频播放（创建一次并复用）
		initAudioContext() {
			if (this.audioContext) return;

			this.audioContext = uni.createInnerAudioContext();
			// 设置最大音量
			this.audioContext.volume = 1.0;
			// 设置不遵循静音开关
			try {
				this.audioContext.obeyMuteSwitch = false;
			} catch (e) {
				console.warn('[VideoCallNew] 设置obeyMuteSwitch失败:', e);
			}
			console.log('[VideoCallNew] 🔊 音频上下文初始化完成，音量设置为最大');

			this.audioContext.onCanplay(() => {
				console.log('[VideoCallNew] ✅ 音频已准备好，可以播放');
				this.tryPlayAudioForSession('canplay');
			});

			this.audioContext.onEnded(() => {
				console.log('[VideoCallNew] AI回复播放完成');
				this.isSpeaking = false;
				this.callStatus = '通话中';
				if (this.videoContext) {
					// 结束后暂停即可，避免每次重置触发重缓冲
					this.videoContext.pause();
				}
			});

			this.audioContext.onError((err) => {
				if (this.isSpeaking && !this.audioDiagnosedInSession && this.pendingAudioUrl) {
					this.audioDiagnosedInSession = true;
					this.diagnoseAudioUrl(this.pendingAudioUrl);
				}
				// 远程直连播放偶发会被系统 stop 或解码失败，先尝试回退到本地缓存/临时文件重试一次
				const isDecodeError = !!(err && (err.errCode === 67 || err.errMsg && String(err.errMsg).includes('decode')));
				const isCurrentlyRemote = !!(this.pendingAudioUrl && (this.currentAudioSrc === this.pendingAudioUrl || this.audioContext.src === this.pendingAudioUrl));
				if (this.isSpeaking && !this.audioTriedFallback && isDecodeError && isCurrentlyRemote && this.pendingAudioUrl && this.audioContext) {
					this.audioTriedFallback = true;
					console.warn('[VideoCallNew] 音频播放失败，尝试回退本地重试一次。errCode=', err.errCode, 'errMsg=', err.errMsg);
					const sessionId = this.audioSessionId;
					this.downloadAudioTemp(this.pendingAudioUrl).then((localSrc) => {
						if (sessionId !== this.audioSessionId) return;
						if (!localSrc || !this.audioContext || !this.isSpeaking) return;
						if (localSrc === this.pendingAudioUrl) return;
						if (localSrc === this.currentAudioSrc || localSrc === this.audioContext.src) return;
						try {
							this.audioContext.stop();
						} catch (e) {
							// ignore
						}
						this.currentAudioSrc = localSrc;
						this.audioContext.src = localSrc;
						setTimeout(() => {
							if (sessionId !== this.audioSessionId) return;
							if (!this.audioContext || !this.isSpeaking) return;
							try {
								this.audioContext.play();
								console.log('[VideoCallNew] ✅ 已使用本地音频回退重试播放');
							} catch (e) {
								// 继续走下面的错误处理
							}
						}, 150);
					});
					return;
				}
				// decode fail 可能会把解码器/上下文打坏：重建 InnerAudioContext 后再用当前 src 重试一次
				if (this.isSpeaking && isDecodeError && !this.audioTriedRecreateContext && this.audioContext) {
					this.audioTriedRecreateContext = true;
					const sessionId = this.audioSessionId;
					const retrySrc = this.currentAudioSrc || (this.audioContext && this.audioContext.src) || this.pendingAudioUrl;
					console.warn('[VideoCallNew] decode fail，尝试重建音频上下文并重试一次');
					this.recreateAudioContext().then(() => {
						if (sessionId !== this.audioSessionId) return;
						if (!this.audioContext || !this.isSpeaking || !retrySrc) return;
						this.audioContext.src = retrySrc;
						this.currentAudioSrc = retrySrc;
						setTimeout(() => {
							if (sessionId !== this.audioSessionId) return;
							if (!this.audioContext || !this.isSpeaking) return;
							try {
								this.audioContext.play();
								console.log('[VideoCallNew] ✅ 已重建音频上下文并重试播放');
							} catch (e) {
								// 继续走下面错误处理
							}
						}, 200);
					});
					return;
				}
				// 某些机型对 savedFilePath(wxfile://store_) 解码偶发失败：清缓存并重新下载 tempFilePath(wxfile://tmp_) 播放一次
				const isCachedStoreFile = !!(this.audioContext && this.audioContext.src && String(this.audioContext.src).startsWith('wxfile://store_'));
				if (this.isSpeaking && isDecodeError && !this.audioTriedRedownload && isCachedStoreFile && this.pendingAudioUrl && this.audioContext) {
					this.audioTriedRedownload = true;
					const sessionId = this.audioSessionId;
					console.warn('[VideoCallNew] 本地缓存音频解码失败，清缓存并重新下载临时文件重试一次');
					this.clearAudioCacheForUrl(this.pendingAudioUrl).finally(() => {
						this.downloadAudioTemp(this.pendingAudioUrl).then((tempSrc) => {
							if (sessionId !== this.audioSessionId) return;
							if (!tempSrc || !this.audioContext || !this.isSpeaking) return;
							try {
								this.audioContext.stop();
							} catch (e) {
								// ignore
							}
							this.currentAudioSrc = tempSrc;
							this.audioContext.src = tempSrc;
							setTimeout(() => {
								if (sessionId !== this.audioSessionId) return;
								if (!this.audioContext || !this.isSpeaking) return;
								try {
									this.audioContext.play();
									console.log('[VideoCallNew] ✅ 已使用重新下载的临时音频重试播放');
								} catch (e) {
									// 继续走下面错误处理
								}
							}, 200);
						});
					});
					return;
				}
				console.error('[VideoCallNew] ❌ 音频播放失败:', JSON.stringify(err));
				console.error('[VideoCallNew] 错误详情 - errMsg:', err.errMsg, 'errCode:', err.errCode);
				this.isSpeaking = false;
				this.callStatus = '通话中';
				this.isProcessing = false;
				uni.showToast({
					title: '音频播放失败',
					icon: 'none',
					duration: 2000
				});
				if (this.videoContext) {
					this.videoContext.pause();
				}
			});

			this.audioContext.onPlay(() => {
				console.log('[VideoCallNew] ▶️ 音频播放事件触发');
				this.audioPlayedInSession = true;
				this.audioPlayStartAt = Date.now();
				// 音频开始时可确保视频处于播放状态，但不要反复 seek(0)（容易导致系统打断/解码异常）
				if (this.videoContext) {
					this.videoContext.play();
				}
			});

			this.audioContext.onPause(() => {
				console.log('[VideoCallNew] ⏸️ 音频暂停事件触发');
				// 如果AI正在说话时音频被暂停，尝试恢复播放
				if (this.isSpeaking) {
					console.warn('[VideoCallNew] ⚠️ AI说话时音频被暂停，尝试恢复播放...');
					setTimeout(() => {
						if (this.isSpeaking && this.audioContext) {
							try {
								this.audioContext.play();
								console.log('[VideoCallNew] ✅ 音频已恢复播放');
							} catch (e) {
								console.error('[VideoCallNew] ❌ 恢复音频播放失败:', e);
							}
						}
					}, 100);
				}
			});

			this.audioContext.onWaiting(() => {
				console.log('[VideoCallNew] ⏳ 音频加载中...');
			});

			this.audioContext.onStop(() => {
				console.log('[VideoCallNew] 音频停止事件触发');
			});

			this.audioContext.onWaiting(() => {
				console.log('[VideoCallNew] 音频等待加载');
			});

			this.audioContext.onCanplay(() => {
				console.log('[VideoCallNew] 音频可以播放');
				this.tryPlayAudioForSession('canplay2');
			});
		},

		tryPlayAudioForSession(reason) {
			const sessionId = this.audioPlayTargetSessionId;
			if (!sessionId || sessionId !== this.audioSessionId) return;
			if (!this.audioContext || !this.isSpeaking) return;
			if (this.audioPlayInvokedInSession) return;
			this.audioPlayInvokedInSession = true;
			try {
				this.audioContext.play();
				console.log('[VideoCallNew] ✅ 已触发音频播放(', reason, ')');
			} catch (e) {
				console.error('[VideoCallNew] ❌ 触发音频播放失败(', reason, '):', e);
			}
		},

		recreateAudioContext() {
			return new Promise((resolve) => {
				try {
					if (this.audioContext) {
						try {
							this.audioContext.stop();
						} catch (e) {
							// ignore
						}
						try {
							this.audioContext.destroy();
						} catch (e) {
							// ignore
						}
					}
				} catch (e) {
					// ignore
				}
				this.audioContext = null;
				this.initAudioContext();
				setTimeout(() => resolve(), 50);
			});
		},

		diagnoseAudioUrl(audioUrl) {
			if (!audioUrl) return;
			console.log('[VideoCallNew] 🔍 音频诊断：开始下载以检测是否为有效mp3', audioUrl);
			try {
				uni.request({
					url: audioUrl,
					method: 'HEAD',
					timeout: 20000,
					success: (res) => {
						console.log('[VideoCallNew] 🔍 音频诊断：HEAD statusCode=', res.statusCode);
						console.log('[VideoCallNew] 🔍 音频诊断：HEAD headers=', res.header || {});
					},
					fail: (e) => {
						console.log('[VideoCallNew] 🔍 音频诊断：HEAD失败', e);
					}
				});
			} catch (e) {
				// ignore
			}
			uni.downloadFile({
				url: audioUrl,
				timeout: 20000,
				success: (res) => {
					console.log('[VideoCallNew] 🔍 音频诊断：downloadFile statusCode=', res.statusCode);
					if (res.statusCode !== 200 || !res.tempFilePath) return;
					try {
						const fsm = uni.getFileSystemManager();
						try {
							fsm.readFile({
								filePath: res.tempFilePath,
								position: 0,
								length: 16,
								success: (readRes) => {
									let hex = '';
									try {
										const u8 = new Uint8Array(readRes.data);
										hex = Array.from(u8).map(b => b.toString(16).padStart(2, '0')).join(' ');
									} catch (e) {
										hex = '';
									}
									console.log('[VideoCallNew] 🔍 音频诊断：file header(16 bytes)=', hex);
								},
								fail: (e) => {
									console.log('[VideoCallNew] 🔍 音频诊断：readFile失败', e);
								}
							});
						} catch (e) {
							// ignore
						}
						fsm.getFileInfo({
							filePath: res.tempFilePath,
							success: (info) => {
								console.log('[VideoCallNew] 🔍 音频诊断：tempFile size=', info.size, 'path=', res.tempFilePath);
							},
							fail: (e) => {
								console.log('[VideoCallNew] 🔍 音频诊断：getFileInfo失败', e);
							}
						});
					} catch (e) {
						console.log('[VideoCallNew] 🔍 音频诊断异常:', e);
					}
				},
				fail: (e) => {
					console.log('[VideoCallNew] 🔍 音频诊断：downloadFile失败', e);
				}
			});
		},

		generateAudioCacheKey(url) {
			if (!url) return '';
			let hash = 0;
			for (let i = 0; i < url.length; i++) {
				const char = url.charCodeAt(i);
				hash = ((hash << 5) - hash) + char;
				hash = hash & hash;
			}
			return 'audiocache_' + Math.abs(hash);
		},

		getPlayableAudioSrc(audioUrl) {
			return new Promise((resolve) => {
				if (!audioUrl) {
					resolve('');
					return;
				}
				const cacheKey = this.generateAudioCacheKey(audioUrl);
				this.audioCacheKey = cacheKey;
				const cachedPath = uni.getStorageSync(cacheKey);
				if (cachedPath) {
					uni.getSavedFileInfo({
						filePath: cachedPath,
						success: () => {
							resolve(cachedPath);
						},
						fail: () => {
							try { uni.removeStorageSync(cacheKey); } catch (e) {}
							resolve('');
						}
					});
					return;
				}
				uni.downloadFile({
					url: audioUrl,
					timeout: 60000,
					success: (res) => {
						if (res.statusCode !== 200) {
							resolve('');
							return;
						}
						uni.saveFile({
							tempFilePath: res.tempFilePath,
							success: (saveRes) => {
								try { uni.setStorageSync(cacheKey, saveRes.savedFilePath); } catch (e) {}
								resolve(saveRes.savedFilePath);
							},
							fail: () => {
								resolve(res.tempFilePath);
							}
						});
					},
					fail: () => {
						resolve('');
					}
				});
			});
		},

		clearAudioCacheForUrl(audioUrl) {
			return new Promise((resolve) => {
				try {
					const cacheKey = this.generateAudioCacheKey(audioUrl);
					const cachedPath = uni.getStorageSync(cacheKey);
					try { uni.removeStorageSync(cacheKey); } catch (e) {}
					if (cachedPath) {
						uni.removeSavedFile({
							filePath: cachedPath,
							complete: () => {
								resolve();
							}
						});
						return;
					}
				} catch (e) {
					// ignore
				}
				resolve();
			});
		},

		downloadAudioTemp(audioUrl) {
			return new Promise((resolve) => {
				if (!audioUrl) {
					resolve('');
					return;
				}
				uni.downloadFile({
					url: audioUrl,
					timeout: 20000,
					success: (res) => {
						if (res.statusCode === 200 && res.tempFilePath) {
							resolve(res.tempFilePath);
							return;
						}
						resolve('');
					},
					fail: () => {
						resolve('');
					}
				});
			});
		},

		// 触摸开始（长按录音）
		handleTouchStart(e) {
			console.log('[VideoCallNew] ========== 触摸开始 ==========');
			console.log('[VideoCallNew] 事件对象:', e);
			console.log('[VideoCallNew] isProcessing:', this.isProcessing, 'isRecording:', this.isRecording);
			
			if (this.isProcessing || this.isRecording) {
				console.log('[VideoCallNew] 跳过：正在处理或录音中');
				return;
			}
			
			this.isTouching = true;
			
			// 设置长按定时器，300ms后开始录音
			this.longPressTimer = setTimeout(() => {
				if (this.isTouching) {
					console.log('[VideoCallNew] 长按触发，开始录音');
					this.startRecording(true);
				}
			}, 300);
		},
		
		// 触摸结束（松开发送）
		handleTouchEnd(e) {
			console.log('[VideoCallNew] ========== 触摸结束 ==========');
			console.log('[VideoCallNew] 事件对象:', e);
			this.isTouching = false;
			
			// 清除长按定时器
			if (this.longPressTimer) {
				clearTimeout(this.longPressTimer);
				this.longPressTimer = null;
			}
			
			// 如果正在录音，则停止并发送
			if (this.isRecording) {
				console.log('[VideoCallNew] 松开手指，发送录音');
				this.stopRecordingInternal('manual');
			}
		},
		
		onDialectChange(e) {
			this.selectedDialect = this.dialectOptions[e.detail.value] || '';
		},

		onLanguageHintChange(e) {
			const label = this.languageHintOptions[e.detail.value] || '';
			this.selectedLanguageHintLabel = label;
			const match = label.match(/\(([^)]+)\)/);
			this.selectedLanguageHint = match && match[1] ? match[1] : '';
		},
		
		// 触摸取消（意外中断）
		handleTouchCancel(e) {
			console.log('[VideoCallNew] ========== 触摸取消 ==========');
			console.log('[VideoCallNew] 事件对象:', e);
			this.isTouching = false;
			
			// 清除长按定时器
			if (this.longPressTimer) {
				clearTimeout(this.longPressTimer);
				this.longPressTimer = null;
			}
			
			// 如果正在录音，则取消录音
			if (this.isRecording) {
				console.log('[VideoCallNew] 触摸取消，停止录音');
				this.isRecording = false;
				this.recorderManager.stop();
				this.resetVADState();
				uni.showToast({
					title: '已取消录音',
					icon: 'none',
					duration: 1500
				});
			}
		},

		handleGlobalTouchEnd(e) {
			console.log('[VideoCallNew] ========== 全局触摸结束兜底 ==========', e);
			this.isTouching = false;
			if (this.longPressTimer) {
				clearTimeout(this.longPressTimer);
				this.longPressTimer = null;
			}
			if (this.isRecording) {
				this.stopRecordingInternal('manual');
			}
		},
		
		handleGlobalTouchCancel(e) {
			console.log('[VideoCallNew] ========== 全局触摸取消兜底 ==========', e);
			this.isTouching = false;
			if (this.longPressTimer) {
				clearTimeout(this.longPressTimer);
				this.longPressTimer = null;
			}
			if (this.isRecording) {
				this.isRecording = false;
				this.recorderManager.stop();
				this.resetVADState();
				uni.showToast({
					title: '已取消录音',
					icon: 'none',
					duration: 1500
				});
			}
		},

		// 开始录音
		startRecording(isManual = false) {
			if (this.isProcessing) return;
			// AI正在说话时，禁止自动录音，但允许用户手动点击触发
			if (this.isSpeaking && !isManual) {
				return;
			}

			this.resetVADState();
			this.recordingStartTime = Date.now();
			this.vadLastSoundTime = this.recordingStartTime;
			this.vadLastFrameTime = this.recordingStartTime;
			this.isRecording = true;
			console.log('[VAD] 开始录音，时间:', new Date().toLocaleTimeString());

			const enableVadAutoStop = this.vadEnabled && !isManual;
			if (enableVadAutoStop) {
				this.vadMaxTimer = setTimeout(() => {
					console.log('[VAD] 达到最大录音时长15秒，强制停止');
					this.stopRecordingInternal('max_duration');
				}, this.vadMaxDurationMs); // 最大录音时长从6秒增加到15秒
				this.vadWatchTimer = setInterval(() => {
					if (!this.isRecording) return;
					const now = Date.now();
					const elapsed = now - (this.recordingStartTime || now);
					const silenceDuration = now - (this.vadLastSoundTime || now);

					// 无帧回调兜底
					if (now - (this.vadLastFrameTime || now) > 8000) {
						console.log('[VAD] 无帧回调超时，停止录音');
						this.stopRecordingInternal('no_frame');
						return;
					}

					// 一直未检测到说话，提前结束，避免空录
					if (!this.vadSpeaking && elapsed > 2500) {
						console.log('[VAD] 未检测到语音，自动停止录音');
						this.stopRecordingInternal('no_speech');
						return;
					}

					// 静音超时（需已检测到说话，且录音时长超过500ms）
					if (elapsed > 500 && this.vadSpeaking && silenceDuration > this.vadSilenceMs) {
						console.log('[VAD] 检测到静音超时');
						console.log('[VAD] 录音总时长:', elapsed, 'ms, 静音时长:', silenceDuration, 'ms');
						this.stopRecordingInternal('silence');
						return;
					}
				}, 150); // 检测间隔优化到150ms，更快响应
			}

			const recorderOptions = {
				format: 'mp3',
				sampleRate: 16000,
				numberOfChannels: 1,
				encodeBitRate: 32000
			};
			if (isManual) {
				recorderOptions.duration = 600000;
			}
		
			// 只有当VAD启用且onFrameRecorded支持时才设置frameSize
			// 注意：frameSize单位是毫秒（ms），微信小程序/App只支持200或500
			if (this.vadEnabled && this.recorderManager.onFrameRecorded) {
				recorderOptions.frameSize = 200; // 200ms触发一次帧回调，快速响应
				console.log('[VAD] 启用帧回调，frameSize: 200ms');
			}
		
			console.log('[VAD] 录音参数:', JSON.stringify(recorderOptions));
			this.recorderManager.start(recorderOptions);

			this.callStatus = '正在录音（自动识别说话）';
		},

		// 停止录音
		stopRecording() {
			if (!this.isRecording) return;

			this.stopRecordingInternal('manual');
		},

		// VAD/手动共用停止逻辑
		stopRecordingInternal(reason = 'manual') {
			if (!this.isRecording) return;

			this.isRecording = false;
			this.recorderManager.stop();
			this.stopFallbackActive = true;
			this.stopFallbackRetries = 0;
			if (this.stopFallbackTimer) {
				clearTimeout(this.stopFallbackTimer);
				this.stopFallbackTimer = null;
			}
			this.stopFallbackTimer = setTimeout(() => {
				this.retryStopRecording();
			}, 1200);
			this.isProcessing = true;
			this.processingText = '正在聆听...';
			this.callStatus = '处理中...';
			this.recognizingText = '';
			this.resetVADState();

			console.log('[VideoCallNew] 停止录音，原因:', reason);
			
			// 用户交互时激活音频上下文（解决自动播放限制）
			if (!this.audioContextActivated) {
				// iOS上需要先播放一次音频才能解锁
				try {
					const tempAudio = uni.createInnerAudioContext();
					tempAudio.src = 'data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQAAAAA=';
					tempAudio.volume = 0;
					// #ifndef MP-WEIXIN
					tempAudio.play();
					// #endif
					setTimeout(() => {
						tempAudio.destroy();
					}, 100);
					this.audioContextActivated = true;
					console.log('[VideoCallNew] 音频上下文已激活');
				} catch (e) {
					console.warn('[VideoCallNew] 激活音频上下文失败:', e);
				}
			}
			
			// 设置30秒超时保护
			this.processingTimeout = setTimeout(() => {
				if (this.isProcessing) {
					console.warn('[VideoCallNew] 处理超时，重置状态');
					this.isProcessing = false;
					this.callStatus = '通话中';
					uni.showToast({
						title: '处理超时，请重试',
						icon: 'none'
					});
				}
			}, 30000);
		},

		retryStopRecording() {
			if (!this.stopFallbackActive) return;
			if (!this.recorderManager) return;
			this.stopFallbackRetries += 1;
			if (this.stopFallbackRetries > 2) {
				this.stopFallbackActive = false;
				this.stopFallbackTimer = null;
				this.isProcessing = false;
				this.callStatus = '通话中';
				return;
			}
			try {
				this.recorderManager.stop();
			} catch (e) {
			}
			this.stopFallbackTimer = setTimeout(() => {
				this.retryStopRecording();
			}, 1200);
		},

		resetVADState() {
			this.vadSpeaking = false;
			this.vadSilenceStart = null;
			this.vadLastSoundTime = null;
			this.vadLastFrameTime = null;
			if (this.vadMaxTimer) {
				clearTimeout(this.vadMaxTimer);
				this.vadMaxTimer = null;
			}
			if (this.vadWatchTimer) {
				clearInterval(this.vadWatchTimer);
				this.vadWatchTimer = null;
			}
		},

		handleVADFrame(frameBuffer) {
			if (!frameBuffer || frameBuffer.byteLength === 0) return;

			this.vadLastFrameTime = Date.now();
			const volume = this.calculateRms(frameBuffer);

			// 检测到语音
			if (volume > this.vadVoiceThreshold) {
				if (!this.vadSpeaking) {
					console.log('[VAD] 检测到语音开始，音量:', volume.toFixed(4));
				}
				this.vadSpeaking = true;
				this.vadSilenceStart = null;
				this.vadLastSoundTime = Date.now();
				return;
			}

			// 静音判定（只在已检测到说话后才开始计时）
			if (this.vadSpeaking) {
				if (!this.vadSilenceStart) {
					this.vadSilenceStart = Date.now();
					console.log('[VAD] 检测到静音开始，音量:', volume.toFixed(4));
				}
			} else {
				// 还没检测到说话，只记录音量
				if (Math.random() < 0.1) { // 10%概率打印，避免刷屏
					console.log('[VAD] 等待语音中，当前音量:', volume.toFixed(4), '阈值:', this.vadVoiceThreshold);
				}
			}
		},

		calculateRms(frameBuffer) {
			const dataView = new DataView(frameBuffer);
			const len = dataView.byteLength / 2;
			if (len === 0) return 0;

			let sum = 0;
			for (let i = 0; i < len; i++) {
				const sample = dataView.getInt16(i * 2, true);
				sum += sample * sample;
			}

			return Math.sqrt(sum / len) / 32768;
		},

		startAutoLoop() {
			if (!this.autoListen) return;
			if (this.autoLoopTimer) {
				clearInterval(this.autoLoopTimer);
			}
			this.autoLoopTimer = setInterval(() => {
				// 只有在通话真正开始后才自动录音，且避免打断AI播放
				if (!this.callStarted) return;
				if (!this.autoListen) return;
				if (this.isSpeaking) return;
				if (!this.isRecording && !this.isProcessing) {
					this.startRecording(false);
				}
			}, 2000);
		},

		// 处理对话
		async processConversation() {
			try {
				this.processingText = '正在识别...';
				const userId = uni.getStorageSync('userId') || '';
				const token = uni.getStorageSync('token') || '';
				
				// 添加进度提示，让用户感觉更快
				setTimeout(() => {
					if (this.isProcessing) {
						this.processingText = '正在思考...';
					}
				}, 800);
				
				setTimeout(() => {
					if (this.isProcessing) {
						this.processingText = '正在回复...';
					}
				}, 2000);

				uni.uploadFile({
					url: `${this.API_BASE}/api/conversation/talk`,
					filePath: this.audioFilePath,
					name: 'audio',
					header: {
						'X-User-Id': userId,
						'Authorization': token ? `Bearer ${token}` : ''
					},
					formData: (() => {
						const voiceId = this.selectedVoiceId;
						const voiceType = (() => {
							const v = (voiceId || '').trim();
							if (!v) return 'CLONE';
							const knownOfficial = ['Cherry','Kai','Mochi','Bunny','Jada','Dylan','Li','Marcus','Roy','Peter','Sunny','Eric','Rocky','Kiki'];
							if (knownOfficial.includes(v)) return 'OFFICIAL';
							if (v.startsWith('BV') || v.endsWith('_streaming') || v.endsWith('_offline') || v.endsWith('_bigtts')) return 'OFFICIAL';
							return 'CLONE';
						})();
						const data = {
							voiceId,
							voiceType,
							serviceType: 'VIDEO_CALL',
							systemPrompt: this.systemPrompt || '',
							saveHistory: 'false'
						};
						if (this.selectedDialect) {
							data.dialect = this.selectedDialect;
						}
						if (this.selectedLanguageHint) {
							data.languageHints = this.selectedLanguageHint;
						}
						return data;
					})(),
					success: (res) => {
						// 清除超时定时器
						if (this.processingTimeout) {
							clearTimeout(this.processingTimeout);
							this.processingTimeout = null;
						}
						
						console.log('[VideoCallNew] 对话响应:', res);
						
						if (res.statusCode === 200) {
							const result = JSON.parse(res.data);

							if (result.success) {
								// 添加用户消息
								this.addMessage('user', result.recognizedText);

								// 添加AI回复
								this.addMessage('ai', result.aiResponse);

								// 播放AI回复
								this.playAIResponse(result.audioFile);
							} else {
								// 识别失败，重置状态
								const msg = result.message || '未识别到语音内容';
								console.error('[VideoCallNew] 识别失败:', msg);
								uni.showModal({
									title: '对话失败',
									content: msg + '\n\n请重试。若已支付但多次失败，请联系客服补发（提供订单号/支付时间截图）。',
									showCancel: false,
									confirmText: '我知道了'
								});
								this.isProcessing = false;
								this.callStatus = '通话中';
							}
						} else {
							// 请求失败，重置状态
							console.error('[VideoCallNew] 请求失败，状态码:', res.statusCode);
							let parsed = null;
							try {
								parsed = JSON.parse(res.data);
							} catch (e) {
								parsed = null;
							}
							const msg = (parsed && parsed.message) ? parsed.message : ('对话请求失败: HTTP ' + res.statusCode);
							uni.showModal({
								title: '对话失败',
								content: msg + '\n\n请重试。若已支付但多次失败，请联系客服补发（提供订单号/支付时间截图）。',
								showCancel: false,
								confirmText: '我知道了'
							});
							this.isProcessing = false;
							this.callStatus = '通话中';
						}
					},
					fail: (err) => {
						// 清除超时定时器
						if (this.processingTimeout) {
							clearTimeout(this.processingTimeout);
							this.processingTimeout = null;
						}
						
						console.error('[VideoCallNew] 对话失败:', err);
						uni.showModal({
							title: '对话失败',
							content: '网络请求失败：' + (err.errMsg || '未知错误') + '\n\n请重试。若已支付但多次失败，请联系客服补发（提供订单号/支付时间截图）。',
							showCancel: false,
							confirmText: '我知道了'
						});
						this.isProcessing = false;
						this.callStatus = '通话中';
					}
				});
			} catch (error) {
				// 清除超时定时器
				if (this.processingTimeout) {
					clearTimeout(this.processingTimeout);
					this.processingTimeout = null;
				}
				
				console.error('[VideoCallNew] 对话失败:', error);
				uni.showModal({
					title: '对话失败',
					content: (error.message || '未知错误') + '\n\n请重试。若已支付但多次失败，请联系客服补发（提供订单号/支付时间截图）。',
					showCancel: false,
					confirmText: '我知道了'
				});
				this.isProcessing = false;
				this.callStatus = '通话中';
			}
		},

		// 播放AI回复
		async playAIResponse(audioFile) {
			if (!audioFile) {
				console.error('[VideoCallNew] ❌ 音频文件为空');
				uni.showToast({
					title: '音频文件缺失',
					icon: 'none'
				});
				this.isProcessing = false;
				this.callStatus = '通话中';
				return;
			}
			
			this.processingText = '正在回复...';
			
			const audioUrl = `${this.API_BASE}/api/conversation/audio/${audioFile}`;
			console.log('[VideoCallNew] 🎵 准备播放音频:', audioUrl);
			console.log('[VideoCallNew] 📁 音频文件名:', audioFile);

			// 复用单一音频实例，避免反复重建导致卡顿
			if (!this.audioContext) {
				console.log('[VideoCallNew] 初始化音频上下文...');
				this.initAudioContext();
			} else {
				try {
					console.log('[VideoCallNew] 停止之前的音频...');
					this.audioContext.stop();
				} catch (e) {
					console.warn('[VideoCallNew] 停止音频实例失败:', e);
				}
			}
			
			this.audioSessionId += 1;
			const sessionId = this.audioSessionId;
			this.audioPlayedInSession = false;
			this.audioPlayStartAt = 0;
			this.audioTriedRedownload = false;
			this.audioTriedRecreateContext = false;
			this.audioPlayInvokedInSession = false;
			this.audioPlayTargetSessionId = sessionId;
			this.audioDiagnosedInSession = false;
			this.pendingAudioUrl = audioUrl;
			this.audioTriedFallback = false;
			let playableSrc = audioUrl;
			try {
				const tempSrc = await this.downloadAudioTemp(audioUrl);
				if (sessionId === this.audioSessionId && tempSrc) {
					playableSrc = tempSrc;
				}
			} catch (e) {
				// ignore
			}
			if (sessionId !== this.audioSessionId || !this.audioContext) return;
			this.currentAudioSrc = playableSrc;
			this.audioContext.src = playableSrc;
			// 确保音量设置为最大
			this.audioContext.volume = 1.0;
			// 设置不遵循静音开关
			try {
				this.audioContext.obeyMuteSwitch = false;
			} catch (e) {
				console.warn('[VideoCallNew] 设置obeyMuteSwitch失败:', e);
			}
			console.log('[VideoCallNew] 🔊 音频源已设置:', this.audioContext.src);
			console.log('[VideoCallNew] 🔊 音频音量:', this.audioContext.volume);
			console.log('[VideoCallNew] 📢 如果声音过小，请检查：1.手机系统音量 2.媒体音量 3.静音开关');
			
			// 立即开始播放视频（从第1秒开始，避免首帧静止感）
			console.log('[VideoCallNew] 🎬 AI开始回复，从第1秒开始循环播放视频');
			this.isSpeaking = true;
			this.callStatus = '对方正在说话...';
			
			if (this.videoContext) {
				this.videoContext.seek(1);
				setTimeout(() => {
					if (this.videoContext) {
						this.videoContext.play();
					}
				}, 100);
			}
			
			console.log('[VideoCallNew] ▶️ 开始播放音频...');
			// canplay 触发播放为主，兜底：延迟尝试一次（只会触发一次）
			setTimeout(() => {
				if (sessionId !== this.audioSessionId) return;
				this.tryPlayAudioForSession('timer');
			}, 500);

			this.isProcessing = false;
		},

		// 添加消息
		addMessage(role, content) {
			const now = new Date();
			const time = `${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`;

			this.messages.push({
				role,
				content,
				time
			});

			// 滚动到底部
			this.$nextTick(() => {
				this.scrollTop = 999999;
			});
		},

		// 清空历史
		handleClearHistory(e) {
			console.log('[VideoCallNew] ========== 清空按钮被点击 ==========');
			console.log('[VideoCallNew] 事件对象:', e);
			
			// 立即显示反馈
			uni.showToast({
				title: '清空按钮已点击',
				icon: 'none',
				duration: 1000
			});
			
			uni.showModal({
				title: '确认清空',
				content: '确定要清空对话历史吗？',
				confirmColor: '#8B7355',
				success: (res) => {
					if (res.confirm) {
						this.clearHistory();
					}
				}
			});
		},

		clearHistory() {
			const userId = uni.getStorageSync('userId') || '';
			const token = uni.getStorageSync('token') || '';

			uni.request({
				url: `${this.API_BASE}/api/conversation/clear-history`,
				method: 'POST',
				header: {
					'X-User-Id': userId,
					'Authorization': token ? `Bearer ${token}` : ''
				},
				success: (res) => {
					if (res.data && res.data.success) {
						this.messages = [];
						uni.showToast({
							title: '已清空',
							icon: 'success'
						});
					} else {
						uni.showToast({
							title: res.data?.message || '清空失败',
							icon: 'none'
						});
					}
				},
				fail: (err) => {
					console.error('[VideoCallNew] 清空历史失败:', err);
					uni.showToast({
						title: '清空失败，请稍后重试',
						icon: 'none'
					});
				}
			});
		},

		// 自动挂断通话（10分钟到期）
		autoEndCall() {
			// 清除定时器
			if (this.durationTimer) {
				clearInterval(this.durationTimer);
				this.durationTimer = null;
			}
			
			// 震动提醒
			
			// 清理资源
			this.cleanup();
			
			// 显示提示弹窗
			uni.showModal({
				title: '通话已结束',
				content: '您本次通话已达10分钟时长上限，通话已自动结束。',
				showCancel: false,
				confirmText: '好的',
				confirmColor: '#8B7355',
				success: (res) => {
					if (res.confirm) {
						// 返回首页
						uni.reLaunch({
							url: '/pages/index/index'
						});
					}
				}
			});
		},
		
		// 手动挂断通话
		endCall(e) {
			console.log('[VideoCallNew] ========== 挂断按钮被点击 ==========');
			console.log('[VideoCallNew] 事件对象:', e);
			
			// 立即显示反馈
			uni.showToast({
				title: '挂断按钮已点击',
				icon: 'none',
				duration: 1000
			});
			
			uni.showModal({
				title: '结束通话',
				content: `通话时长：${this.callDuration}\n确定要结束通话吗？`,
				confirmColor: '#eb3349',
				success: (res) => {
					if (res.confirm) {
						uni.showLoading({
							title: '正在挂断...',
							mask: true
						});
						
						setTimeout(() => {
							uni.hideLoading();
							this.cleanup();
							
							uni.showToast({
								title: '通话已结束',
								icon: 'success',
								duration: 1500
							});
							
							// 延迟后跳转到首页
							setTimeout(() => {
								uni.reLaunch({
									url: '/pages/index/index'
								});
							}, 1500);
						}, 800);
					}
				}
			});
		},

		// 返回
		goBack() {
			if (this.callStarted) {
				this.endCall();
			} else {
				uni.navigateBack();
			}
		},

		// 跳转到复活照片
		goToRevival() {
			console.log('[VideoCallNew] 跳转到复活照片页面');
			uni.navigateTo({
				url: '/pages/revival/revival-original',
				fail: (err) => {
					console.error('[VideoCallNew] 跳转失败:', err);
				}
			});
		},
		
		// 视频元数据加载完成（更早触发）
		onVideoMetadataLoaded() {
			console.log('[VideoCallNew] 视频元数据加载完成，可以开始播放');
			
			// 元数据加载完成后立即隐藏接通中状态
			if (this.isConnecting) {
				this.connectingText = '已接通';
				// 快速隐藏接通界面，提升用户体验
				setTimeout(() => {
					this.isConnecting = false;
					uni.showToast({
						title: '通话已接通',
						icon: 'success',
						duration: 1500
					});
				}, 300);
			}
		},
		
		// 视频加载成功
		onVideoLoaded() {
			console.log('[VideoCallNew] 视频数据加载成功');
			console.log('[VideoCallNew] 当前视频URL:', this.selectedVideoUrl);
			
			// #ifndef MP-WEIXIN
			// 非微信小程序：可做短暂自动播放避免黑屏
			if (this.videoContext && !this.isSpeaking && !this.videoInitialPlayed) {
				console.log('[VideoCallNew] 视频数据加载完成，自动播放一秒避免黑屏');
				this.videoInitialPlayed = true;
				this.videoContext.seek(0);
				this.videoContext.play();
				
				// 播放一秒后暂停
				setTimeout(() => {
					if (this.videoContext && !this.isSpeaking) {
						this.videoContext.pause();
						console.log('[VideoCallNew] 视频已播放一秒，暂停等待AI回复');
					}
				}, 1000);
			}
			// #endif
		},
		
		// 视频预加载完成
		onVideoPreloaded(videoId) {
			console.log('[VideoCallNew] 视频预加载完成:', videoId);
			this.videoPreloadStatus[videoId] = true;
			this.$forceUpdate();
		},
		
		// 视频播放事件
		onVideoPlay() {
			console.log('[VideoCallNew] 视频开始播放');
		},
		
		// 视频暂停事件
		onVideoPause() {
			console.log('[VideoCallNew] 视频已暂停');
		},
		
		// 视频时间更新（当前不做额外处理，保留占位）
		onVideoTimeUpdate(e) {
			// console.log('[VideoCallNew] 视频播放进度:', e.detail);
		},
		
		// 视频加载错误
		onVideoError(e) {
			console.error('[VideoCallNew] 视频加载失败:', e);
			console.error('[VideoCallNew] 视频URL:', this.selectedVideoUrl);
			console.error('[VideoCallNew] 错误详情:', JSON.stringify(e));
			uni.showToast({
				title: '视频加载失败',
				icon: 'none',
				duration: 3000
			});
		},

		// 加载视频缓存信息
		async loadVideoCache() {
			try {
				const cache = uni.getStorageSync('videoCache');
				console.log('[VideoCallNew] 💾 读取缓存数据:', cache);
				if (cache) {
					this.videoCache = JSON.parse(cache);
					console.log('[VideoCallNew] ✅ 加载视频缓存信息:', Object.keys(this.videoCache).length, '个视频');
					console.log('[VideoCallNew] 缓存详情:', this.videoCache);
				} else {
					console.log('[VideoCallNew] ⚠️ 未找到缓存数据');
					this.videoCache = {};
				}
			} catch (e) {
				console.error('[VideoCallNew] ❌ 加载视频缓存失败:', e);
				this.videoCache = {};
			}
		},
		
		// 保存视频缓存信息
		async saveVideoCache() {
			try {
				const cacheStr = JSON.stringify(this.videoCache);
				uni.setStorageSync('videoCache', cacheStr);
				console.log('[VideoCallNew] 💾 保存视频缓存信息成功');
				console.log('[VideoCallNew] 保存内容:', cacheStr);
				
				// 验证保存是否成功
				const verify = uni.getStorageSync('videoCache');
				if (verify) {
					console.log('[VideoCallNew] ✅ 验证保存成功');
				} else {
					console.error('[VideoCallNew] ❌ 验证保存失败，数据未持久化');
				}
			} catch (e) {
				console.error('[VideoCallNew] ❌ 保存视频缓存失败:', e);
			}
		},
		
		// 检查并加载本地视频（启动时调用）
		async checkAndLoadLocalVideo(videoId, videoUrl) {
			if (!videoId || !videoUrl) {
				console.warn('[VideoCallNew] ⚠️ 视频ID或URL为空');
				return;
			}
			
			console.log('[VideoCallNew] 🔍 开始检查视频缓存');
			console.log('[VideoCallNew] 视频ID:', videoId);
			console.log('[VideoCallNew] 当前缓存对象:', this.videoCache);
			console.log('[VideoCallNew] 缓存中是否有该ID:', videoId in this.videoCache);
			
			// 检查是否已缓存
			if (this.videoCache[videoId]) {
				const cachedPath = this.videoCache[videoId];
				console.log('[VideoCallNew] 📝 发现缓存记录，验证文件:', cachedPath);
				
				// 验证文件是否存在
				const fileExists = await this.checkFileExists(cachedPath);
				console.log('[VideoCallNew] 文件存在性检查结果:', fileExists);
				
				if (fileExists) {
					this.localVideoUrl = cachedPath;
					console.log('[VideoCallNew] ✅ 本地视频文件存在，使用缓存');
					console.log('[VideoCallNew] 已设置 localVideoUrl:', this.localVideoUrl);
					return;
				} else {
					console.warn('[VideoCallNew] ⚠️ 缓存记录存在但文件丢失，清除缓存');
					delete this.videoCache[videoId];
					await this.saveVideoCache();
				}
			} else {
				console.log('[VideoCallNew] ❌ 缓存中未找到视频ID:', videoId);
			}
			
			// 无缓存时：先使用原始URL播放，同时在后台下载缓存
			console.log('[VideoCallNew] � 未找到缓存，先使用原始URL播放，同时后台下载');
			this.localVideoUrl = ''; // 清空localVideoUrl，让video组件使用selectedVideoUrl
			
			// 启动后台下载（不阻塞播放）
			this.startBackgroundDownload(videoUrl, videoId);
		},
		
		// 批量预加载所有视频
		async preloadAllVideos() {
			if (this.preCachingInProgress) {
				console.log('[VideoCallNew] 预缓存已在进行中，跳过');
				return;
			}
			
			this.preCachingInProgress = true;
			console.log('[VideoCallNew] 🚀 开始预缓存所有视频');
			
			// 按优先级排序：最新的视频优先下载
			const sortedVideos = [...this.videos].sort((a, b) => {
				const timeA = a.created_at || a.createdAt || 0;
				const timeB = b.created_at || b.createdAt || 0;
				return timeB - timeA; // 降序，最新的在前
			});
			
			let cachedCount = 0;
			let downloadCount = 0;
			
			for (const video of sortedVideos) {
				const videoId = video.id;
				const videoUrl = video.edited_video_url || video.videoUrl || video.local_video_path || video.video_url || video.localVideoPath;
				
				if (!videoId || !videoUrl) {
					console.warn('[VideoCallNew] 视频缺少ID或URL，跳过:', video.name);
					continue;
				}
				
				// 检查是否已缓存
				if (this.videoCache[videoId]) {
					const cachedPath = this.videoCache[videoId];
					const fileExists = await this.checkFileExists(cachedPath);
					
					if (fileExists) {
						cachedCount++;
						this.cacheProgress[videoId] = { progress: 100, status: 'completed' };
						console.log(`[VideoCallNew] ✅ 视频已缓存: ${video.name || videoId}`);
						continue;
					} else {
						// 缓存记录存在但文件丢失
						delete this.videoCache[videoId];
						await this.saveVideoCache();
					}
				}
				
				// 开始下载
				downloadCount++;
				console.log(`[VideoCallNew] 📥 开始预缓存 [${downloadCount}/${sortedVideos.length - cachedCount}]: ${video.name || videoId}`);
				
				// 初始化进度
				this.cacheProgress[videoId] = { progress: 0, status: 'downloading' };
				
				// 异步下载，不等待完成
				this.startBackgroundDownload(videoUrl, videoId, video.name).catch(err => {
					console.error(`[VideoCallNew] 预缓存失败: ${video.name}`, err);
					this.cacheProgress[videoId] = { progress: 0, status: 'failed' };
				});
				
				// 添加小延迟避免同时发起太多请求
				await new Promise(resolve => setTimeout(resolve, 200));
			}
			
			console.log(`[VideoCallNew] 🎉 预缓存启动完成: 已缓存 ${cachedCount} 个，正在下载 ${downloadCount} 个`);
			this.preCachingInProgress = false;
		},
		
		// 后台下载视频（不阻塞主流程）
		async startBackgroundDownload(videoUrl, videoId, videoName = '') {
			if (!videoUrl || !videoId) {
				return;
			}
			
			// 检查是否正在下载
			if (this.downloadingVideos[videoId]) {
				console.log('[VideoCallNew] 视频已在下载队列中');
				return;
			}
			
			console.log(`[VideoCallNew] 📥 开始后台下载视频: ${videoName || videoId}`, videoUrl);
			
			const downloadPromise = new Promise((resolve, reject) => {
				const downloadTask = uni.downloadFile({
					url: videoUrl,
					success: (res) => {
						if (res.statusCode === 200) {
							console.log('[VideoCallNew] 视频下载成功，临时路径:', res.tempFilePath);
							
							// 使用uni.saveFile保存到本地永久存储
							uni.saveFile({
								tempFilePath: res.tempFilePath,
								success: (saveRes) => {
									console.log(`[VideoCallNew] ✅ 视频保存成功 ${videoName || videoId}:`, saveRes.savedFilePath);
									
									// 更新缓存
									this.videoCache[videoId] = saveRes.savedFilePath;
									// 如果是当前选中的视频，更新localVideoUrl
									if (this.selectedVideoId === videoId) {
										this.localVideoUrl = saveRes.savedFilePath;
										console.log('[VideoCallNew] 🎯 当前视频缓存完成，已更新播放源');
										
										// 给用户一个轻量级提示（仅在通话中时显示）
										if (this.callStarted) {
											uni.showToast({
												title: '已切换到本地播放',
												icon: 'success',
												duration: 1500
											});
										}
									}
									this.saveVideoCache();
									
									// 更新进度状态
									if (this.cacheProgress[videoId]) {
										this.cacheProgress[videoId] = { progress: 100, status: 'completed' };
									}
									
									console.log('[VideoCallNew] 💾 缓存已更新，下次启动将直接使用');
									
									delete this.downloadingVideos[videoId];
									resolve(saveRes.savedFilePath);
								},
								fail: (err) => {
									console.error(`[VideoCallNew] 视频保存失败 ${videoName || videoId}:`, err);
									
									// 使用临时文件
									if (this.selectedVideoId === videoId) {
										this.localVideoUrl = res.tempFilePath;
									}
									
									// 标记为失败
									if (this.cacheProgress[videoId]) {
										this.cacheProgress[videoId] = { progress: 100, status: 'failed' };
									}
									
									delete this.downloadingVideos[videoId];
									resolve(res.tempFilePath);
								}
							});
						} else {
							console.error(`[VideoCallNew] 视频下载失败 ${videoName || videoId}，状态码:`, res.statusCode);
							
							// 标记为失败
							if (this.cacheProgress[videoId]) {
								this.cacheProgress[videoId] = { progress: 0, status: 'failed' };
							}
							
							delete this.downloadingVideos[videoId];
							reject(new Error('下载失败'));
						}
					},
					fail: (err) => {
						console.error(`[VideoCallNew] 视频下载失败 ${videoName || videoId}:`, err);
						
						// 标记为失败
						if (this.cacheProgress[videoId]) {
							this.cacheProgress[videoId] = { progress: 0, status: 'failed' };
						}
						
						delete this.downloadingVideos[videoId];
						reject(err);
					}
				});
				
				// 记录下载进度
				downloadTask.onProgressUpdate((res) => {
					const progress = res.progress || 0;
					if (this.cacheProgress[videoId]) {
						this.cacheProgress[videoId].progress = progress;
					}
					// 每20%打印一次日志
					if (progress % 20 === 0) {
						console.log(`[VideoCallNew] 下载进度 ${videoName || videoId}: ${progress}%`);
					}
				});
			});
			
			this.downloadingVideos[videoId] = downloadPromise;
			
			try {
				await downloadPromise;
			} catch (e) {
				console.error('[VideoCallNew] 下载视频异常:', e);
			}
		},
		
		// 检查文件是否存在
		async checkFileExists(filePath) {
			return new Promise((resolve) => {
				try {
					// 检查是否支持文件系统API
					if (typeof uni.getFileSystemManager !== 'function') {
						console.log('[VideoCallNew] 💡 当前平台不支持FileSystemManager，信任缓存记录');
						resolve(true);
						return;
					}
					
					const fs = uni.getFileSystemManager();
					fs.access({
						path: filePath,
						success: () => {
							console.log('[VideoCallNew] ✅ 文件存在验证通过');
							resolve(true);
						},
						fail: () => {
							console.warn('[VideoCallNew] ❌ 文件不存在');
							resolve(false);
						}
					});
				} catch (e) {
					console.warn('[VideoCallNew] ⚠️ 检查文件存在性异常:', e);
					// 出错时信任缓存记录，避免误删缓存
					console.log('[VideoCallNew] 💡 异常情况下信任缓存记录');
					resolve(true);
				}
			});
		},

		// 清理资源
		cleanup() {
			console.log('[VideoCallNew] 开始清理资源');
			
			if (this.durationTimer) {
				clearInterval(this.durationTimer);
				this.durationTimer = null;
			}
			if (this.processingTimeout) {
				clearTimeout(this.processingTimeout);
				this.processingTimeout = null;
			}
			if (this.stopFallbackTimer) {
				clearTimeout(this.stopFallbackTimer);
				this.stopFallbackTimer = null;
			}
			this.stopFallbackActive = false;
			this.stopFallbackRetries = 0;
			if (this.longPressTimer) {
				clearTimeout(this.longPressTimer);
				this.longPressTimer = null;
			}
			if (this.audioContext) {
				try {
					this.audioContext.stop();
					this.audioContext.destroy();
				} catch (e) {
					console.warn('[VideoCallNew] 清理音频上下文失败:', e);
				}
				this.audioContext = null;
			}
			if (this.recorderManager) {
				try {
					if (this.isRecording) {
						this.recorderManager.stop();
					}
				} catch (e) {
					console.warn('[VideoCallNew] 清理录音管理器失败:', e);
				}
				this.recorderManager = null;
			}
			if (this.vadMaxTimer) {
				clearTimeout(this.vadMaxTimer);
				this.vadMaxTimer = null;
			}
			if (this.vadWatchTimer) {
				clearInterval(this.vadWatchTimer);
				this.vadWatchTimer = null;
			}
			if (this.autoLoopTimer) {
				clearInterval(this.autoLoopTimer);
				this.autoLoopTimer = null;
			}
			
			console.log('[VideoCallNew] 资源清理完成');
		},

		// 格式化时间
		formatTime(timestamp) {
			if (!timestamp) return '';
			const date = new Date(timestamp);
			const year = date.getFullYear();
			const month = String(date.getMonth() + 1).padStart(2, '0');
			const day = String(date.getDate()).padStart(2, '0');
			const hours = String(date.getHours()).padStart(2, '0');
			const minutes = String(date.getMinutes()).padStart(2, '0');
			return `${year}-${month}-${day} ${hours}:${minutes}`;
		},
		
		// 支付相关方法
		handlePaymentClose() {
			this.paymentModalData.show = false;
			if (this._paymentReject) {
				this._paymentReject(new Error('用户取消支付'));
			}
		},
		
		async handlePaymentConfirm(paymentData) {
			await processPayment(this, paymentData);
		}
	}
};
</script>

<style lang="scss" scoped>
.video-call-container {
	min-height: 100vh;
	background: #FDF8F2;
	background-image: 
		radial-gradient(circle at 10% 20%, rgba(212, 185, 150, 0.1) 0%, transparent 20%),
		radial-gradient(circle at 90% 80%, rgba(109, 139, 139, 0.1) 0%, transparent 20%);
	display: flex;
	flex-direction: column;
}

/* 顶部导航 */
.header {
	background: linear-gradient(135deg, #8B7355 0%, #6D8B8B 100%);
	padding: 30upx;
	padding-top: calc(30upx + constant(safe-area-inset-top));
	padding-top: calc(30upx + env(safe-area-inset-top));
	display: flex;
	justify-content: space-between;
	align-items: center;
	box-shadow: 0 8upx 30upx rgba(0, 0, 0, 0.16);
}

.back-btn {
	font-size: 28upx;
	color: white;
	font-weight: 600;
	padding: 10upx;
}

.header-title {
	font-size: 32upx;
	font-weight: bold;
	color: white;
}

.placeholder {
	width: 100upx;
}

/* 选择视频阶段 */
.select-video-section {
	flex: 1;
	display: flex;
	flex-direction: column;
}

/* 加载状态 */
.loading-box {
	flex: 1;
	display: flex;
	justify-content: center;
	align-items: center;
}

.loading-text {
	font-size: 28rpx;
	color: #999;
}

/* 视频列表 */
.video-list {
	flex: 1;
	padding: 0 32rpx;
}

.list-header {
	padding: 40rpx 0 24rpx;
	display: flex;
	justify-content: space-between;
	align-items: center;
}

.list-title {
	font-size: 32rpx;
	font-weight: bold;
	color: #333;
}

.list-count {
	font-size: 24rpx;
	color: #999;
}

/* 视频项 */
.video-item {
	background: white;
	border-radius: 16rpx;
	padding: 24rpx;
	margin-bottom: 20rpx;
	display: flex;
	align-items: center;
	box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
	transition: all 0.3s;
}

.video-item:active {
	transform: scale(0.98);
	box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.12);
}

/* 封面 */
.item-cover {
	width: 120rpx;
	height: 160rpx;
	border-radius: 12rpx;
	overflow: hidden;
	background: #f5f5f5;
	flex-shrink: 0;
	margin-right: 24rpx;
}

.cover-img {
	width: 100%;
	height: 100%;
}

.cover-placeholder {
	width: 100%;
	height: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
	background: linear-gradient(135deg, #f5f5f5 0%, #e8e8e8 100%);
}

.placeholder-icon {
	font-size: 48rpx;
	opacity: 0.5;
}

/* 预加载视频（隐藏） */
.preload-video {
	position: absolute;
	top: 0;
	left: 0;
	width: 1px;
	height: 1px;
	opacity: 0;
	pointer-events: none;
	z-index: -1;
}

/* 信息 */
.item-info {
	flex: 1;
	display: flex;
	flex-direction: column;
	justify-content: center;
	min-width: 0;
	margin-right: 16rpx;
}

.info-name-row {
	display: flex;
	align-items: center;
	gap: 12rpx;
	margin-bottom: 12rpx;
}

.info-name {
	font-size: 30rpx;
	font-weight: 600;
	color: #333;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	flex-shrink: 1;
}

.cache-badge {
	flex-shrink: 0;
}

.cache-badge-text {
	font-size: 20rpx;
	padding: 4rpx 12rpx;
	border-radius: 20rpx;
	white-space: nowrap;
}

.cache-badge-text.completed {
	background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%);
	color: #2e7d32;
	font-weight: 500;
}

.cache-badge-text.downloading {
	background: linear-gradient(135deg, #fff3e0 0%, #ffe0b2 100%);
	color: #e65100;
	font-weight: 500;
}

.info-text {
	font-size: 24rpx;
	color: #999;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* 操作按钮 */
.item-action {
	flex-shrink: 0;
}

.call-icon {
	width: 88rpx;
	height: 88rpx;
	background: linear-gradient(135deg, #07c160 0%, #06ae56 100%);
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	box-shadow: 0 4rpx 12rpx rgba(7, 193, 96, 0.3);
}

.icon-text {
	font-size: 40rpx;
}

/* 空状态 */
.empty-box {
	flex: 1;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	padding: 100rpx 40rpx;
}

.empty-icon {
	font-size: 120rpx;
	margin-bottom: 32rpx;
}

.empty-text {
	font-size: 32rpx;
	color: #666;
	margin-bottom: 16rpx;
}

.empty-hint {
	font-size: 26rpx;
	color: #999;
	margin-bottom: 40rpx;
}

.go-revival-btn {
	padding: 24rpx 48rpx;
	background: linear-gradient(135deg, #8B7355 0%, #6D8B8B 100%);
	color: white;
	border: none;
	border-radius: 40rpx;
	font-size: 28rpx;
	font-weight: bold;
	box-shadow: 0 8rpx 16rpx rgba(139, 115, 85, 0.3);
}

/* 通话中阶段 */
.call-active-section {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background: #000;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: flex-start;
	padding: 0;
	overflow: hidden;
	z-index: 1;
}

/* 视频容器 */
.video-fullscreen {
	position: fixed;
	top: 220rpx;
	left: 40rpx;
	right: 40rpx;
	bottom: calc(320rpx + 40rpx + constant(safe-area-inset-bottom));
	bottom: calc(320rpx + 40rpx + env(safe-area-inset-bottom));
	border-radius: 32rpx;
	overflow: hidden;
	background: #000;
	box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.5);
	z-index: 1;
}

.video-player {
	width: 100%;
	height: 100%;
	background: #000;
	border-radius: 32rpx;
	object-fit: cover;
	position: relative;
	z-index: 1;
	pointer-events: none;
}

.video-placeholder {
	width: 100%;
	height: 100%;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);
}

.placeholder-text {
	font-size: 28rpx;
	color: rgba(255, 255, 255, 0.6);
	margin-top: 20rpx;
}

/* 顶部状态栏 */
.call-status-bar {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	padding: 80rpx 40rpx 40rpx;
	background: linear-gradient(180deg, rgba(0, 0, 0, 0.7) 0%, transparent 100%);
	z-index: 1000;
	pointer-events: none;
}

.call-status-bar .listening-chip {
	pointer-events: auto;
}

.status-info {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 8rpx;
}

.call-name {
	font-size: 32rpx;
	font-weight: 600;
	color: white;
	text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.5);
}

.call-time {
	font-size: 26rpx;
	color: rgba(255, 255, 255, 0.9);
	font-family: 'Courier New', monospace;
	text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.5);
}

.listening-chip {
	margin-top: 12rpx;
	padding: 18rpx 28rpx;
	background: linear-gradient(135deg, #FFB6C1 0%, #FFC0CB 50%, #FFD4E5 100%);
	border-radius: 50rpx;
	border: 2rpx solid rgba(255, 255, 255, 0.8);
	box-shadow: 0 8rpx 24rpx rgba(255, 182, 193, 0.4), 0 0 20rpx rgba(255, 192, 203, 0.3);
	display: inline-flex;
	align-items: center;
	gap: 12rpx;
	transition: all 0.3s ease;
	animation: listeningGlow 1.5s ease-in-out infinite, listeningPulse 2s ease-in-out infinite;
	position: relative;
	z-index: 1001;
	pointer-events: auto;
	opacity: 1;
	visibility: visible;
}

.listening-chip.inline-chip {
	margin-top: 0;
}

.listening-icon {
	font-size: 32rpx;
	animation: iconBounce 1s ease-in-out infinite;
	filter: drop-shadow(0 2rpx 4rpx rgba(0, 0, 0, 0.3));
}

.listening-text {
	font-size: 28rpx;
	color: white;
	font-weight: 700;
	letter-spacing: 1rpx;
	text-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.4), 0 0 10rpx rgba(255, 255, 255, 0.3);
}

/* 对话历史 */
.chat-section {
	flex: 1;
	background: white;
	border-radius: 24rpx;
	padding: 30rpx;
	box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
	min-height: 150rpx;
}

.chat-messages {
	height: 100%;
}

.message-item {
	display: flex;
	margin-bottom: 20rpx;
	animation: fadeIn 0.3s;
}

.message-item.user {
	justify-content: flex-end;
}

.message-item.ai {
	justify-content: flex-start;
}

.message-bubble {
	max-width: 70%;
	padding: 20rpx 24rpx;
	border-radius: 16rpx;
}

.message-item.user .message-bubble {
	background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
	border-bottom-right-radius: 4rpx;
}

.message-item.ai .message-bubble {
	background: #f0f0f0;
	border-bottom-left-radius: 4rpx;
}

.message-text {
	font-size: 28rpx;
	line-height: 1.5;
	color: white;
}

.message-item.ai .message-text {
	color: #333;
}

.empty-chat {
	height: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
}

.empty-chat-text {
	font-size: 26rpx;
	color: #999;
}

/* 底部控制区 */
.call-controls {
	position: fixed;
	bottom: 0;
	left: 0;
	right: 0;
	height: 320rpx;
	padding: 40rpx 60rpx calc(80rpx + constant(safe-area-inset-bottom));
	padding: 40rpx 60rpx calc(80rpx + env(safe-area-inset-bottom));
	background: linear-gradient(0deg, rgba(0, 0, 0, 0.95) 0%, rgba(0, 0, 0, 0.6) 60%, transparent 100%);
	z-index: 1001;
	pointer-events: none;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
}

/* 录音提示 */
.recording-hint {
	text-align: center;
	margin-bottom: 30rpx;
}

.hint-box {
	display: inline-flex;
	align-items: center;
	gap: 12rpx;
	padding: 16rpx 32rpx;
	border-radius: 50rpx;
	box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.4);
	backdrop-filter: blur(10rpx);
}

.hint-box.listening-pulse {
	background: linear-gradient(135deg, rgba(7, 193, 96, 0.9) 0%, rgba(6, 174, 86, 0.9) 100%);
	animation: listeningPulse 1.5s ease-in-out infinite;
}

.hint-box.processing-pulse {
	background: linear-gradient(135deg, rgba(255, 193, 7, 0.9) 0%, rgba(255, 152, 0, 0.9) 100%);
	animation: processingPulse 1.5s ease-in-out infinite;
}

.hint-icon {
	font-size: 32rpx;
	animation: iconBounce 1s ease-in-out infinite;
}

.hint-text {
	font-size: 28rpx;
	color: white;
	font-weight: 600;
	text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.3);
}

/* 控制按钮组 */
.control-buttons {
	display: flex;
	align-items: flex-end;
	justify-content: space-around;
	gap: 40rpx;
	position: relative;
	z-index: 1002;
	pointer-events: auto;
}

.control-btn {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 12rpx;
	cursor: pointer;
	user-select: none;
	-webkit-user-select: none;
	touch-action: manipulation;
	position: relative;
	z-index: 1003;
	pointer-events: auto;
}

.btn-circle {
	width: 100rpx;
	height: 100rpx;
	border-radius: 50%;
	background: rgba(255, 255, 255, 0.95);
	backdrop-filter: blur(20rpx);
	display: flex;
	align-items: center;
	justify-content: center;
	transition: all 0.3s;
	box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.5), 0 0 0 4rpx rgba(255, 255, 255, 0.3);
	position: relative;
	z-index: 1004;
}

.btn-circle.large {
	width: 130rpx;
	height: 130rpx;
	background: rgba(255, 255, 255, 0.95);
}

.btn-circle.recording {
	background: rgba(235, 51, 73, 0.95);
	animation: recordingPulse 1s ease-in-out infinite;
}

.btn-circle.processing {
	background: rgba(200, 200, 200, 0.95);
}

/* 新的说话按钮设计 - 椭圆形状 */
.speak-button {
	position: relative;
	width: 240rpx;
	height: 160rpx;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
	z-index: 1005;
}

.speak-button-inner {
	width: 240rpx;
	height: 160rpx;
	border-radius: 80rpx;
	background: linear-gradient(135deg, #FF6B6B 0%, #FFE66D 100%);
	display: flex;
	align-items: center;
	justify-content: center;
	box-shadow: 0 8rpx 32rpx rgba(255, 107, 107, 0.6),
	            0 4rpx 16rpx rgba(255, 230, 109, 0.5),
	            0 0 0 6rpx rgba(255, 255, 255, 0.3);
	transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
	position: relative;
	z-index: 1006;
}

.speak-button.recording .speak-button-inner {
	background: linear-gradient(135deg, #4ECDC4 0%, #44A08D 100%);
	box-shadow: 0 12rpx 48rpx rgba(78, 205, 196, 0.6),
	            0 6rpx 24rpx rgba(68, 160, 141, 0.5),
	            0 0 0 6rpx rgba(255, 255, 255, 0.3);
	transform: scale(1.05);
	width: 260rpx;
	height: 180rpx;
}

.speak-button.processing .speak-button-inner {
	background: linear-gradient(135deg, #a8a8a8 0%, #7a7a7a 100%);
	box-shadow: 0 8rpx 32rpx rgba(168, 168, 168, 0.3);
}

/* 录音时的外圈动画 - 椭圆形状 */
.speak-ring {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	width: 240rpx;
	height: 160rpx;
	border-radius: 80rpx;
	border: 4rpx solid rgba(78, 205, 196, 0.6);
	animation: speakRingPulse 1.5s ease-out infinite;
	z-index: 1;
}

.speak-ring-outer {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	width: 240rpx;
	height: 160rpx;
	border-radius: 80rpx;
	border: 3rpx solid rgba(68, 160, 141, 0.5);
	animation: speakRingPulse 1.5s ease-out infinite 0.3s;
	z-index: 0;
}

@keyframes speakRingPulse {
	0% {
		width: 240rpx;
		height: 160rpx;
		opacity: 1;
	}
	100% {
		width: 320rpx;
		height: 220rpx;
		opacity: 0;
	}
}

.btn-circle.end-call {
	background: rgba(235, 51, 73, 0.98);
	box-shadow: 0 8rpx 24rpx rgba(235, 51, 73, 0.6), 0 0 0 4rpx rgba(255, 255, 255, 0.3);
}

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

.btn-icon {
	font-size: 40rpx;
}

.btn-icon.large {
	font-size: 52rpx;
}

.btn-icon.pulse {
	animation: pulse 1s infinite;
}

.btn-label {
	font-size: 22rpx;
	color: rgba(255, 255, 255, 0.98);
	font-weight: 600;
	text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.6), 0 0 20rpx rgba(0, 0, 0, 0.4);
}

.btn-label-new {
	font-size: 24rpx;
	color: rgba(255, 255, 255, 0.95);
	font-weight: 600;
	text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.4);
	transition: all 0.3s;
	margin-top: 8rpx;
}

.btn-label-new.active {
	color: #4ECDC4;
	text-shadow: 0 2rpx 8rpx rgba(78, 205, 196, 0.6);
	transform: scale(1.05);
}

/* 图标样式 */
.icon-delete {
	width: 40rpx;
	height: 40rpx;
	position: relative;
}

.icon-delete::before,
.icon-delete::after {
	content: '';
	position: absolute;
	left: 50%;
	top: 50%;
	width: 32rpx;
	height: 4rpx;
	background: #333;
	border-radius: 2rpx;
	box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
}

.icon-delete::before {
	transform: translate(-50%, -50%) rotate(45deg);
}

.icon-delete::after {
	transform: translate(-50%, -50%) rotate(-45deg);
}

.icon-mic {
	width: 40rpx;
	height: 52rpx;
	border: 4rpx solid #333;
	border-radius: 20rpx 20rpx 20rpx 20rpx;
	position: relative;
}

.icon-mic::after {
	content: '';
	position: absolute;
	bottom: -12rpx;
	left: 50%;
	transform: translateX(-50%);
	width: 30rpx;
	height: 12rpx;
	border: 4rpx solid #333;
	border-top: none;
	border-radius: 0 0 15rpx 15rpx;
}

.icon-mic::before {
	content: '';
	position: absolute;
	bottom: -20rpx;
	left: 50%;
	transform: translateX(-50%);
	width: 3rpx;
	height: 8rpx;
	background: #333;
}

/* 新的麦克风图标 */
.icon-mic-new {
	width: 48rpx;
	height: 64rpx;
	border: 5rpx solid white;
	border-radius: 24rpx 24rpx 24rpx 24rpx;
	position: relative;
	animation: micFloat 2s ease-in-out infinite;
}

.icon-mic-new::after {
	content: '';
	position: absolute;
	bottom: -16rpx;
	left: 50%;
	transform: translateX(-50%);
	width: 36rpx;
	height: 16rpx;
	border: 5rpx solid white;
	border-top: none;
	border-radius: 0 0 18rpx 18rpx;
}

.icon-mic-new::before {
	content: '';
	position: absolute;
	bottom: -26rpx;
	left: 50%;
	transform: translateX(-50%);
	width: 4rpx;
	height: 10rpx;
	background: white;
}

@keyframes micFloat {
	0%, 100% {
		transform: translateY(0);
	}
	50% {
		transform: translateY(-6rpx);
	}
}

.icon-recording {
	width: 48rpx;
	height: 48rpx;
	background: white;
	border-radius: 50%;
	position: relative;
}

.icon-recording::after {
	content: '';
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	width: 32rpx;
	height: 32rpx;
	background: #eb3349;
	border-radius: 50%;
}

/* 录音时的波形动画 */
.icon-wave-container {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 6rpx;
	height: 60rpx;
}

.wave-bar {
	width: 6rpx;
	background: white;
	border-radius: 3rpx;
	animation: waveAnimation 1s ease-in-out infinite;
}

@keyframes waveAnimation {
	0%, 100% {
		height: 20rpx;
	}
	50% {
		height: 50rpx;
	}
}

.icon-loading {
	width: 48rpx;
	height: 48rpx;
	border: 4rpx solid rgba(255, 255, 255, 0.3);
	border-top-color: #666;
	border-radius: 50%;
	animation: spin 1s linear infinite;
}

.icon-loading-new {
	width: 56rpx;
	height: 56rpx;
	border: 5rpx solid rgba(255, 255, 255, 0.2);
	border-top-color: white;
	border-right-color: white;
	border-radius: 50%;
	animation: spin 0.8s linear infinite;
}

.icon-hangup {
	width: 48rpx;
	height: 20rpx;
	border: 5rpx solid white;
	border-radius: 12rpx;
	position: relative;
	transform: rotate(-135deg);
	filter: drop-shadow(0 2rpx 4rpx rgba(0, 0, 0, 0.3));
}

.icon-hangup::before {
	content: '';
	position: absolute;
	right: -8rpx;
	top: -12rpx;
	width: 16rpx;
	height: 16rpx;
	border: 5rpx solid white;
	border-left: none;
	border-bottom: none;
	border-radius: 0 8rpx 0 0;
}

@keyframes spin {
	from {
		transform: rotate(0deg);
	}
	to {
		transform: rotate(360deg);
	}
}

@keyframes listeningGlow {
	0%, 100% {
		box-shadow: 0 8rpx 24rpx rgba(255, 182, 193, 0.4), 0 0 20rpx rgba(255, 192, 203, 0.3);
		border-color: rgba(255, 255, 255, 0.8);
	}
	50% {
		box-shadow: 0 12rpx 36rpx rgba(255, 182, 193, 0.6), 0 0 40rpx rgba(255, 192, 203, 0.5), 0 0 60rpx rgba(255, 212, 229, 0.4);
		border-color: rgba(255, 255, 255, 1);
	}
}

@keyframes listeningPulse {
	0%, 100% {
		transform: scale(1);
	}
	50% {
		transform: scale(1.05);
	}
}

@keyframes iconBounce {
	0%, 100% {
		transform: translateY(0);
	}
	50% {
		transform: translateY(-4rpx);
	}
}

/* 设置弹窗样式 */
.settings-dialog-mask {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background: rgba(0, 0, 0, 0.6);
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 9999;
	padding: 40rpx;
}

.settings-dialog {
	width: 92vw;
	max-width: 640rpx;
	max-height: 80vh;
	background: white;
	border-radius: 32rpx;
	overflow: hidden;
	box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.3);
	display: flex;
	flex-direction: column;
	box-sizing: border-box;
}

.dialog-header {
	padding: 40rpx 40rpx 30rpx;
	background: linear-gradient(135deg, #8B7355 0%, #6D8B8B 100%);
	display: flex;
	justify-content: space-between;
	align-items: center;
	border-bottom: 1rpx solid rgba(255, 255, 255, 0.1);
}

.dialog-title {
	font-size: 36rpx;
	font-weight: bold;
	color: white;
}

.dialog-close {
	font-size: 40rpx;
	color: white;
	opacity: 0.9;
	padding: 0 10rpx;
	font-weight: 300;
}

.dialog-content {
	flex: 1;
	overflow-y: auto;
	padding: 32rpx;
	box-sizing: border-box;
}

.dialog-section {
	margin-bottom: 30rpx;
}

.section-label {
	font-size: 30rpx;
	font-weight: bold;
	color: #333;
	margin-bottom: 12rpx;
}

.section-hint {
	font-size: 24rpx;
	color: #999;
	margin-bottom: 24rpx;
	display: block;
	line-height: 1.6;
}

.memory-item {
	margin-bottom: 24rpx;
	width: 100%;
}

.memory-label {
	font-size: 26rpx;
	color: #666;
	margin-bottom: 12rpx;
	display: block;
	font-weight: 500;
}

.memory-input {
	width: 100%;
	padding: 28rpx 24rpx;
	min-height: 88rpx;
	background: #FDF8F2;
	border-radius: 16rpx;
	font-size: 28rpx;
	border: 2rpx solid #e8dcc8;
	transition: all 0.3s;
	box-sizing: border-box;
	max-width: 100%;
}

.memory-input:focus {
	border-color: #8B7355;
	background: white;
}

.memory-textarea {
	width: 100%;
	min-height: 140rpx;
	max-height: none;
	padding: 24rpx;
	background: #FDF8F2;
	border-radius: 16rpx;
	font-size: 28rpx;
	border: 2rpx solid #e8dcc8;
	line-height: 1.6;
	transition: all 0.3s;
	box-sizing: border-box;
	max-width: 100%;
}

.memory-textarea:focus {
	border-color: #8B7355;
	background: white;
}

.dialog-footer {
	padding: 30rpx 40rpx;
	padding-bottom: calc(30rpx + env(safe-area-inset-bottom));
	display: flex;
	gap: 20rpx;
	border-top: 1rpx solid #f0f0f0;
	background: #fafafa;
}

.dialog-btn {
	flex: 1;
	height: 88rpx;
	border: none;
	border-radius: 44rpx;
	font-size: 30rpx;
	font-weight: 600;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: all 0.3s;
}

.dialog-btn.cancel {
	background: #f5f5f5;
	color: #666;
}

.dialog-btn.cancel:active {
	background: #e0e0e0;
}

.dialog-btn.confirm {
	background: linear-gradient(135deg, #8B7355 0%, #6D8B8B 100%);
	color: white;
	box-shadow: 0 8rpx 20rpx rgba(139, 115, 85, 0.3);
}

.dialog-btn.confirm:active {
	transform: translateY(-2rpx);
	box-shadow: 0 12rpx 28rpx rgba(139, 115, 85, 0.4);
}

/* 动画 */
@keyframes fadeIn {
	from {
		opacity: 0;
		transform: translateY(20rpx);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

@keyframes pulse {
	0%, 100% {
		transform: scale(1);
	}
	50% {
		transform: scale(1.1);
	}
}

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

@keyframes recordingPulse {
	0%, 100% {
		transform: scale(1);
		box-shadow: 0 8rpx 16rpx rgba(235, 51, 73, 0.3);
	}
	50% {
		transform: scale(1.02);
		box-shadow: 0 12rpx 24rpx rgba(235, 51, 73, 0.5),
		            0 0 0 8rpx rgba(235, 51, 73, 0.2);
	}
}

/* 接通中遮罩层 */
.connecting-overlay {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 100;
	display: flex;
	align-items: center;
	justify-content: center;
	animation: fadeIn 0.3s ease;
}

/* 模糊背景 */
.connecting-bg {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	overflow: hidden;
}

.bg-avatar {
	width: 100%;
	height: 100%;
	filter: blur(40rpx) brightness(0.4);
	transform: scale(1.2);
}

/* 接通中内容 */
.connecting-content {
	position: relative;
	z-index: 1;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 40rpx;
	padding: 60rpx;
}

/* 头像容器 */
.avatar-container {
	width: 240rpx;
	height: 240rpx;
	border-radius: 50%;
	overflow: hidden;
	background: rgba(255, 255, 255, 0.1);
	border: 6rpx solid rgba(255, 255, 255, 0.3);
	box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.5);
	animation: avatarPulse 2s ease-in-out infinite;
}

.avatar-img {
	width: 100%;
	height: 100%;
}

.avatar-placeholder {
	width: 100%;
	height: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 80rpx;
	background: linear-gradient(135deg, #8B7355 0%, #6D8B8B 100%);
}

/* 名称 */
.connecting-name {
	font-size: 40rpx;
	font-weight: 600;
	color: white;
	text-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.5);
}

/* 状态区域 */
.connecting-status {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 20rpx;
}

/* 状态点动画 */
.status-dots {
	display: flex;
	gap: 12rpx;
	align-items: center;
}

.dot {
	width: 12rpx;
	height: 12rpx;
	border-radius: 50%;
	background: white;
	animation: dotBounce 1.4s ease-in-out infinite;
}

.dot-1 {
	animation-delay: 0s;
}

.dot-2 {
	animation-delay: 0.2s;
}

.dot-3 {
	animation-delay: 0.4s;
}

.status-text {
	font-size: 32rpx;
	color: rgba(255, 255, 255, 0.9);
	text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.5);
}

/* 隐藏视频 */
.video-hidden {
	opacity: 0;
	pointer-events: none;
}

/* 头像脉冲动画 */
@keyframes avatarPulse {
	0%, 100% {
		transform: scale(1);
		box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.5);
	}
	50% {
		transform: scale(1.05);
		box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.5),
		            0 0 0 20rpx rgba(255, 255, 255, 0.1);
	}
}

/* AI生成提示标签 */
.ai-tag {
	position: absolute;
	top: 100rpx;
	right: 24rpx;
	z-index: 100;
	background: rgba(0, 0, 0, 0.6);
	padding: 8rpx 20rpx;
	border-radius: 30rpx;
	backdrop-filter: blur(10rpx);
}

.ai-tag-text {
	font-size: 22rpx;
	color: #fff;
	font-weight: 500;
}

/* 点跳动动画 */
@keyframes dotBounce {
	0%, 80%, 100% {
		transform: translateY(0);
		opacity: 1;
	}
	40% {
		transform: translateY(-16rpx);
		opacity: 0.7;
	}
}

/* #ifdef MP-WEIXIN */
.video-fullscreen {
	box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.35);
}

.call-controls {
	backdrop-filter: none;
}

.hint-box {
	backdrop-filter: none;
	box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.35);
}

.btn-circle {
	backdrop-filter: none;
	box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.4);
}

.speak-button-inner {
	box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.35);
}

.listening-chip {
	box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.35);
}

.listening-icon {
	filter: none;
}

.bg-avatar {
	filter: blur(16rpx) brightness(0.4);
}
/* #endif */
</style>
