<template>
	<view>
		<view class="body">
			<uni-nav-bar fixed statusBar left-icon="left" background-color="transparent" :border="false" @clickLeft="back"
				color="#ffffff"></uni-nav-bar>
			<image class="back"
				:src="loverBasicList.image_url ? loverBasicList.image_url : 'https://nvlovers.oss-cn-qingdao.aliyuncs.com/uploads/20251226/39c6f8899c15f60fc59207835f95e07a.png'"
				mode="aspectFill"></image>
		</view>
		<!-- 麦克风权限开关移到右上角 -->
		<view class="mic-permission-switch" @click="toggleMicPermission">
			<text class="mic-emoji">{{ micEnabled ? '🎤' : '🔇' }}</text>
		</view>
		<view class="header">
			<view class="header_content">
				<view class="header_time">{{ isVip ? 'VIP 无限通话' : '通话剩余时间' }}</view>
				<view class="header_module fa sb" v-if="!isVip">
					<view class="header_title faj">{{ formatTime(remainingTime).hours }}</view>
					<view class="header_title faj">{{ formatTime(remainingTime).minutes }}</view>
					<view class="header_title faj">{{ formatTime(remainingTime).seconds }}</view>
				</view>
				<view class="header_recharge faj" @click="goRecharge">充值<image src="/static/images/phone_more.png" mode="widthFix"></image>
				</view>
			</view>
		</view>
		<!-- 替换为动态脑电波效果 -->
		<view class="dynamic-wave">
			<view class="wave-bar" v-for="n in 15" :key="n" :style="{ 'animation-delay': n * 0.1 + 's' }"></view>
		</view>
		<view class="opt fa">
			<view @click="hangUp()" class="opt_item">
				<image class="opt_image" src="/static/images/phone_a2.png" mode="widthFix"></image>
				<view class="opt_name">挂断</view>
			</view>
			<!-- 按住说话按钮 -->
			<view class="opt_item mic-button" 
				@touchstart.stop.prevent="startTalking" 
				@touchend.stop.prevent="stopTalking" 
				@touchcancel.stop.prevent="stopTalking"
				@click="testClick"
				:class="{ 'talking': isTalking }">
				<image class="opt_image" src="/static/images/phone_a1.png" mode="widthFix"></image>
				<view class="opt_name">{{ isTalking ? '松开结束' : '按住说话' }}</view>
			</view>
		</view>
		<view class="black"></view>
	</view>
</template>

