// #ifdef H5
import weixin from 'jweixin-module'
import Cache from './cache'
import $http from '../config/https.js'

function isAndroid() {
	return /android/i.test(navigator.userAgent)
}

// 录音相关：优先使用微信 JSSDK，失败时降级到浏览器原生 MediaRecorder API
// JSSDK 路径：复用 App.vue 全局 config() → weixin.startRecord/stopRecord/onVoiceRecordEnd/uploadVoice → serverId
// MediaRecorder 路径：getUserMedia → MediaRecorder → Blob → fetch /uploadFile → filePath
// 限制：1) 仅 H5 端；小程序端继续走 uni.getRecorderManager
let _mediaRecorder = null
let _mediaStream = null
let _recordStartTime = 0
let _recordChunks = []
let _recordMimeType = ''

// JSSDK 共存相关状态
let _recordUsingJSSDK = false    // 当前录音是否使用 JSSDK 路径
let _jssdkRecordStartTime = 0    // JSSDK 录音开始时间（毫秒）
let _stopRecordInProgress = false // 防止 touchend/touchcancel 竞态重复调用 stopRecord

// 探测浏览器支持的录音格式（优先级：mp4 > webm > ogg）
function getSupportedMimeType() {
	const candidates = [
		'audio/mp4',        // iOS Safari 14.3+
		'audio/webm;codecs=opus', // Chrome / Android
		'audio/webm',
		'audio/ogg;codecs=opus',
		'audio/ogg'
	]
	for (const type of candidates) {
		if (typeof MediaRecorder !== 'undefined' && MediaRecorder.isTypeSupported(type)) {
			return type
		}
	}
	return '' // 浏览器不支持录音
}

// 释放麦克风资源
function releaseStream() {
	if (_mediaStream) {
		_mediaStream.getTracks().forEach((track) => {
			try { track.stop() } catch (e) {}
		})
		_mediaStream = null
	}
	_mediaRecorder = null
}

