<template>
	<view class="content">
		<view class="header">
			<text class="title">语音识别</text>
		</view>
		
		<!-- 状态显示区域 -->
		<view class="status-area">
			<view class="status-item">
				<text class="status-label">状态：</text>
				<text class="status-value" :class="{ 'status-ready': isReady, 'status-loading': isLoading }">{{ statusText }}</text>
			</view>
		</view>
		
		<!-- 操作按钮区域 -->
		<view class="action-area">
			<button 
				class="action-btn" 
				:class="{ 'btn-recording': isRecording, 'btn-ready': !isRecording && isReady }"
				:disabled="isLoading || !isReady"
				@click="handleStart">
				<text class="btn-text">{{ isRecording ? '正在识别...' : '开始说话' }}</text>
			</button>
			<button 
				v-if="isRecording"
				class="action-btn btn-stop" 
				@click="handleStop">
				<text class="btn-text">停止识别</text>
			</button>
		</view>
		
		<!-- 识别结果区域 -->
		<view class="result-area">
			<view class="result-title">
				<text>识别结果：</text>
			</view>
			<view class="result-content">
				<scroll-view class="result-scroll" scroll-y>
					<text class="result-text">{{ content }}</text>
				</scroll-view>
			</view>
		</view>
	</view>
</template>

<script setup>
	import { ref, onMounted, onBeforeUnmount } from 'vue'
	import { downloadModel, initVoskModel, startSpeechVoice, stopSpeechVoice } from '@/uni_modules/xwq-speech-to-text'
	
	const modelPath = ref('') // 储存模型解压地址
	const content = ref('') // 识别结果
	const isReady = ref(false) // 模型是否已准备好
	const isRecording = ref(false) // 是否正在录音
	const isLoading = ref(false) // 是否正在加载/初始化
	const statusText = ref('准备中...')
	const serverUrl = ref('') // 服务器地址（可从配置文件或环境变量获取）
	
	/**
	 * 页面加载时自动初始化
	 */
	onMounted(() => {
		// 从本地存储读取服务器地址（如果有配置的话）
		const savedServerUrl = uni.getStorageSync('vosk_server_url')
		if (savedServerUrl) {
			serverUrl.value = savedServerUrl
		}
		// 如果需要在代码中直接配置默认服务器地址，可以取消下面的注释
		// 如果没有配置，可以使用默认地址
		// if (!serverUrl.value) {
		// 	serverUrl.value = 'http://192.168.1.100:8080/vosk-model-small-cn-0.22.zip'
		// }
		autoInit()
	})
	
	/**
	 * 页面卸载前停止识别
	 */
	onBeforeUnmount(() => {
		if (isRecording.value) {
			stopSpeechVoice()
		}
	})
	
	/**
	 * 自动初始化模型
	 * 1. 先尝试使用已解压的模型（最快）
	 * 2. 如果没有，尝试从服务器下载
	 * 3. 如果服务器下载失败，从 static 目录初始化
	 */
	const autoInit = () => {
		isLoading.value = true
		statusText.value = '正在初始化模型...'
		
		// 尝试从本地存储读取已解压的模型路径
		const savedModelPath = uni.getStorageSync('vosk_model_path')
		
		if (savedModelPath) {
			// 使用已解压的模型（最快）
			statusText.value = '正在加载已解压模型...'
			initVoskModel({
				modelPath: savedModelPath,
				zipModelPath: ''
			}, (result) => {
				if (result && result.data) {
					modelPath.value = result.data.modelPath
					isReady.value = true
					isLoading.value = false
					statusText.value = '准备就绪，可以开始说话'
					uni.showToast({
						title: '模型加载成功',
						icon: 'success',
						duration: 2000
					})
				} else {
					// 加载失败，尝试从服务器下载
					downloadFromServer()
				}
			})
		} else {
			// 没有已解压的模型，优先从服务器下载
			downloadFromServer()
		}
	}
	
	/**
	 * 从服务器下载模型
	 */
	const downloadFromServer = () => {
		// 如果配置了服务器地址，优先从服务器下载
		if (serverUrl.value) {
			statusText.value = '正在从服务器下载模型...'
			downloadModel(serverUrl.value, (res) => {
				console.log('服务器下载结果：', res)
				if (res.code == 0 && res.filePath != '') {
					// 下载成功，初始化模型
					statusText.value = '下载完成，正在初始化...'
					initVoskModel({
						zipModelPath: res.filePath
					}, (result) => {
						if (result && result.data) {
							modelPath.value = result.data.modelPath
							// 保存模型路径到本地存储
							uni.setStorageSync('vosk_model_path', result.data.modelPath)
							isReady.value = true
							isLoading.value = false
							statusText.value = '准备就绪，可以开始说话'
							uni.showToast({
								title: '模型初始化成功',
								icon: 'success',
								duration: 2000
							})
						} else {
							// 初始化失败，尝试从 static 加载
							initFromStatic()
						}
					})
				} else {
					// 下载失败，尝试从 static 加载
					console.log('服务器下载失败，尝试从本地加载')
					initFromStatic()
				}
			})
		} else {
			// 没有配置服务器地址，直接尝试从 static 加载
			console.log('未配置服务器地址，从本地加载')
			initFromStatic()
		}
	}
	
	/**
	 * 从 static 目录初始化模型
	 */
	const initFromStatic = () => {
		statusText.value = '正在解压模型文件...'
		let path = '/static/vosk-model-small-cn-0.22.zip'
		
		// #ifdef APP-PLUS
		try {
			const staticPath = plus.io.convertLocalFileSystemURL(path)
			initVoskModel({
				zipModelPath: staticPath
			}, (result) => {
				if (result && result.data) {
					modelPath.value = result.data.modelPath
					// 保存模型路径到本地存储
					uni.setStorageSync('vosk_model_path', result.data.modelPath)
					isReady.value = true
					isLoading.value = false
					statusText.value = '准备就绪，可以开始说话'
					uni.showToast({
						title: '模型初始化成功',
						icon: 'success',
						duration: 2000
					})
				} else {
					statusText.value = '初始化失败，请检查模型文件'
					isLoading.value = false
					uni.showToast({
						title: '初始化失败',
						icon: 'error'
					})
				}
			})
		} catch (error) {
			console.error('初始化错误：', error)
			statusText.value = '初始化失败：' + error.message
			isLoading.value = false
			uni.showToast({
				title: '初始化失败',
				icon: 'error'
			})
		}
		// #endif
		
		// #ifndef APP-PLUS
		statusText.value = '仅支持 APP 端'
		isLoading.value = false
		uni.showToast({
			title: '仅支持 APP 端',
			icon: 'error'
		})
		// #endif
	}
	
	/**
	 * 开始识别
	 */
	const handleStart = () => {
		if (!isReady.value) {
			uni.showToast({
				title: '模型未准备好，请稍候',
				icon: 'error'
			})
			return
		}
		
		isRecording.value = true
		statusText.value = '正在识别中...'
		content.value = '' // 清空上次识别结果
		
		startSpeechVoice((res) => {
			console.log('识别结果===', res.data)
			if (res && res.data && res.data.text) {
				content.value += res.data.text + ' '
				// 滚动到底部
				uni.pageScrollTo({
					scrollTop: 10000,
					duration: 300
				})
			}
		})
	}
	
	/**
	 * 停止识别
	 */
	const handleStop = () => {
		stopSpeechVoice()
		isRecording.value = false
		statusText.value = '已停止识别'
		uni.showToast({
			title: '已停止识别',
			icon: 'success',
			duration: 1500
		})
	}