<script>
	import {

	} from '@/utils/api.js'
	import notHave from '@/components/not-have.vue';
	import { baseURLPy } from '@/utils/request.js'
	import topSafety from '@/components/top-safety.vue';
	
	// 在组件外部初始化 recorderManager（所有平台统一使用）
	let recorderManager = null;
	
	export default {
		components: {
			notHave,
			topSafety,
		},
		beforeCreate() {
			console.log('beforeCreate', )
			// const domModule = uni.requireNativePlugin('io.dcolud.audio.recode')
			// console.log('beforeCreate',domModule)
		},
		data() {
			return {
				loverBasicList: uni.getStorageSync('loverBasicList'),
				socketTask: null,
				isRecording: false,
				status: 'Ready',
				audioContext: null,
				audioData: [],
				totalDuration: 300000, // 默认 5 分钟
				remainingTime: 300000,
				timer: null,
				isVip: false,
				isTalking: false, // 是否正在说话
				micEnabled: true, // 麦克风是否开启
				isReconnecting: false, // 是否正在重连
				recordStartTime: null // 录音开始时间
			}
		},
		onLoad() {
			// 检测平台
			const systemInfo = uni.getSystemInfoSync()
			console.log('systemInfo', systemInfo)
			
			// 检查录音权限
			this.checkRecordPermission()
			
			// 所有平台统一使用 uni.getRecorderManager()
			recorderManager = uni.getRecorderManager();
			console.log('✅ recorderManager 初始化完成')
			
			// 设置录音监听器（只设置一次）
			this.setupRecorderListeners()
			
			this.getCallDuration()
			this.initAudio()
		},
		onUnload() {
			this.stopCall()
			if (this.timer) {
				clearInterval(this.timer)
			}
		},
		methods: {
			// 检查录音权限
			checkRecordPermission() {
				// #ifdef APP-PLUS
				console.log('📱 检查 Android 录音权限...')
				
				// 检查录音权限
				const permissions = ['android.permission.RECORD_AUDIO']
				plus.android.requestPermissions(permissions, (result) => {
					console.log('📱 录音权限检查结果:', result)
					
					if (result.deniedAlways && result.deniedAlways.length > 0) {
						console.error('❌ 录音权限被永久拒绝')
						uni.showModal({
							title: '权限不足',
							content: '录音权限被拒绝，请在设置中手动开启录音权限',
							showCancel: false
						})
					} else if (result.denied && result.denied.length > 0) {
						console.warn('⚠️ 录音权限被临时拒绝')
						uni.showToast({
							title: '需要录音权限才能使用语音功能',
							icon: 'none',
							duration: 3000
						})
					} else {
						console.log('✅ 录音权限已获取')
					}
				}, (error) => {
					console.error('❌ 权限检查失败:', error)
				})
				// #endif
				
				// #ifndef APP-PLUS
				console.log('📱 非 APP 平台，跳过权限检查')
				// #endif
			},
			getCallDuration() {
				uni.request({
					url: baseURLPy + '/voice/call/duration',
					method: 'GET',
					header: {
						'Authorization': 'Bearer ' + uni.getStorageSync("token")
					},
					success: (res) => {
						console.log('通话时长配置:', res.data)
						if (res.data.code === 1 && res.data.data) {
							const duration = res.data.data.duration
							this.isVip = res.data.data.is_vip
							if (duration > 0) {
								this.totalDuration = duration
								this.remainingTime = duration
							} else {
								// VIP 用户无限制，设置为 24 小时
								this.totalDuration = 24 * 60 * 60 * 1000
								this.remainingTime = 24 * 60 * 60 * 1000
							}
						}
						this.connectWebSocket()
					},
					fail: (err) => {
						console.error('获取通话时长失败:', err)
						// 失败时使用默认值
						this.connectWebSocket()
					}
				})
			},
			formatTime(ms) {
				const totalSeconds = Math.floor(ms / 1000)
				const hours = Math.floor(totalSeconds / 3600)
				const minutes = Math.floor((totalSeconds % 3600) / 60)
				const seconds = totalSeconds % 60
				return {
					hours: String(hours).padStart(2, '0'),
					minutes: String(minutes).padStart(2, '0'),
					seconds: String(seconds).padStart(2, '0')
				}
			},
			startTimer() {
				if (this.timer) {
					clearInterval(this.timer)
				}
				this.timer = setInterval(() => {
					this.remainingTime -= 1000
					if (this.remainingTime <= 0) {
						clearInterval(this.timer)
						uni.showToast({
							title: '通话时间已用完',
							icon: 'none'
						})
						setTimeout(() => {
							uni.navigateBack()
						}, 1500)
					} else if (this.remainingTime === 60000) {
						// 剩余 1 分钟提示
						uni.showToast({
							title: '剩余 1 分钟',
							icon: 'none',
							duration: 2000
						})
					}
				}, 1000)
			},
			hangUp() {
				uni.showModal({
					title: '提示',
					content: '挂断改通话',
					success: function(res) {
						if (res.confirm) {
							uni.navigateBack();
						} else if (res.cancel) {
							console.log('用户点击取消');
						}
					}
				});
			},
			initAudio() {
				// 销毁旧的音频上下文（如果存在）
				if (this.audioContext) {
					this.audioContext.destroy();
				}
				// 创建新的音频上下文
				this.audioContext = uni.createInnerAudioContext()
				this.audioContext.volume = 1
				// 设置音频源类型
				this.audioContext.srcType = 'auto'
				// 添加音频事件监听
				this.audioContext.onError((err) => {
					console.error('音频播放失败:', err)
					console.error('错误详情:', JSON.stringify(err))
					uni.showToast({
						title: '音频播放失败',
						icon: 'none'
					})
				})
				this.audioContext.onPlay(() => {
					console.log('音频开始播放')
				})
				this.audioContext.onEnded(() => {
					console.log('音频播放结束')
				})
				this.audioContext.onCanplay(() => {
					console.log('音频可以播放了')
				})
			},
			connectWebSocket() {
				// 如果已经有连接且状态正常，不重复连接
				if (this.socketTask && this.socketTask.readyState === 1) {
					console.log('⚠️ WebSocket 已连接，跳过重复连接')
					return
				}
				
				// 如果正在连接中，不重复连接
				if (this.socketTask && this.socketTask.readyState === 0) {
					console.log('⚠️ WebSocket 正在连接中，请稍候...')
					return
				}
				
				// 关闭旧连接
				if (this.socketTask) {
					console.log('🔌 关闭旧的 WebSocket 连接...')
					try {
						this.socketTask.close()
					} catch (e) {
						console.error('关闭旧连接失败:', e)
					}
					this.socketTask = null
				}
				
				// 根据 baseURLPy 构建 WebSocket URL
				let wsUrl = baseURLPy.replace('http://', 'ws://').replace('https://', 'wss://') + '/voice/call'
				console.log('🔗 WebSocket URL:', wsUrl)
				
				this.socketTask = uni.connectSocket({
					url: wsUrl,
					header: {
						"content-type": "application/json",
						'Authorization': 'Bearer ' + uni.getStorageSync("token") || ""
					},
					success: () => console.log('WS 连接成功')
				});
				this.socketTask.onOpen((res) => {
					console.log('WebSocket onOpen:', res)
					// 不要在这里自动开始录音，等用户按住按钮时再开始
					// this.startRecording();
					this.startTimer();
				});
				this.socketTask.onMessage((res) => {
					console.log('📨 收到服务器消息, 数据类型:', typeof res.data)
					console.log('📨 消息内容:', res.data)
					this.handleServerMessage(res.data);
				});
				this.socketTask.onError((err) => {
					console.error('❌ WS 错误', err);
					console.error('错误详情:', JSON.stringify(err));
					
					// 尝试重连
					if (err.errMsg && err.errMsg.includes('timeout')) {
						console.log('⚠️ WebSocket 超时，尝试重连...')
						setTimeout(() => {
							this.connectWebSocket()
						}, 2000)
					}
				});
				this.socketTask.onClose((res) => {
					console.log('❌ WebSocket 关闭, code:', res.code, 'reason:', res.reason)
					
					if (res.code !== 1000) {
						console.error('⚠️ 非正常关闭')
					}
					
					if (this.isRecording && recorderManager) {
						console.log('关闭录音')
						recorderManager.stop()
					}
					
					// 自动重连（避免重复重连）
					if (!this.isReconnecting) {
						this.isReconnecting = true
						console.log('⚠️ WebSocket 已关闭，3秒后自动重连...')
						setTimeout(() => {
							console.log('🔄 尝试重新连接 WebSocket...')
							this.isReconnecting = false
							this.connectWebSocket()
						}, 3000)
					}
				})
			},
			// 设置录音监听器（只在初始化时调用一次）
			setupRecorderListeners() {
				if (!recorderManager) {
					console.error('❌ recorderManager 未初始化')
					return
				}
				
				console.log('📝 设置录音监听器...')
				
				// 监听录音开始
				recorderManager.onStart(() => {
					console.log('✅ 录音已开始')
					console.log('当前时间:', new Date().toLocaleTimeString())
				})
				
				// 监听音频帧 - 实时发送（关键！）
				let frameCount = 0
				let hasReceivedFrames = false // 标记是否收到过音频帧
				recorderManager.onFrameRecorded((res) => {
					frameCount++
					hasReceivedFrames = true
					const { frameBuffer, isLastFrame } = res
					
					console.log(`🎤 收到音频帧 #${frameCount}, isTalking:`, this.isTalking, 'frameBuffer size:', frameBuffer ? frameBuffer.byteLength : 'null')
					
					if (!frameBuffer) {
						console.error('❌ frameBuffer 为空！')
						return
					}
					
					// 只有在说话状态下才发送音频数据
					if (this.isTalking && this.socketTask && this.socketTask.readyState === 1) {
						console.log('✅ 发送音频帧到服务器, 帧号:', frameCount)
						this.socketTask.send({
							data: frameBuffer,
							success: () => {
								console.log('✅ 音频帧发送成功, 帧号:', frameCount)
							},
							fail: (err) => {
								console.error('❌ 音频帧发送失败:', err)
							}
						})
					} else {
						console.log('⏸️ 不发送音频帧 - isTalking:', this.isTalking)
					}
				})
				
				// 添加测试：在录音开始后检查是否收到音频帧
				setTimeout(() => {
					if (!hasReceivedFrames) {
						console.warn('⚠️ 警告：录音开始 2 秒后仍未收到音频帧，onFrameRecorded 可能不工作')
						console.warn('⚠️ 将使用备用方案：录音结束后发送完整文件')
					}
				}, 2000)
				
				// 监听录音错误
				recorderManager.onError((err) => {
					console.error('❌ 录音错误:', err)
					console.error('错误详情:', JSON.stringify(err))
					uni.showToast({
						title: '录音失败: ' + (err.errMsg || '未知错误'),
						icon: 'none'
					})
					this.isRecording = false
				})
				
				// 监听录音停止
				recorderManager.onStop((res) => {
					const stopTime = Date.now()
					const actualDuration = this.recordStartTime ? stopTime - this.recordStartTime : 0
					
					console.log('⏹️ 录音已停止')
					console.log('📅 录音停止时间:', new Date(stopTime).toLocaleTimeString())
					console.log('⏱️ 实际录音时长:', actualDuration, 'ms')
					console.log('📋 系统报告时长:', res.duration, 'ms')
					console.log('📦 文件大小:', res.fileSize, 'bytes')
					console.log('📁 文件路径:', res.tempFilePath)
					console.log('📊 是否收到过音频帧:', hasReceivedFrames)
					
					// 计算预期的录音时长
					if (res.fileSize && res.fileSize > 0) {
						// PCM 16kHz 单声道 16bit: 每秒 32000 字节
						const calculatedDuration = (res.fileSize / 32000) * 1000 // 转换为毫秒
						console.log('📊 根据文件大小计算的时长:', calculatedDuration.toFixed(0), 'ms')
						
						if (actualDuration > 1000) { // 只有当实际录音时长超过1秒时才比较
							const timeDiff = Math.abs(actualDuration - calculatedDuration)
							if (timeDiff > 500) {
								console.warn('⚠️ 录音数据丢失严重！')
								console.warn('⚠️ 实际录音时长:', actualDuration, 'ms')
								console.warn('⚠️ 系统报告时长:', res.duration, 'ms')
								console.warn('⚠️ 文件大小计算时长:', calculatedDuration.toFixed(0), 'ms')
								console.warn('⚠️ 数据丢失率:', ((actualDuration - calculatedDuration) / actualDuration * 100).toFixed(1), '%')
								console.warn('⚠️ 可能的原因：uni-app Android 录音 API 问题、设备性能限制、或系统录音限制')
								
								// 提示用户数据丢失问题
								uni.showToast({
									title: `录音数据丢失${((actualDuration - calculatedDuration) / actualDuration * 100).toFixed(0)}%，识别可能不准确`,
									icon: 'none',
									duration: 3000
								})
							}
						}
					}
					
					// 如果收到过音频帧，说明实时发送工作正常，只需发送结束信号
					if (hasReceivedFrames) {
						console.log('✅ 已通过实时音频帧发送，发送 ptt_off 信号')
						if (this.socketTask && this.socketTask.readyState === 1) {
							this.socketTask.send({
								data: 'ptt_off',
								success: () => {
									console.log('✅ ptt_off 信号发送成功')
								}
							})
						}
					} else {
						// 新的处理方式：不通过WebSocket，而是在onStop回调中通过HTTP发送
						console.log('⚠️ 未收到音频帧，将在 onStop 回调中通过 HTTP 发送到 ASR 端点')
						console.log('⚠️ 不发送 WebSocket 信号，避免触发旧的 finalize_asr 流程')
					}
				})
				
				// 录音停止监听器
				recorderManager.onStop((res) => {
					console.log('⏹️ 录音已停止')
					console.log('📅 录音停止时间:', new Date().toLocaleString())
					console.log('⏱️ 实际录音时长:', res.duration, 'ms')
					console.log('📋 系统报告时长:', res.duration, 'ms')
					console.log('📦 文件大小:', res.fileSize, 'bytes')
					console.log('📁 文件路径:', res.tempFilePath)
					console.log('📊 是否收到过音频帧:', hasReceivedFrames)
					
					this.isRecording = false
					
					// 处理录音文件
					if (!res.tempFilePath) {
						console.error('❌ 没有录音文件')
						hasReceivedFrames = false // 重置标记
						frameCount = 0 // 重置计数
						return
					}
					
					console.log('📁 开始处理录音文件:', res.tempFilePath)
					
					// 使用之前成功的文件读取方法
					let filePath = res.tempFilePath
					if (!filePath.startsWith('/') && !filePath.includes('://')) {
						if (typeof plus !== 'undefined' && plus.io) {
							filePath = plus.io.convertLocalFileSystemURL(filePath)
						}
					}
					
					console.log('📁 转换后文件路径:', filePath)
					const that = this
					
					if (typeof plus !== 'undefined' && plus.io) {
						plus.io.resolveLocalFileSystemURL(filePath, (entry) => {
							entry.file((file) => {
								const reader = new plus.io.FileReader()
								reader.onload = async (e) => {
									const dataUrl = e.target.result
									const base64 = dataUrl.split(',')[1]
									const binaryString = atob(base64)
									const bytes = new Uint8Array(binaryString.length)
									for (let i = 0; i < binaryString.length; i++) {
										bytes[i] = binaryString.charCodeAt(i)
									}
									
									console.log('✅ 文件读取成功，开始发送到ASR')
									console.log('📊 音频数据大小:', bytes.length, 'bytes')
									
									// 发送到ASR端点进行处理
									try {
										const response = await that.sendAudioToASR(bytes)
										console.log('✅ ASR处理成功:', response)
									} catch (error) {
										console.error('❌ ASR处理失败:', error)
									}
								}
								reader.readAsDataURL(file)
							}, (error) => {
								console.error('❌ 文件读取失败:', error)
							})
						}, (error) => {
							console.error('❌ 文件路径解析失败:', error)
						})
					} else {
						console.error('❌ plus.io 不可用')
					}
					
					hasReceivedFrames = false // 重置标记
					frameCount = 0 // 重置计数
				})
				
				console.log('✅ 所有录音监听器已设置')
			},
			// 切换麦克风权限开关
			toggleMicPermission() {
				this.micEnabled = !this.micEnabled
				if (this.micEnabled) {
					uni.showToast({
						title: '麦克风已开启',
						icon: 'none'
					})
				} else {
					uni.showToast({
						title: '麦克风已关闭',
						icon: 'none'
					})
					// 如果正在说话，停止录音
					if (this.isTalking) {
						this.stopTalking()
					}
				}
			},
			// 测试点击
			testClick() {
				console.log('🔥🔥🔥 ===== testClick 被调用 ===== 🔥🔥🔥')
				console.log('🔥🔥🔥 按钮被点击了！')
				console.log('🔥 当前时间:', new Date().toLocaleTimeString())
				uni.showToast({
					title: '按钮可以点击',
					icon: 'none'
				})
			},
			// 开始说话（按住）
			// 开始说话（按住）
			async startTalking(e) {
				console.log('🔥🔥🔥 ===== startTalking 被调用 ===== 🔥🔥🔥')
				console.log('🔥 事件对象:', e)
				console.log('🔥 当前时间:', new Date().toLocaleTimeString())
				console.log('=== startTalking 被调用 ===')
				console.log('事件对象:', e)
				console.log('micEnabled:', this.micEnabled, 'isRecording:', this.isRecording)
				
				if (!this.micEnabled) {
					uni.showToast({
						title: '请先开启麦克风权限',
						icon: 'none'
					})
					return
				}
				
				// 检查 WebSocket 连接状态
				if (!this.socketTask) {
					console.error('❌ socketTask 不存在，尝试建立连接...')
					uni.showToast({
						title: '正在连接，请稍候...',
						icon: 'loading',
						duration: 2000
					})
					this.connectWebSocket()
					
					// 等待连接建立（最多 3 秒）
					for (let i = 0; i < 30; i++) {
						await new Promise(resolve => setTimeout(resolve, 100))
						if (this.socketTask && this.socketTask.readyState === 1) {
							console.log('✅ WebSocket 连接成功')
							uni.hideToast()
							break
						}
					}
					
					// 如果还是没有连接，放弃
					if (!this.socketTask || this.socketTask.readyState !== 1) {
						console.error('❌ WebSocket 连接超时')
						uni.showToast({
							title: 'WebSocket 连接失败，请重试',
							icon: 'none'
						})
						return
					}
				}
				
				if (this.socketTask.readyState !== 1) {
					console.error('❌ WebSocket 未连接, 状态:', this.socketTask.readyState)
					uni.showToast({
						title: 'WebSocket 未连接，正在重连...',
						icon: 'none'
					})
					this.connectWebSocket()
					return
				}
				
				this.isTalking = true
				console.log('✅ 开始说话, isTalking 设置为:', this.isTalking)
				
				// 发送 ptt_on 信号
				console.log('📤 发送 ptt_on 信号')
				this.socketTask.send({
					data: 'ptt_on',
					success: () => {
						console.log('✅ ptt_on 信号发送成功')
					},
					fail: (err) => {
						console.error('❌ ptt_on 信号发送失败:', err)
					}
				})
				
				// 如果录音还没开始，先启动录音
				if (!this.isRecording) {
					console.log('录音未启动，开始启动录音')
					this.startRecording()
				} else {
					console.log('录音已在运行')
				}
			},
			// 停止说话（松开）- 停止录音并发送
			stopTalking(e) {
				console.log('=== stopTalking 被调用 ===')
				console.log('事件对象:', e)
				console.log('当前 isTalking:', this.isTalking)
				
				this.isTalking = false
				console.log('❌ 停止说话, isTalking 设置为:', this.isTalking)
				
				// 停止录音，触发 onStop 回调发送文件
				if (this.isRecording && recorderManager) {
					console.log('🛑 停止录音并准备发送...')
					recorderManager.stop()
					this.isRecording = false
				}
			},
			// 开始录制
			async startRecording() {
				console.log('=== startRecording 被调用 ===')
				console.log('isRecording:', this.isRecording)
				console.log('socketTask 状态:', this.socketTask ? this.socketTask.readyState : 'null')
				
				if (this.isRecording) {
					console.log('录音已在进行中，跳过')
					return
				}

				this.isRecording = true
				this.status = 'Call Started'
				
				// 检查 recorderManager
				if (!recorderManager) {
					console.error('❌ recorderManager 未初始化')
					uni.showToast({
						title: '录音功能初始化失败',
						icon: 'none'
					})
					this.isRecording = false
					return
				}
				
				console.log('🎙️ 启动 recorderManager')
				try {
					// 使用最稳定的录音配置
					const recorderOptions = {
						duration: 600000, // 10 分钟
						sampleRate: 16000, // 必须 16kHz，匹配服务器
						numberOfChannels: 1, // 单声道
						encodeBitRate: 64000, // 降低到 64kbps，更适合语音识别
						format: 'mp3', // 使用 MP3 格式
						audioSource: 'mic' // 明确指定麦克风作为音频源
						// 完全移除 frameSize，避免任何实时处理
					}
					
					console.log('📋 录音参数:', JSON.stringify(recorderOptions))
					console.log('⚠️ 注意：使用最稳定的完整文件模式，无实时处理')
					
					// 记录开始时间
					this.recordStartTime = Date.now()
					console.log('📅 录音开始时间:', new Date(this.recordStartTime).toLocaleTimeString())
					
					recorderManager.start(recorderOptions)
					console.log('✅ recorderManager.start 已调用')
				} catch (err) {
					console.error('❌ 启动录音失败:', err)
					this.isRecording = false
					uni.showToast({
						title: '启动录音失败: ' + err.message,
						icon: 'none'
					})
				}
			},
			// 分片发送音频数据（按照官方推荐参数）
			async sendAudioInChunks(audioData) {
				// 官方推荐：每包3200字节（约100ms音频），延迟100ms
				// PCM 16kHz 单声道：16000 * 2 * 0.1 = 3200 bytes/100ms
				const chunkSize = 3200 // 3.2KB per chunk（官方推荐）
				const chunkDelay = 100 // 100ms（官方推荐）
				
				// 确保 audioData 是 ArrayBuffer
				if (!(audioData instanceof ArrayBuffer)) {
					console.error('❌ audioData 不是 ArrayBuffer，类型:', typeof audioData)
					uni.showToast({
						title: '音频数据格式错误',
						icon: 'none'
					})
					return
				}
				
				const totalSize = audioData.byteLength
				let offset = 0
				let chunkCount = 0
				
				console.log('📦 开始分片发送（官方推荐参数）')
				console.log('📊 总大小:', totalSize, 'bytes')
				console.log('📊 每片大小:', chunkSize, 'bytes')
				console.log('📊 发送间隔:', chunkDelay, 'ms')
				console.log('📊 预计发送时间:', Math.ceil(totalSize / chunkSize) * chunkDelay, 'ms')
				console.log('📊 预计录音时长:', (totalSize / 32000).toFixed(2), '秒')
				
				// 显示加载提示
				uni.showLoading({
					title: '发送中...',
					mask: true
				})
				
				// 使用 Promise 确保顺序发送
				const sendChunk = (chunk, index) => {
					return new Promise((resolve, reject) => {
						console.log(`📤 发送第 ${index} 片，大小: ${chunk.byteLength} bytes`)
						
						this.socketTask.send({
							data: chunk,
							success: () => {
								console.log(`✅ 第 ${index} 片发送成功`)
								resolve()
							},
							fail: (err) => {
								console.error(`❌ 第 ${index} 片发送失败:`, err)
								reject(err)
							}
						})
					})
				}
				
				try {
					// 顺序发送所有片段
					while (offset < totalSize) {
						const end = Math.min(offset + chunkSize, totalSize)
						const chunk = audioData.slice(offset, end)
						chunkCount++
						
						await sendChunk(chunk, chunkCount)
						offset = end
						
						// 延迟指定时间再发送下一片（模拟实时流）
						if (offset < totalSize) {
							await new Promise(resolve => setTimeout(resolve, chunkDelay))
						}
					}
					
					console.log('✅ 所有音频片段发送完成，共', chunkCount, '片')
					console.log('📊 实际发送时间:', chunkCount * chunkDelay, 'ms')
					
					// 延迟 100ms 后发送结束标记
					await new Promise(resolve => setTimeout(resolve, 100))
					
					// 发送结束标记
					await new Promise((resolve, reject) => {
						console.log('📤 发送结束标记 "end"')
						this.socketTask.send({
							data: 'end',
							success: () => {
								console.log('✅ 结束标记发送成功，等待服务器处理...')
								uni.showLoading({
									title: '识别中...',
									mask: true
								})
								resolve()
							},
							fail: (err) => {
								console.error('❌ 结束标记发送失败:', err)
								reject(err)
							}
						})
					})
					
				} catch (err) {
					console.error('❌ 发送过程出错:', err)
					uni.hideLoading()
					uni.showToast({
						title: '发送失败',
						icon: 'none'
					})
				}
			},
			stopCall() {
				this.isRecording = false;
				if (recorderManager) {
					recorderManager.stop();
				}
				if (this.socketTask) {
					this.socketTask.close();
				}
				if (this.audioContext) {
					this.audioContext.pause();
					this.audioContext.destroy();
				}
				if (this.timer) {
					clearInterval(this.timer);
				}
			},
			// 接收消息
			async handleServerMessage(data) {
				console.log('=== handleServerMessage 被调用 ===')
				console.log('📥 接收到的信息, 类型:', typeof(data))
				
				// 隐藏加载提示
				uni.hideLoading()
				
				if (data && typeof(data) == 'object') {
					//处理数据流（音频数据）
					this.audioData.push(data);
					console.log('🎵 收到音频数据流, 当前缓存数量:', this.audioData.length, '本次大小:', data.byteLength)
				} else {
					//处理非数据流（控制消息）
					try {
						let dataInfo = JSON.parse(data)
						console.log('📋 收到控制消息, type:', dataInfo.type)
						console.log('📋 完整消息:', JSON.stringify(dataInfo))
						
						if (dataInfo.type == 'reply_end') {
						// 检测是否为 App 平台
						// #ifdef APP-PLUS
						uni.showLoading({
							title: '思考中',
							mask: true
						});
						console.log('reply_end (App平台)')
						// 创建一个完整的 ArrayBuffer 来存储所有音频数据
						const totalLength = this.audioData.reduce((acc, buffer) => acc + buffer.byteLength, 0)
						console.log('totalLength:',totalLength)
						const mergedArrayBuffer = new Uint8Array(totalLength)
						
						// 将音频数据填充到mergedArrayBuffer中
						let offset = 0;
						for (let i = 0; i < this.audioData.length; i++) {
							const buffer = new Uint8Array(this.audioData[i]);
							mergedArrayBuffer.set(buffer, offset);
							offset += buffer.byteLength;
						}
						
						const base64Audio = uni.arrayBufferToBase64(mergedArrayBuffer.buffer);
						const base64WithPrefix = `data:audio/mp3;base64,${base64Audio}`;
						
						const filePath = await new Promise((resolve) => {
							console.log('this:',this)
							const fileName = `_doc/${Date.now()}_numberPerson.mp3`;
							this.base64ToFile(base64WithPrefix, fileName, (path) => {
								console.log('pathpathpath',path)
								resolve(path);
							});
						});
						
						console.log('pathpathpathfilePath',filePath)
						this.audioContext.src = filePath;
						try {
							uni.hideLoading()
							console.log('尝试播放...');
							this.audioContext.play();
							// 清空音频数据
							this.audioData = [];
						} catch (delayError) {
							console.error('播放失败:', delayError);
						}
						// #endif
						
						// #ifndef APP-PLUS
						this.mergeAudioData()
						// #endif
					}
					if (dataInfo.type == 'reply_text') {
						// #ifdef APP
						uni.showLoading({
							title: '思考中',
							mask: true
						});
						// #endif
						// #ifdef MP
						uni.showLoading({
							title: '思考中',
							mask: true
						});
						// #endif
						
					}
					if (dataInfo.type == 'interrupt') {
						this.audioContext.pause();
						// uni.showToast({
						// 	icon: "none",
						// 	duration: 2000,
						// 	title: "被打断"
						// })
					}
					if (dataInfo.type == 'ready') {
						// 准备好的
					}
				} catch (parseError) {
					console.error('❌ JSON 解析失败:', parseError)
					console.error('原始数据:', data)
				}
			}
		},
			base64ToFile(base64Str, fileName, callback) {
				// https://blog.csdn.net/qq_45225600/article/details/149859602
			    var index = base64Str.indexOf(',');
			    var base64Str = base64Str.slice(index + 1, base64Str.length);
			    
			    plus.io.requestFileSystem(plus.io.PRIVATE_DOC, function(fs) {
			        fs.root.getFile(fileName, { create: true }, function(entry) {
			            var fullPath = entry.fullPath;
			            let platform = uni.getSystemInfoSync().platform;
			            
			            if (platform == 'android') {
			                // Android实现
			                var Base64 = plus.android.importClass("android.util.Base64");
			                var FileOutputStream = plus.android.importClass("java.io.FileOutputStream");
			                try {
			                    var out = new FileOutputStream(fullPath);
			                    var bytes = Base64.decode(base64Str, Base64.DEFAULT);
			                    out.write(bytes);
			                    out.close();
			                    callback && callback(entry.toLocalURL());
			                } catch (e) {
			                    console.log(e.message);
			                }
			            } else if (platform == 'ios') {
			                // iOS实现
			                var NSData = plus.ios.importClass('NSData');
			                var nsData = new NSData();
			                nsData = nsData.initWithBase64EncodedStringoptions(base64Str, 0);
			                if (nsData) {
			                    nsData.plusCallMethod({ writeToFile: fullPath, atomically: true });
			                    plus.ios.deleteObject(nsData);
			                }
			                callback && callback(entry.toLocalURL());
			            }
			        });
			    });
			},
			// 合并音频数据并播放
			async mergeAudioData() {
				console.log('this.audioData:',this.audioData)
				if (this.audioData.length === 0) {
					console.log('没有音频数据')
					return null
				}
				try {
					// 创建一个完整的 ArrayBuffer 来存储所有音频数据
					const totalLength = this.audioData.reduce((acc, buffer) => acc + buffer.byteLength, 0)
					console.log('totalLength:',totalLength)
					const mergedArrayBuffer = new Uint8Array(totalLength)
					console.log('mergedArrayBuffer:',mergedArrayBuffer)
					let offset = 0
					for (let i = 0; i < this.audioData.length; i++) {
						const buffer = new Uint8Array(this.audioData[i])
						mergedArrayBuffer.set(buffer, offset)
						offset += buffer.byteLength
					}
					
					// 将合并后的 ArrayBuffer 保存为文件并播放
					const fileName = `recording_${Date.now()}.mp3`
					let filePath;
					
					// #ifdef APP-PLUS
					// App端音频播放处理
					try {
						// 重新初始化音频上下文，确保状态正确
						this.initAudio();
						
						// 先检查存储权限
						if (typeof plus !== 'undefined' && plus.android) {
							const Context = plus.android.importClass('android.content.Context');
							const Environment = plus.android.importClass('android.os.Environment');
							const permissions = ['android.permission.READ_EXTERNAL_STORAGE', 'android.permission.WRITE_EXTERNAL_STORAGE'];
							plus.android.requestPermissions(permissions, (res) => {
								if (res.deniedAlways.length > 0) {
									console.error('存储权限被永久拒绝');
								}
							});
						}
						
						// 方案1：尝试使用Blob URL直接播放（如果支持）
						if (typeof Blob !== 'undefined' && typeof URL !== 'undefined' && URL.createObjectURL) {
							console.log('尝试使用Blob URL播放...');
							const blob = new Blob([mergedArrayBuffer.buffer], { type: 'audio/mpeg' });
							const blobURL = URL.createObjectURL(blob);
							console.log('Blob URL:', blobURL);
							this.audioContext.src = blobURL;
						} else {
							// 方案2：写入文件后播放
							console.log('尝试使用文件方式播放...');
							filePath = `${plus.io.PUBLIC_DOWNLOADS}/${fileName}`;
							const fileURL = await this.writeFileApp(mergedArrayBuffer.buffer, filePath);
							console.log('文件播放路径:', fileURL);
							
							// 尝试多种路径格式
							let finalURL = fileURL;
							
							// 如果fileURL是file://格式，尝试去除协议
							if (fileURL.startsWith('file://')) {
								const pathWithoutProtocol = fileURL.substring(7);
								console.log('尝试无协议路径:', pathWithoutProtocol);
							}
							
							// 尝试使用plus.io.convertLocalFileSystemURL转换
							const convertedURL = plus.io.convertLocalFileSystemURL(filePath);
							console.log('转换后的路径:', convertedURL);
							
							this.audioContext.src = finalURL;
						}
					} catch (error) {
						console.error('App端音频播放准备失败:', error);
						// 回退到简单的文件写入方式
						filePath = `${plus.io.PUBLIC_DOWNLOADS}/${fileName}`;
						const fileURL = await this.writeFileApp(mergedArrayBuffer.buffer, filePath);
						this.audioContext.src = fileURL;
					}
					// #endif
					
					// #ifndef APP-PLUS
					// 小程序端使用uni API写入文件
					filePath = `${uni.env.USER_DATA_PATH}/${fileName}`;
					await this.writeFileMiniProgram(mergedArrayBuffer.buffer, filePath);
					this.audioContext.src = filePath;
					// #endif
					
					console.log('最终音频源:', this.audioContext.src)
					// 播放音频
					try {
						console.log('开始播放...');
						// 确保音频上下文已正确创建
						if (!this.audioContext) {
							console.error('音频上下文未初始化');
							this.initAudio();
							this.audioContext.src = filePath;
						}
						// 先设置src再播放
						this.audioContext.play();
						// 清空音频数据
						this.audioData = [];
					} catch (playError) {
						console.error('音频播放异常:', playError);
						console.error('异常详情:', JSON.stringify(playError));
						// 尝试延迟播放
						setTimeout(() => {
							try {
								console.log('尝试延迟播放...');
								this.audioContext.play();
							} catch (delayError) {
								console.error('延迟播放也失败:', delayError);
							}
						}, 500);
						this.audioData = [];
					}
				} catch (error) {
					console.error('合并音频数据失败:', error)
					return null
				}
			},
			// App端文件写入方法
			writeFileApp(buffer, filePath) {
				console.log('App端文件写入方法:',buffer, filePath)
				return new Promise((resolve, reject) => {
					console.log('plus.io.requestFileSystem',plus.io.requestFileSystem)
					console.log('plus.io.PUBLIC_DOWNLOADS',plus.io.PUBLIC_DOWNLOADS)
					plus.io.requestFileSystem(plus.io.PUBLIC_DOWNLOADS, (fs) => {
						console.log('fs.root:',fs.root)
						const fileName = filePath.split('/').pop();
						fs.root.getFile(fileName, {create: true, exclusive: false}, (fileEntry) => {
							console.log('fileEntry',fileEntry)
							fileEntry.createWriter((writer) => {
								console.log('writer:',writer)
								console.log('writer 的方法:', Object.keys(writer))
								console.log('buffer type:', typeof buffer, 'buffer instanceof ArrayBuffer:', buffer instanceof ArrayBuffer)
								console.log('Blob 是否存在:', typeof Blob !== 'undefined')
								console.log('准备进入 try 块...')
								
								try {
									console.log('已进入 try 块')
									// 检查 Blob 是否可用，如果不可用则尝试直接写入 ArrayBuffer
									let writeData;
									if (typeof Blob !== 'undefined') {
										// 先将 ArrayBuffer 转换为 Blob，再写入
										console.log('开始创建 Blob...')
										writeData = new Blob([buffer], { type: 'audio/mpeg' });
										console.log('Blob 创建成功:', writeData, 'size:', writeData.size)
									} else {
										// Blob 不可用，直接使用 ArrayBuffer
										console.log('Blob 不可用，直接使用 ArrayBuffer')
										writeData = buffer;
									}
									
									// 先设置所有事件监听器
									console.log('开始设置事件监听器...')
									writer.onerror = (err) => {
										console.error('App端文件写入失败:', err)
										reject(err)
									}
									
									writer.onwriteend = (e) => {
										console.log('App端文件写入完成', e)
										// 获取文件信息验证
										fileEntry.file((file) => {
											console.log('文件信息:', {
												name: file.name,
												size: file.size,
												type: file.type,
												lastModified: file.lastModified
											})
											
											// 验证文件大小是否大于0
											if (file.size === 0) {
												console.error('警告: 文件大小为0，可能写入失败')
											}
										}, (err) => {
											console.error('获取文件信息失败:', err)
										})
										
										// 获取文件路径的多种方式
										const toURL = fileEntry.toURL();
										const fullPath = fileEntry.fullPath;
										console.log('toURL():', toURL)
										console.log('fullPath:', fullPath)
										
										let fileURL;
										
										// 优先使用 toURL()，如果返回的是有效路径（file://开头）
										if (toURL && toURL.startsWith('file://')) {
											fileURL = toURL;
											console.log('使用 toURL() 路径:', fileURL)
										} else if (fullPath && fullPath.startsWith('/')) {
											// fullPath 是绝对路径（如 /storage/emulated/0/...）
											// 直接添加 file:// 前缀
											fileURL = 'file://' + fullPath;
											console.log('使用 fullPath 并添加 file:// 前缀:', fileURL)
										} else if (toURL) {
											// toURL() 返回相对路径（如 _downloads/xxx.mp3），尝试转换
											fileURL = plus.io.convertLocalFileSystemURL(toURL);
											console.log('使用 toURL() 相对路径转换:', fileURL)
											
											// 如果转换后仍不是 file:// 开头，手动添加
											if (!fileURL || !fileURL.startsWith('file://')) {
												// 尝试用 fullPath（如果可用）
												if (fullPath && fullPath.startsWith('/')) {
													fileURL = 'file://' + fullPath;
													console.log('回退到 fullPath 并添加 file:// 前缀:', fileURL)
												}
											}
										}
										
										console.log('App端最终文件URL:', fileURL)
										resolve(fileURL)
									}
									
									writer.ontruncate = (e) => {
										console.log('App端文件清空完成，开始写入', e)
										console.log('writer.readyState:', writer.readyState)
										
										// truncate 完成后再写入数据
										console.log('准备写入数据:', writeData, '类型:', typeof writeData, '是否是ArrayBuffer:', writeData instanceof ArrayBuffer)
										
										// 添加写入开始监听
										writer.onwritestart = (e) => {
											console.log('文件写入开始', e)
										}
										
										// 如果 Blob 不可用，ArrayBuffer 需要转换为 Uint8Array 的 buffer
										if (writeData instanceof ArrayBuffer) {
											console.log('写入 ArrayBuffer，大小:', writeData.byteLength, 'bytes')
											// FileWriter.write() 应该支持 ArrayBuffer，但需要确保格式正确
											writer.write(writeData)
										} else {
											console.log('写入 Blob，大小:', writeData.size)
											writer.write(writeData)
										}
									}
									
									console.log('事件监听器设置完成，开始 seek...')
									// 清空文件内容（如果文件已存在）
									// 先定位到文件开头
									writer.seek(0);
									console.log('seek 完成，开始 truncate...')
									// 然后清空文件，这会触发 ontruncate 事件
									writer.truncate(0);
									console.log('truncate 调用完成')
								} catch (error) {
									console.error('写入过程中发生错误:', error)
									reject(error)
								}
							}, (err) => {
								console.error('App端创建写入器失败:', err)
								reject(err)
							})
						}, (err) => {
							console.error('App端获取文件失败:', err)
							reject(err)
						})
					}, (err) => {
						console.error('App端请求文件系统失败:', err)
						reject(err)
					})
				})
			},
			// 小程序端文件写入方法
			writeFileMiniProgram(buffer, filePath) {
				return new Promise((resolve, reject) => {
					const fs = uni.getFileSystemManager()
					fs.writeFile({
						filePath: filePath,
						data: buffer,
						encoding: 'binary',
						success: (res) => {
							console.log('小程序端文件写入成功:', res)
							resolve(filePath)
						},
						fail: (err) => {
							console.error('小程序端文件写入失败:', err)
							reject(err)
						}
					})
				})
			},
			back() {
				uni.navigateBack({
					delta: 1,
				});
			},
			generateClick() {
				uni.navigateBack({
					delta: 2,
				});
			},
			// 发送音频到ASR端点进行处理
			async sendAudioToASR(audioBytes) {
				console.log('📤 开始发送音频到ASR端点')
				console.log('📊 音频数据大小:', audioBytes.length, 'bytes')
				
				// 显示加载提示
				uni.showLoading({
					title: '语音识别中...',
					mask: true
				})
				
				try {
					// 将音频数据转换为base64
					let base64Audio = ''
					for (let i = 0; i < audioBytes.length; i++) {
						base64Audio += String.fromCharCode(audioBytes[i])
					}
					base64Audio = btoa(base64Audio)
					
					console.log('📤 发送ASR请求...')
					const response = await uni.request({
						url: this.baseURLPy + '/voice/call/asr',
						method: 'POST',
						header: {
							'Content-Type': 'application/json',
							'Authorization': 'Bearer ' + uni.getStorageSync("token")
						},
						data: {
							audio_data: base64Audio,
							format: 'mp3'
						}
					})
					
					console.log('✅ ASR响应:', response)
					
					// 隐藏加载提示
					uni.hideLoading()
					
					if (response.statusCode === 200 && response.data) {
						const result = response.data
						console.log('✅ ASR识别结果:', result.text)
						
						// 显示识别结果
						uni.showToast({
							title: `识别: ${result.text}`,
							icon: 'none',
							duration: 3000
						})
						
						return result
					} else {
						throw new Error(`ASR请求失败: ${response.statusCode}`)
					}
				} catch (error) {
					console.error('❌ ASR请求失败:', error)
					
					// 隐藏加载提示
					uni.hideLoading()
					
					uni.showToast({
						title: 'ASR识别失败',
						icon: 'none',
						duration: 2000
					})
					throw error
				}
			},
			goRecharge() {
				uni.showToast({
					title: '充值功能开发中',
					icon: 'none'
				})
			}
		}
	}
