<template>
	<view class="revival-container">
		<!-- 顶部标题与历史入口 -->
		<view class="hero">
			<view class="hero-text">
				<text class="hero-title">照片复活</text>
				<text class="hero-subtitle">让记忆中的人再次开口说话</text>
			</view>
			<button class="history-btn" @click="goToHistory">
				📜 历史
			</button>
		</view>

		<!-- 帮助弹窗 -->
		<view v-if="showHelpModal" class="help-modal" @click="showHelpModal = false">
			<view class="help-content" @click.stop>
				<view class="help-header">
					<text class="help-title">💡 如何获取音色？</text>
					<text class="close-btn" @click="showHelpModal = false">✕</text>
				</view>
				<scroll-view scroll-y class="help-body">
					<view class="help-step">
						<text class="help-step-title">1️⃣ 点击底部导航"声音克隆"</text>
						<image class="help-image" :src="`${API_BASE}/static/1.jpg`" mode="widthFix"></image>
					</view>
					<view class="help-step">
						<text class="help-step-title">2️⃣ 点击"上传音频"按钮</text>
						<image class="help-image" :src="`${API_BASE}/static/2.jpg`" mode="widthFix"></image>
					</view>
					<view class="help-step">
						<text class="help-step-title">3️⃣ 录制或上传10-20秒清晰人声</text>
						<text class="help-step-desc">建议录制清晰的普通话朗读，避免背景噪音</text>
					</view>
					<view class="help-step">
						<text class="help-step-title">4️⃣ 创建成功后即可在此选择使用</text>
						<text class="help-step-desc">💡 提示：音色质量越好，合成效果越自然</text>
					</view>
				</scroll-view>
				<view class="help-footer">
					<button class="help-btn secondary" @click="showHelpModal = false">知道了</button>
					<button class="help-btn primary" @click="goToUpload">去创建</button>
				</view>
			</view>
		</view>

		<scroll-view scroll-y class="content">
			<view class="tips-card">
				<view class="tips-title">合规提示</view>
				<view class="tips-item">照片中避免：未成年人、明显真人正脸/疑似公众人物、裸露低俗、血腥暴力、涉政涉恐等内容</view>
				<view class="tips-item">避免：平台截图/水印/二维码/账号昵称/大量文字（图片中的文字也会被审核）</view>
				<view class="tips-item">建议：使用清晰、无水印、无UI、背景干净的照片；台词避免敏感词</view>
			</view>

			<!-- 步骤1: 上传照片 -->
			<view class="step-card">
				<view class="step-header">
					<view class="step-number">1</view>
					<text class="step-title">上传照片</text>
				</view>
				<view class="upload-btn" @click="choosePhoto">
					<image 
						v-show="photoPreview" 
						:src="photoPreview" 
						class="preview-img" 
						mode="aspectFill"
						style="width: 100%; height: 100%; position: absolute; top: 0; left: 0; z-index: 10;"
					></image>
					<view v-show="!photoPreview" class="upload-placeholder">
						<text class="icon">📷</text>
						<text>点击选择照片</text>
					</view>
				</view>
			</view>

			<!-- 步骤2: 输入标题 -->
			<view class="step-card">
				<view class="step-header">
					<view class="step-number">2</view>
					<text class="step-title">输入标题</text>
				</view>
				<input class="input-field" v-model="videoTitle" placeholder="给这个视频起个名字..." maxlength="20" />
				<view class="char-count">{{ videoTitle.length }} / 20</view>
				<view class="input-hint">💡 标题将显示在历史记录和AI通话页面</view>
			</view>

			<!-- 步骤3: 选择模型 -->
			<view class="step-card">
				<view class="step-header">
					<view class="step-number">3</view>
					<text class="step-title">选择模型</text>
				</view>
				<picker mode="selector" :range="modelOptions" range-key="label" @change="onModelChange" class="picker-large">
					<view class="picker">
						{{ selectedModelName || '请选择模型' }}
					</view>
				</picker>
				<view v-if="getCurrentModelMaintenanceMsg()" class="maintenance-warning">
					⚠️ {{ getCurrentModelMaintenanceMsg() }}
				</view>
				<view v-else class="input-hint">💡 {{ modelHint }}</view>
			</view>

			<!-- 步骤4: 选择音色 -->
			<view class="step-card">
				<view class="step-header">
					<view class="step-number">4</view>
					<text class="step-title">选择音色</text>
					<text class="help-icon" @click="showVoiceHelp">❓</text>
				</view>

				<!-- 使用已有音色 -->
				<view class="voice-section">
					<picker mode="selector" :range="voiceTypeOptions" range-key="label" @change="onVoiceTypeChange" class="picker-large">
						<view class="picker">
							{{ selectedVoiceTypeLabel }}
						</view>
					</picker>
					<picker mode="selector" :range="displayVoices" range-key="voice_name" @change="onVoiceChange" class="picker-large">
						<view class="picker">
							{{ selectedVoiceName || '请选择音色' }}
						</view>
					</picker>
					<view class="input-hint">💡 请先在"声音克隆"页面创建音色</view>
				</view>

				<view v-if="selectedSupportsDialect && (selectedVoiceType || 'CLONE') !== 'OFFICIAL'" class="voice-section">
					<view class="input-hint">方言</view>
					<picker mode="selector" :range="dialectOptions" @change="onDialectChange" class="picker-large">
						<view class="picker">
							{{ selectedDialect || '请选择方言（可选）' }}
						</view>
					</picker>
				</view>

				<view v-if="selectedSupportsLanguageHints && (selectedVoiceType || 'CLONE') !== 'OFFICIAL'" class="voice-section">
					<view class="input-hint">语言提示（可选）</view>
					<picker mode="selector" :range="languageHintOptions" @change="onLanguageHintChange" class="picker-large">
						<view class="picker">
							{{ selectedLanguageHintLabel || '请选择语言（可选）' }}
						</view>
					</picker>
				</view>
			</view>

			<!-- 步骤5: 输入文本 -->
			<view class="step-card">
				<view class="step-header">
					<view class="step-number">5</view>
					<text class="step-title">输入台词</text>
				</view>
				<textarea 
					class="textarea" 
					v-model="text" 
					placeholder="输入想让照片中的人说的话..." 
					maxlength="500" 
				/>
				<view class="char-count">{{ text.length }} / 500</view>
			</view>

			<!-- 生成按钮 -->
			<button class="generate-btn" :disabled="loading || !canGenerate" @click="handleGenerate">
				<text v-if="loading">{{ loadingText }}</text>
				<text v-else>🎬 开始生成视频</text>
			</button>

			<!-- 进度显示 -->
			<view v-if="progress.length > 0" class="progress-section">
				<view class="progress-title">生成进度</view>
				<view v-for="(item, index) in progress" :key="index" class="progress-item">
					<text :class="['progress-icon', item.status]">
						{{ item.status === 'done' ? '✅' : item.status === 'error' ? '❌' : '⏳' }}
					</text>
					<text class="progress-text">{{ item.text }}</text>
				</view>
			</view>

			<!-- 结果展示 -->
			<view v-if="videoUrl" class="result-section">
				<view class="result-title">🎉 生成成功！</view>
				
				<!-- 视频播放器（静音） -->
				<video 
					id="resultVideo"
					:src="videoUrl" 
					class="result-video" 
					:show-center-play-btn="false"
					:controls="false"
					:muted="true"
					:autoplay="false"
					:loop="false"
					:enable-play-gesture="true"
					:object-fit="'contain'"
					:show-loading="true"
					:enable-progress-gesture="false"
					:poster="photoPreview"
					@play="onVideoPlay"
					@pause="onVideoPause"
					@ended="onVideoEnded"
					@error="onVideoError"
					@loadedmetadata="onVideoLoaded"
					@waiting="onVideoWaiting"
					@timeupdate="onVideoTimeUpdate"
				></video>
				
				<!-- 音频播放器（隐藏） -->
				<audio 
					id="resultAudio"
					:src="audioUrl" 
					style="display: none;"
				></audio>
				
				<!-- 自定义播放控制 -->
				<view class="play-controls">
					<button class="play-btn" @click="viewVideo">
						▶️ 播放
					</button>
				</view>
				
				<button class="download-btn" @click="downloadVideo">
					💾 保存到相册
				</button>
			</view>
		</scroll-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, mapOfficialVoiceDisplay } from '@/utils/payment.js';