</script>

<style>
	.content {
		display: flex;
		flex-direction: column;
		padding: 20rpx;
		min-height: 100vh;
		background: linear-gradient(to bottom, #667eea 0%, #764ba2 100%);
	}
	
	.header {
		display: flex;
		justify-content: center;
		margin: 40rpx 0 60rpx 0;
	}
	
	.title {
		font-size: 48rpx;
		font-weight: bold;
		color: #fff;
	}
	
	.status-area {
		background-color: rgba(255, 255, 255, 0.9);
		border-radius: 20rpx;
		padding: 30rpx;
		margin-bottom: 40rpx;
		box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
	}
	
	.status-item {
		display: flex;
		align-items: center;
		justify-content: space-between;
	}
	
	.status-label {
		font-size: 28rpx;
		color: #666;
		font-weight: 500;
	}
	
	.status-value {
		font-size: 28rpx;
		color: #333;
		font-weight: 600;
	}
	
	.status-ready {
		color: #52c41a;
	}
	
	.status-loading {
		color: #1890ff;
		animation: pulse 1.5s ease-in-out infinite;
	}
	
	@keyframes pulse {
		0%, 100% {
			opacity: 1;
		}
		50% {
			opacity: 0.6;
		}
	}
	
	.action-area {
		display: flex;
		flex-direction: column;
		align-items: center;
		margin-bottom: 40rpx;
		gap: 30rpx;
	}
	
	.action-btn {
		width: 500rpx;
		height: 500rpx;
		border-radius: 50%;
		border: none;
		display: flex;
		align-items: center;
		justify-content: center;
		font-size: 36rpx;
		font-weight: bold;
		color: #fff;
		box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.3);
		transition: all 0.3s;
	}
	
	.btn-ready {
		background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
	}
	
	.btn-ready:active {
		transform: scale(0.95);
	}
	
	.btn-recording {
		background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
		animation: pulse-ring 1.5s ease-out infinite;
	}
	
	@keyframes pulse-ring {
		0% {
			box-shadow: 0 0 0 0 rgba(245, 87, 108, 0.7);
		}
		50% {
			box-shadow: 0 0 0 40rpx rgba(245, 87, 108, 0);
		}
		100% {
			box-shadow: 0 0 0 0 rgba(245, 87, 108, 0);
		}
	}
	
	.btn-stop {
		width: 300rpx;
		height: 100rpx;
		border-radius: 50rpx;
		background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
	}
	
	.btn-text {
		font-size: 36rpx;
		font-weight: bold;
		color: #fff;
	}
	
	.result-area {
		flex: 1;
		display: flex;
		flex-direction: column;
		background-color: rgba(255, 255, 255, 0.95);
		border-radius: 20rpx;
		padding: 30rpx;
		box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
		min-height: 400rpx;
	}
	
	.result-title {
		font-size: 32rpx;
		font-weight: bold;
		color: #333;
		margin-bottom: 20rpx;
	}
	
	.result-content {
		flex: 1;
		display: flex;
		flex-direction: column;
	}
	
	.result-scroll {
		flex: 1;
		width: 100%;
	}
	
	.result-text {
		font-size: 30rpx;
		line-height: 1.8;
		color: #333;
		word-break: break-all;
		min-height: 200rpx;
	}
</style>