</script>
<style>
	/* page 选择器在 nvue 中不支持，已移除 */
</style>
<style>
	.body {
		position: relative;
	}

	.back {
		position: fixed;
		left: 0;
		top: 0;
		width: 100%;
		height: 100%;
		display: block;
	}

	.body {
		position: relative;
		padding: 0 60rpx;
	}

	.header {
		position: absolute;
		right: 28rpx;
		top: 11%;
		z-index: 5;
	}

	.header_content {
		position: relative;
		padding: 12rpx 24rpx;
		background: linear-gradient(135deg, rgba(159, 71, 255, 0.6) 0%, rgba(0, 83, 250, 0.6) 100%);
		border-radius: 12rpx;
		text-align: center;
	}

	.header_time {
		font-weight: 400;
		font-size: 24rpx;
		color: #FFFFFF;
		line-height: 50rpx;
	}

	.header_module {
		position: relative;
	}

	.header_title {
		margin: 0 8rpx 0 0;
		padding: 0 12rpx;
		font-weight: 400;
		font-size: 24rpx;
		color: #FFFFFF;
		line-height: 50rpx;
		background: linear-gradient(135deg, rgba(159, 71, 255, 0.6) 0%, rgba(0, 83, 250, 0.6) 100%);
		border-radius: 12rpx;
	}

	.header_title:nth-child(3) {
		margin: 0 0 0 0;
	}

	.header_recharge {
		position: relative;
		font-weight: 400;
		font-size: 24rpx;
		color: #FFFFFF;
		line-height: 50rpx;
	}

	.header_recharge image {
		margin: 0 0 0 10rpx;
		width: 6rpx;
		height: 10rpx;
	}

	/* 删除原来的voice样式，添加新的动态波形样式 */
	.dynamic-wave {
		position: absolute;
		left: 0;
		right: 0;
		bottom: 50%;
		width: 300rpx;
		/* 增加整体宽度 */
		margin: 0 auto;
		z-index: 5;
		display: flex;
		align-items: center;
		justify-content: center;
		height: 150rpx;
		/* 增加容器高度 */
	}

	.wave-bar {
		width: 8rpx;
		/* 增加单个波形条的宽度 */
		height: 40rpx;
		/* 增加基础高度 */
		margin: 0 4rpx;
		/* 增加间距 */
		background: linear-gradient(to top, #9f47ff, #0053fa);
		border-radius: 4rpx;
		/* 调整圆角 */
		animation: wave 1.5s infinite ease-in-out;
	}

	@keyframes wave {

		0%,
		100% {
			height: 40rpx;
			/* 调整基础高度 */
			opacity: 0.6;
		}

		50% {
			height: 120rpx;
			/* 调整最大高度 */
			opacity: 1;
		}
	}

	/* 麦克风权限开关样式 */
	.mic-permission-switch {
		position: absolute;
		right: 28rpx;
		top: 100rpx;
		z-index: 10;
		width: 80rpx;
		height: 80rpx;
		background: rgba(0, 0, 0, 0.3);
		border-radius: 50%;
		display: flex;
		align-items: center;
		justify-content: center;
		backdrop-filter: blur(10rpx);
	}

	.mic-emoji {
		font-size: 48rpx;
	}

	.opt {
		position: absolute;
		left: 0;
		right: 0;
		bottom: 15%;
		margin: 0 auto;
		z-index: 5;
		display: flex;
		justify-content: center;
		gap: 100rpx;
	}

	.opt_item {
		position: relative;
	}

	.opt_image {
		width: 132rpx;
		height: 132rpx;
		border-radius: 100rpx;
		display: block;
	}

	.opt_name {
		margin: 26rpx 0 0 0;
		font-weight: 500;
		font-size: 32rpx;
		color: #FFFFFF;
		line-height: 50rpx;
		text-align: center;
	}

	/* 按住说话按钮样式 */
	.mic-button {
		transition: transform 0.2s;
	}

	.mic-button.talking {
		transform: scale(1.1);
	}

	.mic-button.talking .opt_image {
		box-shadow: 0 0 20rpx rgba(159, 71, 255, 0.8);
	}

	.black {
		position: absolute;
		left: 0;
		bottom: 0;
		width: 100%;
		height: 812rpx;
		background: linear-gradient(178deg, rgba(0, 0, 0, 0) 0%, #000000 100%);
		z-index: 2;
	}
</style>