import PermissionManager from '@/utils/permission.js';

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

			// 照片
			photoFile: null,
			photoPreview: '',

			// 模型选择
			modelOptions: [
				{ value: 'veo', label: 'Veo', hint: '需要音色，生成说话人视频', disabled: false, maintenanceMsg: '' },
				{ value: 'volcengine', label: '火山引擎', hint: '需要音色，生成说话人视频', disabled: false, maintenanceMsg: '' }
			],
			selectedModel: 'veo',
			selectedModelName: 'Veo',
			modelHint: '需要音色，生成说话人视频',

			// 音色
			voices: [],
			voiceTypeOptions: [
				{ value: 'CLONE', label: '克隆音色' },
				{ value: 'OFFICIAL', label: '官方音色（Flash）' }
			],
			selectedVoiceType: 'CLONE',
			selectedVoiceTypeLabel: '克隆音色',
			selectedVoiceId: '',
			selectedVoiceName: '',
			selectedDialect: '',
			selectedLanguageHint: '',
			selectedLanguageHintLabel: '',
			selectedSupportsDialect: false,
			selectedSupportsLanguageHints: false,
			languageHintOptions: ['中文(zh)', '英文(en)', '法语(fr)', '德语(de)', '日语(ja)', '韩语(ko)', '俄语(ru)'],
			dialectOptions: ['广东话', '东北话', '甘肃话', '贵州话', '河南话', '湖北话', '江西话', '闽南话', '宁夏话', '山西话', '陕西话', '山东话', '上海话', '四川话', '天津话', '云南话'],

			// 标题
			videoTitle: '',

			// 文本
			text: '',

			// 帮助弹窗
			showHelpModal: false,

			// 状态
			loading: false,
			loadingText: '',
			progress: [],

			// 结果
			videoUrl: '',
			audioUrl: '',
			localVideoPath: '', // 本地视频路径
			localAudioPath: '', // 本地音频路径
			isPlaying: false,
			videoRetried: false, // 视频重试标志
			
			// 支付相关
			paymentModalData: {
				show: false,
				serviceType: '',
				serviceName: '',
				serviceDesc: '',
				price: 0,
				orderNo: '',
				paymentTips: '点击确认支付后将开始生成视频'
			},
			_paymentResolve: null,
			_paymentReject: null,
			_paymentOnSuccess: null,
			_paymentOnFailed: null
		};
	},

	computed: {
		displayVoices() {
			const list = (this.voices || []).filter(v => (v && (v.voice_type || 'CLONE') === this.selectedVoiceType));
			if ((this.selectedVoiceType || 'CLONE') !== 'OFFICIAL') return list;
			const regionPrefixMap = {
				'湾区大叔': '台湾',
				'台湾小何': '台湾',
				'双节棍小哥': '台湾',
				'广州德哥': '广州',
				'浩宇小哥': '大陆'
			};
			return list.map(v => {
				const rawName = v && v.voice_name ? String(v.voice_name) : '';
				const prefix = rawName && regionPrefixMap[rawName] ? regionPrefixMap[rawName] : '';
				if (!prefix) return v;
				return { ...v, voice_name: `${prefix}-${rawName}` };
			});
		},
		canGenerate() {
			const currentModel = this.modelOptions.find(m => m.value === this.selectedModel);
			if (currentModel && currentModel.disabled) {
				return false;
			}
			return this.photoFile && this.videoTitle && this.text && this.selectedVoiceId;
		}
	},

	onLoad() {
		this.loadVoices();
		this.loadModelStatus();
	},

	methods: {
		onVoiceTypeChange(e) {
			const option = this.voiceTypeOptions[e.detail.value];
			this.selectedVoiceType = option ? option.value : 'CLONE';
			this.selectedVoiceTypeLabel = option ? option.label : '克隆音色';
			this.selectedVoiceId = '';
			this.selectedVoiceName = '';
			this.selectedDialect = '';
			this.selectedLanguageHint = '';
			this.selectedLanguageHintLabel = '';
			this.selectedSupportsDialect = false;
			this.selectedSupportsLanguageHints = false;
		},
		// 显示音色获取帮助
		showVoiceHelp() {
			this.showHelpModal = true;
		},
		
		// 跳转到上传音频页面
		goToUpload() {
			this.showHelpModal = false;
			uni.navigateTo({
				url: '/pages/upload-audio/upload-audio'
			});
		},
		
		// 获取当前模型的维护信息
		getCurrentModelMaintenanceMsg() {
			const currentModel = this.modelOptions.find(m => m.value === this.selectedModel);
			return currentModel && currentModel.disabled ? currentModel.maintenanceMsg : '';
		},
		
		// 选择模型
		onModelChange(e) {
			const index = e.detail.value;
			const model = this.modelOptions[index];
			this.selectedModel = model.value;
			this.selectedModelName = model.label;
			this.modelHint = model.hint;
			console.log('[Revival] 选择模型:', this.selectedModel);
		},
		
		// 选择照片
		choosePhoto() {
			uni.chooseImage({
				count: 1,
				sizeType: ['compressed'],
				sourceType: ['album', 'camera'],
				success: (res) => {
					this.photoFile = {
						path: res.tempFilePaths[0],
						name: 'photo.jpg'
					};
					this.photoPreview = res.tempFilePaths[0];
				},
				fail: (err) => {
					uni.showToast({
						title: '选择照片失败',
						icon: 'none'
					});
				}
			});
		},
		
		// 选择音频
		chooseAudio() {
			// #ifdef H5
			// H5环境使用文件选择器
			const input = document.createElement('input');
			input.type = 'file';
			input.accept = 'audio/*,.mp3,.wav,.m4a';
			input.onchange = (e) => {
				const file = e.target.files[0];
				if (file) {
					// 创建临时URL
					const tempPath = URL.createObjectURL(file);
					this.audioFile = {
						path: tempPath,
						name: file.name,
						file: file // 保存原始文件对象
					};
					console.log('[Revival] 已选择音频:', file.name);
					uni.showToast({
						title: '已选择: ' + file.name,
						icon: 'none'
					});
				}
			};
			input.click();
			// #endif
			
			// #ifdef APP-PLUS
			// APP环境使用原生文件选择
			uni.showActionSheet({
				itemList: ['选择音频文件', '录音'],
				success: (res) => {
					if (res.tapIndex === 0) {
						// 选择音频文件
						this.chooseAudioFileFromSystem();
					} else if (res.tapIndex === 1) {
						// 使用录音功能
						this.startRecordAudio();
					}
				}
			});
			// #endif
		},
		
		// 从系统选择音频文件（APP专用）
		chooseAudioFileFromSystem() {
			// #ifdef APP-PLUS
			// Android使用Intent打开文件选择器
			if (plus.os.name === 'Android') {
				const Intent = plus.android.importClass('android.content.Intent');
				const main = plus.android.runtimeMainActivity();
				const intent = new Intent(Intent.ACTION_GET_CONTENT);
				intent.setType('audio/*');
				intent.addCategory(Intent.CATEGORY_OPENABLE);
				
				main.startActivityForResult(intent, 1001);
				
				// 监听返回结果
				main.onActivityResult = (requestCode, resultCode, data) => {
					if (requestCode === 1001 && resultCode === -1) {
						const uri = data.getData();
						const uriString = uri.toString();
						
						console.log('[Revival] URI:', uriString);
						
						// 如果是content://格式的URI，需要特殊处理
						if (uriString.startsWith('content://')) {
							this.copyContentUriToLocalForAudio(uri, uriString);
						} else {
							// 使用plus.io转换URI到真实路径
							plus.io.resolveLocalFileSystemURL(uriString, (entry) => {
								const path = entry.fullPath;
								console.log('[Revival] 真实路径:', path);
								
								// 检查文件扩展名
								const fileName = entry.name;
								const ext = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
								
								if (['.mp3', '.wav', '.m4a', '.aac'].indexOf(ext) === -1) {
									uni.showToast({
										title: '请选择音频文件',
										icon: 'none'
									});
									return;
								}
								
								this.audioFile = {
									path: entry.fullPath, // 使用绝对路径
									name: fileName
								};
								
								console.log('[Revival] 已选择音频:', this.audioFile);
								
								uni.showToast({
									title: '已选择: ' + fileName,
									icon: 'success',
									duration: 2000
								});
							}, (error) => {
								console.error('[Revival] 路径转换失败:', error);
								uni.showToast({
									title: '文件路径获取失败',
									icon: 'none'
								});
							});
						}
					}
				};
			} else {
				// iOS暂不支持，提示使用录音
				uni.showModal({
					title: '提示',
					content: 'iOS暂不支持选择文件，请使用录音功能',
					showCancel: false,
					success: () => {
						this.startRecordAudio();
					}
				});
			}
			// #endif
		},
		
		// 复制content URI到本地临时目录（Android专用）
		copyContentUriToLocalForAudio(uri, uriString) {
			// #ifdef APP-PLUS
			try {
				console.log('[Revival] 开始处理content URI...');
				
				const main = plus.android.runtimeMainActivity();
				const contentResolver = main.getContentResolver();
				
				// 获取文件名
				let fileName = 'audio_' + Date.now() + '.mp3';
				
				// 尝试从ContentResolver获取文件名
				try {
					const cursor = plus.android.invoke(contentResolver, 'query', uri, null, null, null, null);
					if (cursor) {
						const moveToFirst = plus.android.invoke(cursor, 'moveToFirst');
						if (moveToFirst) {
							const nameIndex = plus.android.invoke(cursor, 'getColumnIndex', '_display_name');
							if (nameIndex >= 0) {
								fileName = plus.android.invoke(cursor, 'getString', nameIndex);
							}
						}
						plus.android.invoke(cursor, 'close');
					}
				} catch (e) {
					console.log('[Revival] 无法获取文件名，使用默认名称:', e.message);
				}
				
				// 确保文件名有扩展名
				if (fileName.indexOf('.') === -1) {
					fileName += '.mp3';
				}
				
				console.log('[Revival] 文件名:', fileName);
				
				// 检查文件扩展名
				const ext = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
				if (['.mp3', '.wav', '.m4a', '.aac'].indexOf(ext) === -1) {
					uni.showToast({
						title: '请选择音频文件',
						icon: 'none'
					});
					return;
				}
				
				// 创建临时文件路径
				const tempDir = plus.io.convertLocalFileSystemURL('_doc/');
				const tempFilePath = tempDir + fileName;
				
				console.log('[Revival] 临时文件路径:', tempFilePath);
				
				// 导入必要的Java类
				const FileOutputStream = plus.android.importClass('java.io.FileOutputStream');
				const File = plus.android.importClass('java.io.File');
				
				// 打开输入流
				const inputStream = plus.android.invoke(contentResolver, 'openInputStream', uri);
				if (!inputStream) {
					throw new Error('无法打开输入流');
				}
				
				// 创建输出流
				const outputFile = new File(tempFilePath);
				const outputStream = new FileOutputStream(outputFile);
				
				// 复制文件
				const buffer = plus.android.newObject('byte[]', 4096);
				let length;
				while ((length = plus.android.invoke(inputStream, 'read', buffer)) > 0) {
					plus.android.invoke(outputStream, 'write', buffer, 0, length);
				}
				
				// 关闭流
				plus.android.invoke(outputStream, 'flush');
				plus.android.invoke(outputStream, 'close');
				plus.android.invoke(inputStream, 'close');
				
				console.log('[Revival] 文件复制成功');
				
				// 直接使用绝对路径，不使用file://协议
				this.audioFile = {
					path: tempFilePath,
					name: fileName
				};
				
				console.log('[Revival] 文件选择成功:', this.audioFile);
				
				uni.showToast({
					title: '已选择: ' + fileName,
					icon: 'success',
					duration: 2000
				});
			} catch (e) {
				console.error('[Revival] 处理content URI异常:', e);
				uni.showToast({
					title: '文件处理失败: ' + e.message,
					icon: 'none'
				});
			}
			// #endif
		},
		
		// 录音功能（APP专用）
		async startRecordAudio() {
			// #ifdef APP-PLUS
			// 检查麦克风权限
			const hasPermission = await PermissionManager.ensurePermission('record', {
				permissionName: '麦克风',
				reason: '创建音色录音功能',
				showGuide: true
			});
			
			if (!hasPermission) {
				console.log('[Revival] 麦克风权限未授权');
				return;
			}
			
			const recorderManager = uni.getRecorderManager();
			
			recorderManager.onStart(() => {
				console.log('[Revival] 开始录音');
				uni.showToast({
					title: '正在录音...',
					icon: 'none',
					duration: 10000
				});
			});
			
			recorderManager.onStop((res) => {
				console.log('[Revival] 录音完成:', res.tempFilePath);
				this.audioFile = {
					path: res.tempFilePath,
					name: 'record_' + Date.now() + '.mp3'
				};
				uni.showToast({
					title: '录音完成',
					icon: 'success'
				});
			});
			
			recorderManager.onError((err) => {
				console.error('[Revival] 录音失败:', err);
				
				// 判断是否为权限问题
				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
					});
				}
			});
			
			// 显示录音对话框
			uni.showModal({
				title: '录音',
				content: '点击确定开始录音，录音时长10-20秒',
				success: (modalRes) => {
					if (modalRes.confirm) {
						recorderManager.start({
							format: 'mp3',
							sampleRate: 16000
						});
						
						// 20秒后自动停止
						setTimeout(() => {
							recorderManager.stop();
						}, 20000);
					}
				}
			});
			// #endif
		},

		// 加载模型状态
		loadModelStatus() {
			console.log('[Revival] 开始加载模型状态...');
			uni.request({
				url: `${this.API_BASE}/api/system/model-status`,
				method: 'GET',
				success: (res) => {
					console.log('[Revival] 模型状态响应:', res);
					if (res.statusCode === 200 && res.data && res.data.success) {
						const statusData = res.data.data;
						
						// 更新Veo模型状态
						if (statusData.veo_disabled === 'true') {
							const veoModel = this.modelOptions.find(m => m.value === 'veo');
							if (veoModel) {
								veoModel.disabled = true;
								veoModel.maintenanceMsg = statusData.veo_message || '官方接口参数更新，正在处理中，暂不可用';
							}
						}
						
						// 更新火山引擎模型状态
						if (statusData.volcengine_disabled === 'true') {
							const volcengineModel = this.modelOptions.find(m => m.value === 'volcengine');
							if (volcengineModel) {
								volcengineModel.disabled = true;
								volcengineModel.maintenanceMsg = statusData.volcengine_message || '官方接口参数更新，正在处理中，暂不可用';
							}
						}
						
						console.log('[Revival] 模型状态已更新:', this.modelOptions);
					}
				},
				fail: (error) => {
					console.error('[Revival] 加载模型状态失败:', error);
				}
			});
		},
		
		// 加载音色列表
		loadVoices() {
			console.log('[Revival] 开始加载音色列表...');
			console.log('[Revival] API地址:', `${this.API_BASE}/api/voice/list`);
			
			// 获取用户ID和Token
			const userId = uni.getStorageSync('userId') || '';
			const token = uni.getStorageSync('token') || '';
			console.log('[Revival] 用户ID:', userId);
			
			uni.request({
				url: `${this.API_BASE}/api/voice/list`,
				method: 'GET',
				header: {
					'X-User-Id': userId,
					'Authorization': token ? `Bearer ${token}` : ''
				},
				success: (res) => {
					console.log('[Revival] API响应状态码:', res.statusCode);
					console.log('[Revival] API响应数据:', res.data);
					
					if (res.statusCode === 200) {
						if (res.data && res.data.voices) {
							const hiddenVoices = ['Cherry', 'Kai', 'Mochi', 'Bunny'];
							this.voices = (res.data.voices || [])
								.filter(v => !(v && v.voice && hiddenVoices.includes(v.voice)));
							console.log('[Revival] 音色列表:', this.voices);
							console.log('[Revival] 音色数量:', this.voices.length);
							
							if (this.voices.length > 0) {
								this.selectedVoiceId = this.voices[0].voice;
								this.selectedVoiceName = this.voices[0].voice_name || this.voices[0].voice;
								this.selectedDialect = '';
								console.log('[Revival] 默认选中音色:', this.selectedVoiceId);
							} else {
								console.log('[Revival] 音色列表为空');
								uni.showToast({
									title: '暂无可用音色，请先创建',
									icon: 'none'
								});
							}
						} else {
							console.error('[Revival] 响应数据格式错误:', res.data);
							this.voices = [];
						}
					} else {
						console.error('[Revival] 请求失败，状态码:', res.statusCode);
					}
				},
				fail: (error) => {
					console.error('[Revival] 加载音色列表失败:', error);
					uni.showToast({
						title: '加载音色列表失败',
						icon: 'none'
					});
				}
			});
		},

		// 选择音色
		onVoiceChange(e) {
			const voice = this.displayVoices[e.detail.value];
			this.selectedVoiceId = voice.voice;
			this.selectedVoiceName = voice.voice_name || voice.voice;
			this.selectedVoiceType = voice.voice_type || this.selectedVoiceType || 'CLONE';
			this.selectedVoiceTypeLabel = this.selectedVoiceType === 'OFFICIAL' ? '官方音色（Flash）' : '克隆音色';
			this.selectedDialect = '';
			this.selectedLanguageHint = '';
			this.selectedLanguageHintLabel = '';
			this.selectedSupportsDialect = !!voice.supportsDialect;
			this.selectedSupportsLanguageHints = !!voice.supportsLanguageHints;
			if ((this.selectedVoiceType || 'CLONE') === 'OFFICIAL') {
				this.selectedDialect = '';
				this.selectedLanguageHint = '';
				this.selectedLanguageHintLabel = '';
				this.selectedSupportsDialect = false;
				this.selectedSupportsLanguageHints = false;
			}
		},
		
		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] : '';
		},

		// 生成视频
		async handleGenerate() {
			const userId = uni.getStorageSync('userId') || '';
			const token = uni.getStorageSync('token') || '';
			if (!userId) {
				uni.showModal({
					title: '提示',
					content: '请先登录后再继续',
					confirmText: '去登录',
					cancelText: '取消',
					success: (m) => {
						if (m.confirm) {
							uni.navigateTo({ url: '/pages/login/login' });
						}
					}
				});
				return;
			}

			// 按模型区分 serviceType：Veo -> PHOTO_REVIVAL；火山 -> VOLCENGINE_VIDEO
			const backendServiceType = (this.selectedModel === 'volcengine') ? 'VOLCENGINE_VIDEO' : 'PHOTO_REVIVAL';
			const frontendServiceType = (this.selectedModel === 'volcengine') ? SERVICE_TYPES.VOLCENGINE_VIDEO.type : SERVICE_TYPES.PHOTO_REVIVAL.type;

			// 强制预检：必须成功拿到剩余次数，否则不允许继续
			let preview = null;
			try {
				preview = await new Promise((resolve, reject) => {
					uni.request({
						url: `${API_BASE}/api/pay/usage-preview?serviceType=${backendServiceType}`,
						method: 'GET',
						header: {
							'X-User-Id': userId,
							'Authorization': token ? `Bearer ${token}` : ''
						},
						success: (res) => {
							if (res.statusCode === 200 && res.data && res.data.success) {
								resolve(res.data);
								return;
							}
							reject(new Error((res.data && res.data.message) ? res.data.message : ('预检失败: HTTP ' + res.statusCode)));
						},
						fail: (err) => reject(new Error((err && err.errMsg) ? err.errMsg : '预检失败'))
					});
				});
			} catch (e) {
				uni.showModal({
					title: '检查次数失败',
					content: (e && e.message) ? e.message : '检查剩余次数失败，请稍后重试',
					showCancel: false,
					confirmText: '我知道了'
				});
				return;
			}

			const remaining = (preview && typeof preview.remainingTotalCount === 'number') ? preview.remainingTotalCount : 0;
			if (remaining <= 0) {
				showPaymentModal(
					this,
					frontendServiceType,
					() => {
						this.executeGenerate();
					},
					(error) => {
						console.error('[Payment] 支付失败:', error);
					}
				);
				return;
			}

			this.executeGenerate();
		},
		
		async executeGenerate() {
			this.loading = true;
			this.progress = [];
			this.videoUrl = '';
			this.audioUrl = ''; // 清空之前的音频URL

			try {
				// 根据选择的模型调用不同的生成方法
				if (this.selectedModel === 'volcengine') {
					await this.generateWithVolcengine();
				} else {
					await this.generateWithExistingVoice();
				}
			} catch (error) {
				const isTimeout = error.message.includes('响应超时') || error.message.includes('历史记录');
				
				if (isTimeout) {
					// 超时错误，提示用户查看历史记录
					this.addProgress('⚠️ ' + error.message, 'warning');
					uni.showModal({
						title: '提示',
						content: error.message,
						showCancel: true,
						cancelText: '知道了',
						confirmText: '查看历史',
						success: (res) => {
							if (res.confirm) {
								uni.navigateTo({
									url: '/pages/revival/revival-history'
								});
							}
						}
					});
				} else {
					// 其他错误
					this.addProgress('生成失败: ' + error.message, 'error');
					uni.showModal({
						title: '生成失败',
						content: (error.message || '生成失败') + '\n\n你可以点击“重试”再次生成。\n若已支付但多次生成失败，请联系客服补发（提供订单号/支付时间截图）。',
						confirmText: '重试',
						cancelText: '我知道了',
						success: (m) => {
							if (m.confirm) {
								this.executeGenerate();
							}
						}
					});
				}
			} finally {
				this.loading = false;
			}
		},

		// 使用火山引擎生成视频
		async generateWithVolcengine() {
			return new Promise((resolve, reject) => {
				this.addProgress('📤 正在上传照片...', 'loading');
				
				// 显示提示
				uni.showModal({
					title: '🎬 视频生成中',
					content: '火山引擎视频生成通常需要1-3分钟。\n\n您可以最小化APP做其他事情，生成完成后会弹出提示。\n\n请保持网络连接稳定。',
					showCancel: false,
					confirmText: '我知道了'
				});
				
				// 延迟添加后续进度提示
				setTimeout(() => {
					this.addProgress('🎬 正在生成视频（通常1-3分钟）...', 'loading');
				}, 2000);

				// 获取用户token
				const userId = uni.getStorageSync('userId') || '';
				const token = uni.getStorageSync('token') || '';

				console.log('[Revival-Volcengine] 准备上传照片:', this.photoFile);
				console.log('[Revival-Volcengine] 用户ID:', userId);
				console.log('[Revival-Volcengine] API地址:', `${this.API_BASE}/api/photo-revival/volcengine-video`);

				uni.uploadFile({
					url: `${this.API_BASE}/api/photo-revival/volcengine-video`,
					filePath: this.photoFile.path,
					name: 'photo',
					timeout: 3600000, // 60分钟超时
					header: {
						'X-User-Id': userId,
						'Authorization': token ? `Bearer ${token}` : ''
					},
					formData: (() => {
						const data = {
							voiceId: this.selectedVoiceId,
							voiceType: this.selectedVoiceType,
							text: this.text,
							duration: 8, // 默认8秒
							name: this.videoTitle
						};
						if (this.selectedSupportsDialect && this.selectedDialect) {
							data.dialect = this.selectedDialect;
						}
						if (this.selectedSupportsLanguageHints && this.selectedLanguageHint) {
							data.languageHints = this.selectedLanguageHint;
						}
						return data;
					})(),
					success: (res) => {
						console.log('[Revival-Volcengine] 上传响应:', res);
						let parsed = null;
						try {
							parsed = JSON.parse(res.data);
						} catch (e) {
							parsed = null;
						}

						if (res.statusCode === 402) {
							console.warn('[Revival-Volcengine] HTTP 402：次数不足，需要付费');
							this.addProgress('⚠️ 次数不足，请先付费', 'warning');
							uni.showModal({
								title: '需要付费',
								content: (parsed && parsed.message) ? parsed.message : '次数不足，请先完成支付后再生成',
								confirmText: '去支付',
								cancelText: '取消',
								success: (m) => {
									if (m.confirm) {
										showPaymentModal(
											this,
											SERVICE_TYPES.PHOTO_REVIVAL.type,
											() => {
												uni.showToast({ title: '支付成功，请重新点击生成', icon: 'none' });
											},
											() => {}
										);
									}
								}
							});
							reject(new Error((parsed && parsed.message) ? parsed.message : '次数不足，请先付费'));
							return;
						}

						if (res.statusCode === 504) {
							console.warn('[Revival-Volcengine] HTTP 504：请求超时，但后端可能仍在生成中');
							setTimeout(() => {
								uni.showModal({
									title: '⏰ 响应超时',
									content: '网络响应超时（HTTP 504），但视频可能仍在后台生成中。\n\n是否立即查看历史记录？',
									confirmText: '查看历史',
									cancelText: '稍后查看',
									success: (modalRes) => {
										if (modalRes.confirm) {
											uni.navigateTo({
												url: '/pages/revival/revival-history'
											});
										}
									}
								});
							}, 800);
							this.addProgress('⏳ 网络响应超时（HTTP 504），视频可能仍在后台生成中，请稍后到历史记录查看', 'loading');
							resolve();
							return;
						}

						if (res.statusCode === 200) {
							if (parsed && parsed.status === 'success') {
								this.addProgress('✅ 视频生成完成', 'done');
								this.videoRetried = false;
								uni.showToast({
									title: '生成成功！',
									icon: 'success'
								});
								resolve();
								return;
							}
							const errorMsg = (parsed && parsed.message) ? parsed.message : '生成失败';
							console.error('[Revival-Volcengine] 后端返回错误:', errorMsg);
							reject(new Error(errorMsg));
							return;
						}

						const errorMsg = (parsed && parsed.message)
							? parsed.message
							: ('上传失败: HTTP ' + res.statusCode);
						reject(new Error(errorMsg));
					},
					fail: (err) => {
						console.error('[Revival-Volcengine] 上传响应超时:', err);
						
						// 超时不一定是失败，后端可能仍在处理
						// 延迟3秒后自动刷新历史记录，检查视频是否已生成
						setTimeout(() => {
							uni.showModal({
								title: '⏰ 响应超时',
								content: '网络响应超时，但视频可能已经生成成功。\n\n是否立即查看历史记录？',
								confirmText: '查看历史',
								cancelText: '稍后查看',
								success: (modalRes) => {
									if (modalRes.confirm) {
										uni.navigateTo({
											url: '/pages/revival/revival-history'
										});
									}
								}
							});
						}, 3000);
						
						reject(new Error('网络响应超时，视频可能正在生成中，请稍后在历史记录中查看'));
					}
				});
			});
		},

		// 使用新音色生成
		async generateWithNewVoice() {
			this.addProgress('正在创建音色...', 'loading');

			// #ifdef APP-PLUS
			// APP环境暂不支持创建新音色（需要同时上传两个文件）
			uni.showModal({
				title: '提示',
				content: 'APP环境暂不支持创建新音色，请使用"已有音色"模式，或在电脑端创建音色后使用',
				showCancel: false
			});
			throw new Error('APP环境暂不支持创建新音色');
			// #endif

			// #ifdef H5
			try {
				// H5环境使用FormData
				const formData = new FormData();
				
				// 添加照片文件
				if (this.photoFile.file) {
					formData.append('photo', this.photoFile.file);
				} else {
					const photoBlob = await fetch(this.photoFile.path).then(r => r.blob());
					formData.append('photo', photoBlob, 'photo.jpg');
				}
				
				// 添加音频文件
				if (this.audioFile.file) {
					formData.append('audioSample', this.audioFile.file);
				}
				
				// 添加其他参数
				formData.append('voiceId', this.selectedVoiceId);
				formData.append('voiceType', this.selectedVoiceType);
				formData.append('text', this.text);
				formData.append('voiceName', this.videoTitle);
				if (this.selectedSupportsDialect && this.selectedDialect) {
					formData.append('dialect', this.selectedDialect);
				}
				if (this.selectedSupportsLanguageHints && this.selectedLanguageHint) {
					formData.append('languageHints', this.selectedLanguageHint);
				}
				formData.append('serverUrl', this.API_BASE);

				// 发送请求
				const response = await fetch(`${this.API_BASE}/api/photo-revival/revive`, {
					method: 'POST',
					body: formData
				});

				if (!response.ok) {
					throw new Error('上传失败: ' + response.status);
				}

				const result = await response.json();
				this.handleResult(result);
			} catch (error) {
				console.error('[Revival] 生成失败:', error);
				throw error;
			}
			// #endif
		},

		// 使用已有音色生成
		generateWithExistingVoice() {
			return new Promise((resolve, reject) => {
				this.addProgress('⏳ 正在上传照片...', 'loading');
				
				// 显示详细的等待提示
				uni.showModal({
					title: '⏳ 视频生成中',
					content: '视频生成通常需要2-5分钟，高峰期可能需要10-30分钟。\n\n您可以最小化APP做其他事情，生成完成后会弹出提示。\n\n请保持网络连接稳定。',
					showCancel: false,
					confirmText: '我知道了'
				});
				
				// 延迟添加后续进度提示
				setTimeout(() => {
					this.addProgress('🎵 正在合成语音...', 'loading');
				}, 2000);
				
				setTimeout(() => {
					this.addProgress('🎬 正在生成视频（通常2-5分钟，高峰期可能更久）...', 'loading');
				}, 5000);

				// 获取用户token
				const userId = uni.getStorageSync('userId') || '';
				const token = uni.getStorageSync('token') || '';

				console.log('[Revival] 准备上传照片:', this.photoFile);
				console.log('[Revival] 照片路径:', this.photoFile.path);
				console.log('[Revival] 用户ID:', userId);
				console.log('[Revival] Token:', token ? '已设置' : '未设置');
				console.log('[Revival] API地址:', `${this.API_BASE}/api/photo-revival/revive-quick`);
				
				// 检查userId是否存在
				if (!userId) {
					console.warn('[Revival] ⚠️ 警告：用户ID为空，视频可能无法正确保存到数据库');
					uni.showModal({
						title: '提示',
						content: '检测到您未登录，视频将无法保存到历史记录。是否继续？',
						success: (modalRes) => {
							if (!modalRes.confirm) {
								reject(new Error('用户取消操作'));
								return;
							}
						}
					});
				}

				uni.uploadFile({
					url: `${this.API_BASE}/api/photo-revival/revive-quick`,
					filePath: this.photoFile.path,
					name: 'photo',
					timeout: 3600000, // 60分钟超时（视频生成需要较长时间）
					header: {
						'X-User-Id': userId,
						'Authorization': token ? `Bearer ${token}` : ''
					},
					formData: (() => {
						const data = {
							voiceId: this.selectedVoiceId,
							voiceType: this.selectedVoiceType,
							text: this.text,
							name: this.videoTitle,
							serverUrl: this.API_BASE
						};
						if (this.selectedSupportsDialect && this.selectedDialect) {
							data.dialect = this.selectedDialect;
						}
						if (this.selectedSupportsLanguageHints && this.selectedLanguageHint) {
							data.languageHints = this.selectedLanguageHint;
						}
						return data;
					})(),
					success: (res) => {
						console.log('[Revival] 上传响应:', res);
						let parsed = null;
						try {
							parsed = JSON.parse(res.data);
						} catch (e) {
							parsed = null;
						}

						if (res.statusCode === 504) {
							console.warn('[Revival] HTTP 504：请求超时，但后端可能仍在生成中');
							setTimeout(() => {
								uni.showModal({
									title: '⏰ 响应超时',
									content: '网络响应超时（HTTP 504），但视频可能仍在后台生成中。\n\n是否立即查看历史记录？',
									confirmText: '查看历史',
									cancelText: '稍后查看',
									success: (modalRes) => {
										if (modalRes.confirm) {
											uni.navigateTo({
												url: '/pages/revival/revival-history'
											});
										}
									}
								});
							}, 800);
							this.addProgress('⏳ 网络响应超时（HTTP 504），视频可能仍在后台生成中，请稍后到历史记录查看', 'loading');
							resolve();
							return;
						}

						if (res.statusCode === 200) {
							if (parsed && parsed.status === 'success') {
								this.handleResult(parsed);
								resolve();
								return;
							}
							const errorMsg = (parsed && parsed.message) ? parsed.message : '生成失败';
							console.error('[Revival] 后端返回错误:', errorMsg);
							reject(new Error(errorMsg));
							return;
						}

						// 非 200：尽量解析后端 message
						const errorMsg = (parsed && parsed.message)
							? parsed.message
							: ('上传失败: HTTP ' + res.statusCode);
						reject(new Error(errorMsg));
					},
					fail: (err) => {
						console.error('[Revival] 上传响应超时:', err);
						
						// 超时不一定是失败，后端可能仍在处理
						// 延迟3秒后提示用户查看历史记录
						setTimeout(() => {
							uni.showModal({
								title: '⏰ 响应超时',
								content: '网络响应超时，但视频可能已经生成成功。\n\n是否立即查看历史记录？',
								confirmText: '查看历史',
								cancelText: '稍后查看',
								success: (modalRes) => {
									if (modalRes.confirm) {
										uni.navigateTo({
											url: '/pages/revival/revival-history'
										});
									}
								}
							});
						}, 3000);
						
						// 上传超时不一定是失败，可能是后端正在处理
						// 提示用户稍后在历史记录中查看
						reject(new Error('网络响应超时，视频可能正在生成中，请稍后在历史记录中查看'));
					}
				});
			});
		},

		// 处理结果
		async handleResult(result) {
			if (result.status === 'success') {
				if (this.mode === 'new') {
					this.addProgress('✅ 音色创建完成', 'done');
				}
				this.addProgress('✅ 语音合成完成', 'done');
				this.addProgress('✅ 视频生成完成', 'done');
				
				// 保存远程URL
				this.videoUrl = result.videoUrl;
				this.audioUrl = result.audioUrl;
				this.videoRetried = false;
				
				// 保存到历史记录（直接保存可播放的URL）
				this.saveToHistory(result);

				// 显示成功弹窗
				uni.showModal({
					title: '🎉 生成成功！',
					content: '视频已生成完成！\n\n点击"查看视频"可以立即查看和播放视频。',
					cancelText: '稍后查看',
					confirmText: '查看视频',
					success: (modalRes) => {
						if (modalRes.confirm) {
							this.scrollToResult();
						}
					}
				});

				// 刷新音色列表
				if (this.mode === 'new') {
					this.loadVoices();
				}
			} else {
				throw new Error(result.message || '生成失败');
			}
		},
		
		// 播放/暂停控制
		togglePlay() {
			if (this.isPlaying) {
				this.pausePlayback();
			} else {
				this.startPlayback();
			}
		},
		
		// 开始播放
		startPlayback() {
			// 获取视频和音频元素
			const videoContext = uni.createVideoContext('resultVideo', this);
			const audioContext = uni.createInnerAudioContext();
			audioContext.src = this.audioUrl;
			
			// 同时播放视频和音频
			videoContext.play();
			audioContext.play();
			
			this.isPlaying = true;
			this.audioContext = audioContext; // 保存音频上下文
		},
		
		// 暂停播放
		pausePlayback() {
			const videoContext = uni.createVideoContext('resultVideo', this);
			videoContext.pause();
			
			if (this.audioContext) {
				this.audioContext.pause();
			}
			
			this.isPlaying = false;
		},
		
		// 视频播放事件
		onVideoPlay() {
			this.isPlaying = true;
		},
		
		// 视频暂停事件
		onVideoPause() {
			this.isPlaying = false;
			if (this.audioContext) {
				this.audioContext.pause();
			}
		},
		
		// 视频结束事件
		onVideoEnded() {
			this.isPlaying = false;
			if (this.audioContext) {
				this.audioContext.stop();
			}
		},
		
		// 视频加载成功
		onVideoLoaded(e) {
			console.log('[Revival] 视频加载成功:', e);
		},
		
		// 视频加载错误
		onVideoError(e) {
			console.error('[Revival] 视频加载失败:', e);
			console.error('[Revival] 视频URL:', this.videoUrl);
			console.error('[Revival] 错误详情:', e.detail);
			
			// 尝试重新加载视频（仅尝试一次）
			if (!this.videoRetried) {
				console.log('[Revival] 尝试重新加载视频...');
				this.videoRetried = true;
				setTimeout(() => {
					const videoContext = uni.createVideoContext('resultVideo', this);
					videoContext.stop();
					// 强制刷新视频URL
					const currentUrl = this.videoUrl;
					this.videoUrl = '';
					this.$nextTick(() => {
						this.videoUrl = currentUrl + '?t=' + Date.now();
					});
				}, 500);
				return;
			}
			
			uni.showModal({
				title: '视频加载失败',
				content: '无法播放视频，可能是网络或格式问题。\n\n您可以尝试：\n1. 检查网络连接\n2. 返回后重新进入播放\n3. 重新生成视频或稍后再试',
				showCancel: false
			});
		},
		
		// 视频等待加载
		onVideoWaiting(e) {
			console.log('[Revival] 视频缓冲中...', e);
		},
		
		// 视频时间更新
		onVideoTimeUpdate(e) {
			// 可以在这里更新播放进度
		},
		
		// 滚动到结果区域
		scrollToResult() {
			// 使用nextTick确保DOM已更新
			this.$nextTick(() => {
				// 创建选择器查询
				const query = uni.createSelectorQuery().in(this);
				query.select('.result-section').boundingClientRect(data => {
					if (data) {
						// 滚动到结果区域
						uni.pageScrollTo({
							scrollTop: data.top - 100,
							duration: 300
						});
					}
				}).exec();
			});
		},

		// 添加进度
		addProgress(text, status) {
			this.progress.push({ text, status });
			this.loadingText = text;
		},

		// 跳转到历史记录
		goToHistory() {
			uni.navigateTo({
				url: '/pages/revival/revival-history'
			});
		},
		
		// 播放视频（跳转到播放页面，与历史记录逻辑一致）
		viewVideo() {
			console.log('[Revival] 准备播放视频');
			console.log('[Revival] videoUrl:', this.videoUrl);
			console.log('[Revival] audioUrl:', this.audioUrl);

			if (!this.videoUrl) {
				uni.showToast({
					title: '视频地址不存在',
					icon: 'none'
				});
				return;
			}
			
			let targetUrl = `/pages/video-player/video-player?url=${encodeURIComponent(this.videoUrl)}&title=${encodeURIComponent('复活视频')}`;
			targetUrl += `&audioInVideo=1`;
			console.log('[Revival] 跳转播放页:', targetUrl);

			// 跳转到视频播放页面
			uni.navigateTo({
				url: targetUrl,
				success: () => {
					console.log('[Revival] 跳转播放页面成功');
				},
				fail: (err) => {
					console.error('[Revival] 跳转播放页面失败:', err);
					uni.showToast({
						title: '跳转失败',
						icon: 'none'
					});
				}
			});
		},
		
		// 支付相关方法
		handlePaymentClose() {
			this.paymentModalData.show = false;
			if (this._paymentReject) {
				this._paymentReject(new Error('用户取消支付'));
			}
		},
		
		async handlePaymentConfirm(paymentData) {
			await processPayment(this, paymentData);
		},

		// 手动保存到历史记录（用于测试）
		manualSaveToHistory() {
			if (!this.videoUrl) {
				uni.showToast({
					title: '没有可保存的记录',
					icon: 'none'
				});
				return;
			}

			const result = {
				photoUrl: this.photoPreview,
				videoUrl: this.videoUrl,
				audioUrl: this.audioUrl,
				voiceId: this.selectedVoiceId
			};

			this.saveToHistory(result);

			uni.showToast({
				title: '已保存到历史',
				icon: 'success'
			});
		},

		// 下载视频到本地
		async downloadVideoToLocal(videoUrl) {
			try {
				console.log('[Revival] 开始下载视频到本地:', videoUrl);
				
				// 使用uni.downloadFile下载视频
				const downloadResult = await new Promise((resolve, reject) => {
					uni.downloadFile({
						url: videoUrl,
						success: (res) => {
							if (res.statusCode === 200) {
								console.log('[Revival] 视频下载成功:', res.tempFilePath);
								resolve(res.tempFilePath);
							} else {
								reject(new Error('下载失败，状态码: ' + res.statusCode));
							}
						},
						fail: (err) => {
							console.error('[Revival] 视频下载失败:', err);
							reject(err);
						}
					});
				});
				
				// 保存到永久存储
				const savedPath = await this.saveFilePermanently(downloadResult, 'video');
				this.localVideoPath = savedPath;
				
				console.log('[Revival] 视频已保存到:', savedPath);
			} catch (e) {
				console.error('[Revival] 下载视频失败:', e);
				uni.showToast({
					title: '视频下载失败，将使用在线播放',
					icon: 'none'
				});
			}
		},
		
		// 保存文件到永久存储
		async saveFilePermanently(tempPath, type) {
			return new Promise((resolve, reject) => {
				const fs = uni.getFileSystemManager();
				const savedDir = `${uni.env.USER_DATA_PATH}/revival_${type}s/`;
				const fileName = `${type}_${Date.now()}.${type === 'video' ? 'mp4' : 'mp3'}`;
				const savedPath = savedDir + fileName;
				
				// 创建目录
				fs.mkdir({
					dirPath: savedDir,
					recursive: true,
					success: () => {
						// 复制文件
						fs.copyFile({
							srcPath: tempPath,
							destPath: savedPath,
							success: () => {
								console.log(`[Revival] ${type}已保存到永久存储:`, savedPath);
								resolve(savedPath);
							},
							fail: (err) => {
								console.error(`[Revival] 保存${type}失败:`, err);
								// 如果复制失败，直接使用临时路径
								resolve(tempPath);
							}
						});
					},
					fail: (err) => {
						console.error('[Revival] 创建目录失败:', err);
						// 如果创建目录失败，直接使用临时路径
						resolve(tempPath);
					}
				});
			});
		},

		// 保存到历史记录
		saveToHistory(result) {
			try {
				console.log('[Revival] 开始保存历史记录');
				console.log('[Revival] 结果数据:', result);
				
				// 读取现有历史记录
				let history = [];
				const historyData = uni.getStorageSync('generation_history');
				if (historyData) {
					history = JSON.parse(historyData);
				}

				// 添加新记录（直接保存可播放的URL）
				const record = {
					type: 'revival',
					timestamp: Date.now(),
					text: this.text,
					voiceId: this.mode === 'existing' ? this.selectedVoiceId : (result.voiceId || this.selectedVoiceId),
					photoUrl: this.photoPreview, // 使用本地预览URL
					videoUrl: result.videoUrl,
					audioUrl: result.audioUrl
				};

				console.log('[Revival] 保存记录:', record);

				history.unshift(record); // 添加到开头

				// 限制历史记录数量（最多保存100条）
				if (history.length > 100) {
					history = history.slice(0, 100);
				}

				// 保存到本地存储
				uni.setStorageSync('generation_history', JSON.stringify(history));
				console.log('[Revival] 已保存到历史记录，当前共', history.length, '条');
			} catch (e) {
				console.error('[Revival] 保存历史记录失败:', e);
			}
		},

		// 下载视频
		downloadVideo() {
			uni.showLoading({
				title: '正在保存...'
			});

			uni.downloadFile({
				url: this.videoUrl,
				success: (res) => {
					if (res.statusCode === 200) {
						uni.saveVideoToPhotosAlbum({
							filePath: res.tempFilePath,
							success: () => {
								uni.hideLoading();
								uni.showToast({
									title: '保存成功',
									icon: 'success'
								});
							},
							fail: (err) => {
								uni.hideLoading();
								uni.showToast({
									title: '保存失败: ' + err.errMsg,
									icon: 'none'
								});
							}
						});
					} else {
						uni.hideLoading();
						uni.showToast({
							title: '下载失败',
							icon: 'none'
						});
					}
				},
				fail: (err) => {
					uni.hideLoading();
					uni.showToast({
						title: '下载失败: ' + err.errMsg,
						icon: 'none'
					});
				}
			});
		},
		
		// 开始视频通话
		startVideoCall() {
			if (!this.videoUrl || !this.selectedVoiceId) {
				uni.showToast({
					title: '缺少必要信息',
					icon: 'none'
				});
				return;
			}
			
			const callerName = this.mode === 'new' ? this.voiceName : '对方';
			
			uni.navigateTo({
				url: `/pages/video-call/video-call?idleVideo=${encodeURIComponent(this.videoUrl)}&talkingVideo=${encodeURIComponent(this.videoUrl)}&voiceId=${this.selectedVoiceId}&callerName=${encodeURIComponent(callerName)}`
			});
		}
	}
};
</script>