const Wechath5 = {
	//获取微信配置url
	// 微信 JSSDK 签名 URL 规则：
	// 参与签名的 URL 必须是调用 wx.config 时所在页面的完整 URL（不含 # 及其后面内容）
	// 本项目是 hash 模式路由，SPA 内跳转只会改 # 后的部分，
	// location.href.split('#')[0] 就是首次进入时的完整 URL，等同于 window.entryUrl
	// 因此统一用当前页 URL 签名即可，iOS/Android/PC 微信都适用
	signLink() {
		const url = window.location.href.split('#')[0]
		console.log('[JSSDK] signLink 返回URL:', url, isAndroid())
		return url
	},
	// JSSDK 配置状态：'pending' | 'success' | 'fail'
	_jsSdkState: 'pending',
	// JSSDK config 的 Promise（避免重复调用）
	_jsSdkPromise: null,
	// 录音权限是否已注册（App.vue config 成功后在 then 中验证，null=未验证）
	_recordApiReady: null,
	// JSSDK 语音播放结束回调是否已注册
	_voicePlayEndRegistered: false,
	// JSSDK 语音播放结束回调（最新一次）
	_onVoicePlayEndCallback: null,
	// H5 端音频播放器实例
	_playAudioContext: null,
	//微信sdk配置
	config() {
		// 如果已有缓存的 Promise：
		// - 成功：直接返回（hash 模式下签名 URL 不随页面跳转变化）
		// - 失败：清除缓存允许重试（App.vue onLaunch 时 userData 可能未就绪）
		// - pending：直接返回正在进行的 Promise
		if (this._jsSdkPromise) {
			if (this._jsSdkState === 'success' || this._jsSdkState === 'pending') {
				return this._jsSdkPromise
			}
			// fail 状态：清除缓存，允许重新签名
			this._jsSdkPromise = null
			this._jsSdkState = 'pending'
		}

		this._jsSdkPromise = new Promise((resolve, reject) => {
			const url = this.signLink()
			const userData = Cache.get('userData')
			const jsApiList = [
				'startRecord', 'stopRecord', 'onVoiceRecordEnd',
				'playVoice', 'pauseVoice', 'stopVoice', 'onVoicePlayEnd',
				'uploadVoice', 'downloadVoice',
				'updateAppMessageShareData', 'updateTimelineShareData',
				'onMenuShareAppMessage', 'onMenuShareTimeline', 'onMenuShareWeibo',
				'openAddress', 'chooseWXPay', 'getBrandWCPayRequest'
			]
			console.log('[JSSDK] 开始初始化, 签名URL:' + url)

			const doConfig = (cfg) => {
				console.log('[JSSDK] 后端返回签名配置:' + JSON.stringify(cfg))
				weixin.config({
					debug: false,
					appId: (cfg && cfg.appId) ? cfg.appId : '',
					timestamp: (cfg && cfg.timestamp) ? cfg.timestamp : '',
					nonceStr: (cfg && cfg.nonceStr) ? cfg.nonceStr : '',
					signature: (cfg && cfg.signature) ? cfg.signature : '',
					jsApiList
				})
				// 注意：weixin.ready() 回调在 config 成功和失败时都会执行（jweixin-module 源码行为）
				// 所以不能在 ready 中直接标记成功，需要靠 error 回调来标记失败
				weixin.error((err) => {
					// error 回调在 ready 回调之前执行（A 函数中 fail 先于 complete）
					this._jsSdkState = 'fail'
					console.error('[JSSDK] config 验证失败:', err)
					reject(err)
				})
				weixin.ready(() => {
					// 只有未被 error 标记为失败时，才算成功
					if (this._jsSdkState !== 'fail') {
						this._jsSdkState = 'success'
						console.log('[JSSDK] 初始化成功')
						resolve()
					}
				})
			}

			// 调用后端接口获取 JSSDK 签名配置
			// 注意：医生端 https.js 在 errorCode!='0' 时走 fail（若有），网络异常也会以 errorCode='-1' 走 success
		$http.post('urlS', this, {
			code: '99030014',
			wechatCode: '01020008',
			data: {
				params: [{
					url,
					pubAccCode: 'MYGKHLWYYH5',
				}]
			},
			namespace: 'N',
			success: (res) => {
				console.log('[JSSDK] 后端签名接口返回:', res)
				if (+res.errorCode === 0 && res.result) {
					doConfig(res.result)
				} else {
					console.error('[JSSDK] 获取签名失败:', res)
					this._jsSdkState = 'fail'
					reject(new Error('获取 JSSDK 签名失败: ' + JSON.stringify(res)))
				}
			},
		fail: (err) => {
			console.error('[JSSDK] 签名请求失败:', err)
			this._jsSdkState = 'fail'
			reject(new Error('JSSDK 签名请求失败: ' + JSON.stringify(err)))
		}
			})
		})

		return this._jsSdkPromise
	},

	//获取微信登录url
	getWxUrl() {
	},

	//微信授权
	authLogin(code, pubAccCode) {
		return new Promise((resolve, reject) => {
			const data = {
				params: [{
					code,
					pubAccCode
				}]
			}
			$http.post("urlDeault", this, {
				code: "01020001",
				data: data,
				success: function (res) {
					console.log('01020001 res === ', res)
					let newUserData = {};
					let userData = Cache.get('userData');
					if (userData && userData.openid) delete userData.openid
					if (+res.errorCode === 0) {
						newUserData = {
							...userData,
							openID: res.result.openid,
							pubAccCode: res.result.minPrgCode || pubAccCode,
							session_key: res.result.session_key,
							...(delete res.result.open_id, res.result)
						};
						resolve()
					} else {
						newUserData = {
							...userData,
							openID: "",
							pubAccCode: "",
							session_key: ""
						};
						reject()
					}
					console.log("获取openID成功", newUserData)
					Cache.set('userData', {
						...newUserData
					});
				}
			});
		})
	},

	//微信分享
	share(option) {
		weixin.ready(() => {
			const {
				shareTitle,
				shareLink,
				shareImage,
				shareDesc
			} = option
			// 发送给好友（旧）
			weixin.onMenuShareAppMessage({
				title: shareTitle,
				link: shareLink,
				imgUrl: shareImage,
				success: function (res) {
				}
			})
			// 分享到朋友圈（旧）
			weixin.onMenuShareTimeline({
				title: shareTitle,
				link: shareLink,
				imgUrl: shareImage,
				success: function (res) {
				}
			})
			weixin.updateTimelineShareData({
				title: shareTitle,
				link: shareLink,
				imgUrl: shareImage,
				success: function (res) {
				}
			})
			// 发送给好友
			weixin.updateAppMessageShareData({
				title: shareTitle,
				link: shareLink,
				imgUrl: shareImage,
				desc: shareDesc,
				success: function (res) {
				}
			})
			// 发送到tx微博
			weixin.onMenuShareWeibo({
				title: shareTitle,
				link: shareLink,
				imgUrl: shareImage,
				desc: shareDesc,
				success: function (res) {
				}
			})
		})
	},
	//微信公众号支付
	wxPay(data) {
		return new Promise((reslove, reject) => {
			console.log('jweixin ready')
			if (typeof window.WeixinJSBridge == "undefined") {
				uni.showToast({
					icon: "none",
					title: "请在微信浏览器中打开"
				})
				return
			} else {
				window.WeixinJSBridge.invoke('getBrandWCPayRequest', {
					"appId": data.appId,
					"timeStamp": data.timeStamp,
					"nonceStr": data.nonceStr,
					"package": data.package,
					"signType": data.signType,
					"paySign": data.paySign
				}, (res) => {
					if (res.err_msg == "get_brand_wcpay_request:ok") {
						reslove(res)
					} else if (res.err_msg == "get_brand_wcpay_request:cancel") {
						reject(res)
					} else if (res.err_msg == "get_brand_wcpay_request:fail") {
						reject(res)
					}
				});
			}
		})
	},
	getWxAddress() {
		return new Promise((reslove, reject) => {
			weixin.ready(() => {
				weixin.openAddress({
					success: (res) => {
						reslove(res)
					}
				})
			})
		})
	},
	reviceTransfer(mchId, appId, packageInfo) {
		return new Promise((reslove, reject) => {
			weixin.ready(() => {
				weixin.checkJsApi({
					jsApiList: ['requestMerchantTransfer'],
					success: function (res) {
						if (res.checkResult['requestMerchantTransfer']) {
							WeixinJSBridge.invoke(
								'requestMerchantTransfer', {
									mchId,
									appId,
									package: packageInfo
								},
								function (res) {
									if (res.err_msg ===
										'requestMerchantTransfer:ok') {
										reslove(res.err_msg)
									} else {
										reject(res.err_msg)
									}
								}
							)
						} else {
							alert('你的微信版本过低，请更新至最新版本。')
						}
					}
				})
			})
		})
	},

	// 验证 JSSDK 录音权限是否已注册（供 App.vue config().then() 调用）
	// 返回 Promise<boolean>：true 表示 startRecord 权限可用
	checkRecordApi() {
		return new Promise((resolve) => {
			weixin.ready(() => {
				weixin.checkJsApi({
					jsApiList: ['startRecord'],
					success: (res) => {
						const ready = !!(res.checkResult && res.checkResult.startRecord)
						console.log('[JSSDK] checkRecordApi startRecord:', ready ? '✅已注册' : '❌未注册')
						resolve(ready)
					},
					fail: () => {
						console.warn('[JSSDK] checkRecordApi 失败')
						resolve(false)
					}
				})
			})
		})
	},

	// 等待 JSSDK 初始化完成并验证录音 API（解决 _recordApiReady 时序问题）
	// 返回 Promise<boolean>
	_waitForRecordApiReady() {
		const self = this
		// 如果已验证，直接返回
		if (self._recordApiReady !== null) {
			return Promise.resolve(self._recordApiReady)
		}
		console.log('[录音] _recordApiReady 为 null，等待 JSSDK 初始化...')
		// 等待 config() 完成（config 是幂等的，已成功会直接返回）
		return self.config().then(() => {
			// config 成功后再等一个微任务周期，确保 checkRecordApi 也有机会执行
			return new Promise((resolve) => {
				let retries = 0
				const maxRetries = 30 // 最多等 3 秒（100ms * 30）
				const check = () => {
					if (self._recordApiReady !== null) {
						console.log('[录音] _recordApiReady 验证完成:', self._recordApiReady)
						resolve(self._recordApiReady)
						return
					}
					retries++
					if (retries >= maxRetries) {
						console.warn('[录音] 等待 _recordApiReady 超时，降级 MediaRecorder')
						resolve(false)
						return
					}
					setTimeout(check, 100)
				}
				check()
			})
		}).catch(() => {
			console.warn('[录音] JSSDK config 失败，降级 MediaRecorder')
			return false
		})
	},

	// JSSDK 路径开始录音（带超时保护）
	_startRecordJSSDK() {
		const self = this
		return new Promise((resolve, reject) => {
			const JSSDK_START_TIMEOUT = 5000 // 5 秒超时
			let settled = false

			const timeout = setTimeout(() => {
				if (settled) return
				settled = true
				console.error('[JSSDK-录音] startRecord 超时，降级 MediaRecorder')
				_recordUsingJSSDK = false
				self._startMediaRecorder(resolve, reject)
			}, JSSDK_START_TIMEOUT)

			weixin.ready(() => {
				if (settled) return
				console.log('[JSSDK-录音] 调用 startRecord')
				_recordUsingJSSDK = true
				_jssdkRecordStartTime = Date.now()

				weixin.startRecord({
					success: () => {
						if (settled) return
						settled = true
						clearTimeout(timeout)
						console.log('[JSSDK-录音] ✅ 录音已开始')
						resolve({ type: 'jssdk' })
					},
					fail: (err) => {
						if (settled) return
						settled = true
						clearTimeout(timeout)
						console.error('[JSSDK-录音] startRecord 失败，降级 MediaRecorder:', err)
						_recordUsingJSSDK = false
						self._startMediaRecorder(resolve, reject)
					}
				})
			})
		})
	},

	// 开始录音
	// 录音权限由 App.vue config().then() 中提前验证，存储在 _recordApiReady
	startRecord() {
		const self = this

		// _recordApiReady 为 null 说明 JSSDK 还没初始化完，等待后再决定路径
		if (self._recordApiReady === null) {
			return self._waitForRecordApiReady().then((ready) => {
				if (ready) {
					return self._startRecordJSSDK()
				}
				console.warn('[录音] JSSDK 不可用，降级 MediaRecorder')
				_recordUsingJSSDK = false
				return new Promise((resolve, reject) => {
					self._startMediaRecorder(resolve, reject)
				})
			})
		}

		if (!self._recordApiReady) {
			console.warn('[录音] _recordApiReady=', self._recordApiReady, '，降级 MediaRecorder')
			_recordUsingJSSDK = false
			return new Promise((resolve, reject) => {
				self._startMediaRecorder(resolve, reject)
			})
		}

		// JSSDK 路径
		return self._startRecordJSSDK()
	},

	// 内部辅助：启动浏览器 MediaRecorder 录音
	_startMediaRecorder(resolve, reject) {
		// 浏览器兼容性检查
		if (typeof MediaRecorder === 'undefined') {
			reject(new Error('当前浏览器不支持录音（缺少 MediaRecorder API）'))
			return
		}

		_recordMimeType = getSupportedMimeType()
		if (!_recordMimeType) {
			reject(new Error('当前浏览器不支持任何录音格式'))
			return
		}
		console.log('[H5-MediaRecorder] 使用格式:', _recordMimeType)

		// 请求麦克风权限
		navigator.mediaDevices.getUserMedia({
			audio: true,
			video: false
		}).then((stream) => {
			_mediaStream = stream
			_recordChunks = []
			_recordStartTime = Date.now()

			try {
				_mediaRecorder = new MediaRecorder(stream, {
					mimeType: _recordMimeType,
					audioBitsPerSecond: 32000
				})
			} catch (e) {
				console.error('[H5-MediaRecorder] 创建失败:', e)
				releaseStream()
				reject(new Error('创建录音器失败: ' + e.message))
				return
			}

			_mediaRecorder.ondataavailable = (e) => {
				if (e.data && e.data.size > 0) {
					_recordChunks.push(e.data)
				}
			}

			_mediaRecorder.onerror = (e) => {
				console.error('[H5-MediaRecorder] 录音出错:', e)
				releaseStream()
				reject(new Error('录音出错'))
			}

			_mediaRecorder.start(1000)
			console.log('[H5-MediaRecorder] 录音已开始')
			resolve({ type: 'mediarecorder' })
		}).catch((err) => {
			console.error('[H5-MediaRecorder] 获取麦克风权限失败:', err)
			let msg = '无法访问麦克风'
			let showModal = false
			if (err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError') {
				msg = '麦克风权限被拒绝，请在浏览器设置中允许后重试'
				showModal = true
			} else if (err.name === 'NotFoundError' || err.name === 'DevicesNotFoundError') {
				msg = '未检测到麦克风设备，请检查设备连接'
			} else if (err.name === 'NotReadableError' || err.name === 'TrackStartError') {
				msg = '麦克风被其他程序占用，请关闭后重试'
			} else if (err.name === 'OverconstrainedError') {
				msg = '麦克风设备不支持所需参数'
			} else if (err.name === 'SecurityError') {
				msg = '请在 HTTPS 协议下使用录音功能'
			}
			const finalErr = new Error(msg)
			finalErr.showModal = showModal
			reject(finalErr)
		})
	},

	// 停止录音，返回统一结构
	// JSSDK 路径：{ type: 'jssdk', serverId, duration }
	// MediaRecorder 路径：{ type: 'mediarecorder', blob, duration, mimeType }
	stopRecord() {
		// 防重入：防止 touchend / touchcancel 竞态导致重复调用
		if (_stopRecordInProgress) {
			console.warn('[录音] stopRecord 正在执行中，跳过重复调用')
			return Promise.reject(new Error('停止录音正在处理中'))
		}
		_stopRecordInProgress = true

		// 幂等守卫：没有正在进行的录音
		if (!_recordUsingJSSDK) {
			if (!_mediaRecorder || _mediaRecorder.state === 'inactive') {
				_stopRecordInProgress = false
				return Promise.reject(new Error('没有正在进行的录音'))
			}
		}
		if (_recordUsingJSSDK) {
			return this._stopRecordJSSDK().finally(() => {
				_stopRecordInProgress = false
			})
		}
		return this._stopRecordMediaRecorder().finally(() => {
			_stopRecordInProgress = false
		})
	},

	// JSSDK 路径停止录音
	_stopRecordJSSDK() {
		return new Promise((resolve, reject) => {
			const VOICE_END_TIMEOUT = 10000  // onVoiceRecordEnd 超时 10 秒
			let settled = false

			const timeout = setTimeout(() => {
				if (settled) return
				settled = true
				console.error('[JSSDK-录音] onVoiceRecordEnd 超时')
				this._cleanupJSSDKRecord()
				reject(new Error('录音停止超时，请重试'))
			}, VOICE_END_TIMEOUT)

			// 调用 JSSDK 停止录音
			console.log('[JSSDK-录音] 调用 stopRecord')
			weixin.stopRecord({
				success: (res) => {
					if (settled) return
					settled = true
					clearTimeout(timeout)

					const localId = res.localId || ''
					const duration = Math.round((Date.now() - _jssdkRecordStartTime) / 1000)
					console.log('[JSSDK-录音] stopRecord success, localId:', localId, 'duration:', duration, 's')

					if (!localId) {
						this._cleanupJSSDKRecord()
						reject(new Error('录音数据获取失败'))
						return
					}

					// 上传到微信服务器获取 serverId
					weixin.uploadVoice({
						localId: localId,
						success: (uploadRes) => {
							const serverId = uploadRes.serverId || ''
							console.log('[JSSDK-录音] uploadVoice serverId:', serverId)
							this._cleanupJSSDKRecord()
							if (!serverId) {
								reject(new Error('语音上传失败：未获取到 serverId'))
								return
							}
							resolve({
								type: 'jssdk',
								serverId: serverId,
								localId: localId,
								duration: duration
							})
						},
						fail: (err) => {
							console.error('[JSSDK-录音] uploadVoice 失败:', err)
							this._cleanupJSSDKRecord()
							reject(new Error('语音上传微信服务器失败，请重试'))
						}
					})
				},
				fail: (err) => {
					if (settled) return
					settled = true
					clearTimeout(timeout)
					console.error('[JSSDK-录音] stopRecord 调用失败:', err)
					this._cleanupJSSDKRecord()
					reject(new Error('停止录音失败'))
				}
			})
		})
	},

	// MediaRecorder 路径停止录音
	_stopRecordMediaRecorder() {
		return new Promise((resolve, reject) => {
			if (!_mediaRecorder || _mediaRecorder.state === 'inactive') {
				reject(new Error('没有正在进行的录音'))
				return
			}

			_mediaRecorder.onstop = () => {
				const duration = Math.round((Date.now() - _recordStartTime) / 1000)
				const blob = new Blob(_recordChunks, { type: _recordMimeType })
				console.log('[H5-MediaRecorder] 录音已停止, size:', blob.size, 'duration:', duration, 's')
				releaseStream()
				resolve({ type: 'mediarecorder', blob, duration, mimeType: _recordMimeType })
			}

			try {
				_mediaRecorder.stop()
			} catch (e) {
				console.error('[H5-MediaRecorder] 停止录音失败:', e)
				releaseStream()
				reject(new Error('停止录音失败'))
			}
		})
	},

	// 清理 JSSDK 录音状态
	_cleanupJSSDKRecord() {
		_recordUsingJSSDK = false
		_jssdkRecordStartTime = 0
	},

	// 取消录音（不返回数据）
	cancelRecord() {
		// 幂等守卫：没有正在进行的录音则直接跳过
		if (!_recordUsingJSSDK && (!_mediaRecorder || _mediaRecorder.state === 'inactive')) {
			console.log('[录音] 没有正在进行的录音，取消操作跳过')
			_recordUsingJSSDK = false
			_jssdkRecordStartTime = 0
			_recordChunks = []
			releaseStream()
			return
		}

		if (_recordUsingJSSDK) {
			// JSSDK 路径取消
			try {
				weixin.stopRecord({
					success: () => { console.log('[JSSDK-录音] 取消: stopRecord 成功') },
					fail: () => { console.warn('[JSSDK-录音] 取消: stopRecord 失败（可忽略）') }
				})
			} catch (e) {
				console.warn('[JSSDK-录音] 取消时异常:', e)
			}
			this._cleanupJSSDKRecord()
			console.log('[JSSDK-录音] 录音已取消')
		} else {
			// MediaRecorder 路径取消
			if (_mediaRecorder && _mediaRecorder.state !== 'inactive') {
				try {
					_mediaRecorder.stop()
				} catch (e) {
					console.warn('[H5-MediaRecorder] 取消录音时停止失败:', e)
				}
			}
			_recordChunks = []
			releaseStream()
			console.log('[H5-MediaRecorder] 录音已取消')
		}
	},

	// 上传录音（支持 JSSDK serverId 和 MediaRecorder Blob 双路径）
	// JSSDK 路径：uploadVoice(serverId, duration) → serverId 已由 weixin.uploadVoice 上传到微信服务器
	//   → 直接返回 { filePath: serverId, duration }
	// MediaRecorder 路径：uploadVoice(blob, duration, fileType) → fetch 上传自家 /uploadFile
	//   → 返回 { filePath, soundName, soundPath, duration }
	uploadVoice(firstArg, duration, fileType) {
		// JSSDK 路径：serverId 已由 _stopRecordJSSDK 内部 weixin.uploadVoice 完成上传，直接透传
		if (typeof firstArg === 'string') {
			return Promise.resolve({
				filePath: firstArg,
				soundName: firstArg,
				duration: duration
			})
		}

		// MediaRecorder 路径：Blob 上传到自家后端
		const blob = firstArg
		return new Promise((resolve, reject) => {
			if (!blob) {
				reject(new Error('录音数据为空'))
				return
			}
			const userData = Cache.get('userData')
			const type = fileType || 'consult'

			// 拼上传 URL（参考小程序端 uploadAudio 的实现）
			const origin = (typeof window !== 'undefined' && window.location)
				? window.location.origin : ''
			let url = ''
			if (userData && userData.consultFileUpload) {
				url = userData.consultFileUpload.indexOf('http') !== -1
					? userData.consultFileUpload
					: origin + userData.consultFileUpload
			} else {
				// fallback：使用默认上传 URL
				url = origin + '/uploadFile?fileType=' + type
				console.warn('[H5-MediaRecorder] userData.consultFileUpload 未配置，使用 fallback URL:', url)
			}

			console.log('[H5-MediaRecorder] 上传 URL:', url)

			// 构造 FormData
			const formData = new FormData()
			const ext = _recordMimeType.includes('mp4') ? 'mp4'
				: _recordMimeType.includes('webm') ? 'webm'
					: _recordMimeType.includes('ogg') ? 'ogg' : 'audio'
			formData.append('file', blob, `record_${Date.now()}.${ext}`)

			fetch(url, {
				method: 'POST',
				body: formData,
				credentials: 'include',
				headers: {
					'X-Requested-With': 'XMLHttpRequest'
				}
			}).then((resp) => {
				console.log('[H5-MediaRecorder] 上传响应 status:', resp.status)
				if (resp.status !== 200) {
					reject(new Error('上传失败，HTTP ' + resp.status))
					return
				}
				return resp.text()
			}).then((text) => {
				if (!text) {
					reject(new Error('上传响应为空'))
					return
				}
				try {
					const parsed = JSON.parse(text)
					console.log('[H5-MediaRecorder] 上传响应解析:', parsed)
				if (parsed.filePath) {
					const soundPath = (userData && userData.consultFile
						? (userData.consultFile.indexOf('http') !== -1
							? userData.consultFile
							: origin + userData.consultFile)
						: origin + '/images/consult/') + parsed.filePath
					resolve({
						filePath: parsed.filePath,
						soundName: parsed.filePath,
						soundPath: soundPath,
						duration: duration
					})
					} else {
						reject(new Error('上传响应无 filePath: ' + text))
					}
				} catch (e) {
					console.error('[H5-MediaRecorder] 解析响应失败:', e, 'raw:', text)
					reject(new Error('解析上传响应失败'))
				}
			}).catch((err) => {
				console.error('[H5-MediaRecorder] 上传失败:', err)
				reject(err)
			})
		})
	},

	// 播放语音
	// serverId 路径：downloadVoice(serverId) → playVoice(localId)（微信原生播放器）
	// HTTP URL 路径：直接 uni.createInnerAudioContext() 播放
	// callbacks: { onEnded, onError }
	playVoice(mediaUrl, callbacks = {}) {
		const { onEnded, onError } = callbacks

		if (!mediaUrl) {
			console.warn('[H5-音频] playVoice mediaUrl 为空')
			if (onError) onError(new Error('mediaUrl 为空'))
			return
		}

		if (mediaUrl.startsWith('http://') || mediaUrl.startsWith('https://')) {
			// MediaRecorder 路径：网络 URL → InnerAudioContext 播放
			console.log('[H5-音频] 网络 URL 路径:', mediaUrl)
			this._playByUrl(mediaUrl, onEnded, onError)
		} else {
			// JSSDK 路径：serverId → downloadVoice → localId → playVoice
			console.log('[H5-音频] serverId 路径:', mediaUrl)
			weixin.ready(() => {
				// 注册 JSSDK 全局播放结束事件（只需注册一次）
				if (onEnded) {
					this._onVoicePlayEndCallback = onEnded
					if (!this._voicePlayEndRegistered) {
						weixin.onVoicePlayEnd({
							success: () => {
								console.log('[H5-音频] JSSDK 播放结束')
								if (this._onVoicePlayEndCallback) this._onVoicePlayEndCallback()
							},
							fail: (err) => {
								console.error('[H5-音频] JSSDK onVoicePlayEnd 注册失败:', err)
							}
						})
						this._voicePlayEndRegistered = true
					}
				}

				weixin.downloadVoice({
					serverId: mediaUrl,
					success: (res) => {
						const localId = res.localId || ''
						console.log('[H5-音频] downloadVoice → localId:', localId)
						if (localId) {
							// 用微信原生播放器播放（localId 不是 URL，不能用 InnerAudioContext）
							weixin.playVoice({
								localId,
								fail: (err) => {
									console.error('[H5-音频] playVoice 失败:', err)
									if (onError) onError(err)
								}
							})
						} else if (onError) {
							onError(new Error('downloadVoice 返回 localId 为空'))
						}
					},
					fail: (err) => {
						console.error('[H5-音频] downloadVoice 失败:', err)
						uni.showToast({ title: '音频下载失败', icon: 'none' })
						if (onError) onError(err)
					}
				})
			})
		}
	},

	// 内部辅助：通过 URL 播放音频
	_playByUrl(url, onEnded, onError) {
		if (!url) {
			console.error('[H5-音频] _playByUrl url 为空')
			if (onError) onError(new Error('url 为空'))
			return
		}

		console.log('[H5-音频] 创建 AudioContext，src:', url)

		// 清理旧的 AudioContext
		if (this._playAudioContext) {
			console.log('[H5-音频] 销毁旧 AudioContext')
			this._playAudioContext.destroy()
		}

		const audio = uni.createInnerAudioContext()
		audio.src = url
		audio.play()

		audio.onEnded(() => {
			console.log('[H5-音频] 播放结束')
			if (onEnded) onEnded()
		})

		audio.onError((err) => {
			console.error('[H5-音频] 播放失败:', err)
			console.error('[H5-音频] 失败详情:', JSON.stringify(err))
			uni.showToast({ title: '音频播放失败', icon: 'none' })
			if (onError) onError(err)
		})

		this._playAudioContext = audio
	}
}
export default Wechath5;
// #endif