<style lang="scss" scoped>
.revival-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;
}

.hero {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 32upx 32upx 12upx;
	gap: 20upx;
	max-width: 1000upx;
	margin: 0 auto;
}

.hero-text {
	display: flex;
	flex-direction: column;
	gap: 8upx;
}

.hero-title {
	font-size: 44upx;
	font-weight: 700;
	color: #8B7355;
	letter-spacing: 2upx;
}

.hero-subtitle {
	font-size: 28upx;
	color: #666;
}

.history-btn {
	padding: 16rpx 32rpx;
	background: linear-gradient(135deg, #8B7355 0%, #6D8B8B 100%);
	color: white;
	border: none;
	border-radius: 36rpx;
	font-size: 26rpx;
	box-shadow: 0 4upx 12upx rgba(139, 115, 85, 0.3);
	transition: all 0.3s;
}

.history-btn:active {
	transform: translateY(-2upx);
	box-shadow: 0 6upx 16upx rgba(139, 115, 85, 0.4);
}

.content {
	flex: 1;
	background: transparent;
	padding: 32upx;
	max-width: 650upx;
	margin: 0 auto 40upx auto;
	display: flex;
	flex-direction: column;
	gap: 28upx;
	align-items: center;
}

 .tips-card {
 	width: 100%;
 	max-width: 580upx;
 	background: rgba(255, 255, 255, 0.95);
 	border-radius: 20upx;
 	padding: 22upx 24upx;
 	box-shadow: 0 6upx 20upx rgba(0, 0, 0, 0.06);
 	border: 2upx solid rgba(139, 115, 85, 0.12);
 }

 .tips-title {
 	font-size: 28upx;
 	font-weight: 700;
 	color: #8B7355;
 	margin-bottom: 12upx;
 }

 .tips-item {
 	font-size: 24upx;
 	color: #666;
 	line-height: 1.6;
 	margin-top: 8upx;
 }

.step-card {
	width: 100%;
	max-width: 580upx;
	background: rgba(255, 255, 255, 0.95);
	border-radius: 20upx;
	padding: 28upx 24upx;
	margin: 0;
	box-shadow: 0 6upx 20upx rgba(0, 0, 0, 0.08);
	backdrop-filter: blur(12upx);
	border: 2upx solid rgba(139, 115, 85, 0.12);
}

.step-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin-bottom: 24rpx;
}

.step-number {
	width: 44upx;
	height: 44upx;
	background: linear-gradient(135deg, #8B7355 0%, #6D8B8B 100%);
	color: white;
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: bold;
	margin-right: 14upx;
	font-size: 26upx;
	box-shadow: 0 3upx 10upx rgba(139, 115, 85, 0.25);
}

.step-title {
	font-size: 30rpx;
	font-weight: bold;
	color: #2C2C2C;
	flex: 1;
}

.help-icon {
	font-size: 32upx;
	color: #8B7355;
	padding: 10upx;
	margin-left: 10upx;
	transition: all 0.3s;
}

.help-icon:active {
	transform: scale(1.2);
	color: #6D8B8B;
}

.upload-btn {
	width: 100%;
	min-height: 700upx;
	border: 3upx dashed #D4B996;
	border-radius: 16upx;
	background: rgba(212, 185, 150, 0.05);
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0;
	overflow: hidden;
	position: relative;
	transition: all 0.3s;
	
	&::before {
		content: '';
		position: absolute;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		background: 
			radial-gradient(circle at 30% 30%, rgba(139, 115, 85, 0.08) 0%, transparent 50%),
			radial-gradient(circle at 70% 70%, rgba(109, 139, 139, 0.08) 0%, transparent 50%);
		z-index: 0;
	}
	
	&:active {
		border-color: #8B7355;
		background: rgba(212, 185, 150, 0.1);
	}
}

.preview-img {
	width: 100%;
	height: 700upx;
	border-radius: 14rpx;
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 1;
}

.upload-placeholder {
	display: flex;
	flex-direction: column;
	align-items: center;
	color: #8B7355;
	gap: 12upx;
}

.icon {
	font-size: 72rpx;
}

.mode-selector {
	display: flex;
	gap: 20rpx;
	margin-bottom: 24rpx;
}

.mode-btn {
	flex: 1;
	padding: 24upx;
	border: 2upx solid rgba(212, 185, 150, 0.3);
	border-radius: 30upx;
	background: rgba(255, 255, 255, 0.7);
	font-size: 28upx;
	color: #666;
	transition: all 0.3s;
	box-shadow: 0 2upx 8upx rgba(0, 0, 0, 0.05);
}

.mode-btn.active {
	background: linear-gradient(135deg, #8B7355 0%, #6D8B8B 100%);
	color: white;
	border-color: transparent;
	box-shadow: 0 8upx 24upx rgba(139, 115, 85, 0.3);
}

.voice-section {
	display: flex;
	flex-direction: column;
	gap: 20rpx;
}

.upload-btn-small {
	padding: 24rpx;
	border: 2rpx solid #e0e0e0;
	border-radius: 12rpx;
	background: #f8f9ff;
	font-size: 28rpx;
	color: #666;
}

.input,
.picker {
	padding: 24rpx;
	border: 2rpx solid #e0e0e0;
	border-radius: 12rpx;
	font-size: 28rpx;
	background: white;
}

.picker-large {
	width: 100%;
}

.picker-large .picker {
	padding: 32rpx 24rpx;
	font-size: 32rpx;
	font-weight: 500;
}

.input-hint {
	font-size: 24rpx;
	color: #999;
	margin-top: 16rpx;
	line-height: 1.6;
}

.maintenance-warning {
	font-size: 24rpx;
	color: #ff6b6b;
	background: rgba(255, 107, 107, 0.1);
	padding: 16rpx;
	border-radius: 8rpx;
	margin-top: 16rpx;
	line-height: 1.6;
	border: 1rpx solid rgba(255, 107, 107, 0.3);
}

.input-field {
	width: 100%;
	height: 90rpx;
	padding: 24rpx;
	border: 2rpx solid rgba(139, 115, 85, 0.2);
	border-radius: 12rpx;
	font-size: 32rpx;
	box-sizing: border-box;
	background: white;
	transition: border-color 0.3s;
	
	&:focus {
		border-color: #8B7355;
	}
}

.textarea {
	width: 100%;
	min-height: 150rpx;
	padding: 20rpx;
	border: 2rpx solid rgba(139, 115, 85, 0.2);
	border-radius: 12rpx;
	font-size: 28rpx;
	line-height: 1.6;
	box-sizing: border-box;
	background: white;
	transition: border-color 0.3s;
	
	&:focus {
		border-color: #8B7355;
	}
}

.char-count {
	text-align: right;
	font-size: 24rpx;
	color: #999;
	margin-top: 16rpx;
}

.generate-btn {
	width: 520upx;
	max-width: 580upx;
	padding: 28upx;
	background: linear-gradient(135deg, #8B7355 0%, #6D8B8B 100%);
	color: white;
	border: none;
	border-radius: 20upx;
	font-size: 30upx;
	font-weight: bold;
	margin: 12upx auto 0 auto; /* 居中 */
	display: block;
	box-shadow: 0 10upx 28upx rgba(139, 115, 85, 0.3);
	transition: all 0.3s;
	
	&:active {
		transform: translateY(-2upx);
		box-shadow: 0 14upx 36upx rgba(139, 115, 85, 0.4);
	}
}

.generate-btn[disabled] {
	opacity: 0.5;
	box-shadow: 0 8upx 20upx rgba(0, 0, 0, 0.1);
	transform: none !important;
}

.progress-section {
	width: 100%;
	max-width: 580upx;
	background: rgba(255, 255, 255, 0.96);
	border-radius: 16rpx;
	padding: 24rpx;
	box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.08);
	border: 2upx solid rgba(139, 115, 85, 0.12);
}

.progress-title {
	font-size: 32rpx;
	font-weight: bold;
	margin-bottom: 24rpx;
	color: #333;
}

.progress-item {
	display: flex;
	align-items: center;
	padding: 18rpx 0;
	border-bottom: 1rpx solid #f0f0f0;
}

.progress-item:last-child {
	border-bottom: none;
}

.progress-icon {
	font-size: 32rpx;
	margin-right: 16rpx;
}

.progress-text {
	font-size: 28rpx;
	color: #666;
}

.result-section {
	width: 100%;
	max-width: 580upx;
	background: rgba(255, 255, 255, 0.96);
	border-radius: 16rpx;
	padding: 24rpx;
	margin-top: 24rpx;
	box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.1);
	border: 2upx solid rgba(139, 115, 85, 0.12);
}

.result-title {
	font-size: 36rpx;
	font-weight: bold;
	color: #333;
	text-align: center;
	margin-bottom: 24rpx;
}

.result-video {
	width: 100%;
	height: 400rpx;
	border-radius: 16rpx;
	margin-bottom: 16rpx;
	background: #000;
}

.play-controls {
	display: flex;
	justify-content: center;
	margin-bottom: 24rpx;
}

.play-btn {
	padding: 20rpx 72rpx;
	background: linear-gradient(135deg, #8B7355 0%, #6D8B8B 100%);
	color: white;
	border: none;
	border-radius: 48rpx;
	font-size: 30rpx;
	font-weight: bold;
	box-shadow: 0 6rpx 16rpx rgba(139, 115, 85, 0.3);
	display: flex;
	align-items: center;
	justify-content: center;
	transition: all 0.3s;
}

.play-btn:active {
	transform: scale(0.96);
	box-shadow: 0 3rpx 10rpx rgba(139, 115, 85, 0.25);
}

.result-info {
	background: #f5f5f5;
	padding: 20rpx;
	border-radius: 12rpx;
	margin-bottom: 24rpx;
}

.info-label {
	font-size: 24rpx;
	color: #999;
	display: block;
	margin-bottom: 8rpx;
}

.info-value {
	font-size: 22rpx;
	color: #666;
	word-break: break-all;
}

.video-call-btn {
	width: 100%;
	padding: 24rpx;
	background: linear-gradient(135deg, #8B7355 0%, #6D8B8B 100%);
	color: white;
	border: none;
	border-radius: 16rpx;
	font-size: 28rpx;
	font-weight: bold;
	margin-bottom: 12rpx;
	box-shadow: 0 4rpx 12rpx rgba(139, 115, 85, 0.3);
	transition: all 0.3s;
	
	&:active {
		transform: translateY(-1rpx);
		box-shadow: 0 6rpx 16rpx rgba(139, 115, 85, 0.4);
	}
}

.download-btn {
	width: 100%;
	padding: 24rpx;
	background: linear-gradient(135deg, #6D8B8B 0%, #5A7A7A 100%);
	color: white;
	border: none;
	border-radius: 16rpx;
	font-size: 28rpx;
	margin-bottom: 12rpx;
	box-shadow: 0 4rpx 12rpx rgba(109, 139, 139, 0.3);
	transition: all 0.3s;
	
	&:active {
		transform: translateY(-1rpx);
		box-shadow: 0 6rpx 16rpx rgba(109, 139, 139, 0.4);
	}
}

.save-history-btn {
	width: 100%;
	padding: 24rpx;
	background: linear-gradient(135deg, #D4B996 0%, #C4A986 100%);
	color: white;
	border: none;
	border-radius: 16rpx;
	font-size: 28rpx;
	box-shadow: 0 4rpx 12rpx rgba(212, 185, 150, 0.3);
	transition: all 0.3s;
	
	&:active {
		transform: translateY(-1rpx);
		box-shadow: 0 6rpx 16rpx rgba(212, 185, 150, 0.4);
	}
}

/* 帮助弹窗样式 */
.help-modal {
	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: 40upx;
}

.help-content {
	width: 100%;
	max-width: 640upx;
	max-height: 85vh;
	background: white;
	border-radius: 24upx;
	overflow: hidden;
	display: flex;
	flex-direction: column;
	box-shadow: 0 8upx 32upx rgba(0, 0, 0, 0.3);
}

.help-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 32upx;
	border-bottom: 2upx solid #f0f0f0;
}

.help-title {
	font-size: 32upx;
	font-weight: bold;
	color: #333;
}

.close-btn {
	font-size: 40upx;
	color: #999;
	padding: 10upx;
	line-height: 1;
}

.help-body {
	flex: 1;
	padding: 24upx 32upx;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
}

.help-step {
	margin-bottom: 40upx;
}

.help-step:last-child {
	margin-bottom: 0;
}

.help-step-title {
	display: block;
	font-size: 28upx;
	font-weight: 600;
	color: #333;
	margin-bottom: 16upx;
}

.help-step-desc {
	display: block;
	font-size: 24upx;
	color: #666;
	line-height: 1.6;
	padding-left: 32upx;
}

.help-image {
	width: calc(100% + 48upx); /* 再左移一些 */
	margin-left: -24upx;
	height: auto;
	border-radius: 12upx;
	margin-top: 16upx;
	box-shadow: 0 4upx 12upx rgba(0, 0, 0, 0.1);
	display: block;
}

.help-footer {
	display: flex;
	gap: 20upx;
	padding: 24upx 32upx;
	border-top: 2upx solid #f0f0f0;
}

.help-btn {
	flex: 1;
	padding: 24upx;
	border-radius: 12upx;
	font-size: 28upx;
	border: none;
	transition: all 0.3s;
}

.help-btn.secondary {
	background: #f5f5f5;
	color: #666;
}

.help-btn.primary {
	background: linear-gradient(135deg, #8B7355 0%, #6D8B8B 100%);
	color: white;
}

.help-btn:active {
	transform: scale(0.98);
}
</style>
