<template>
  <div class="text-to-video-create-page">
    <CreationHeader />

    <!-- 主内容区域 -->
    <div class="main-content">
      <!-- 左侧设置面板 -->
      <div class="left-panel">
        <CreationTabs active="text-to-video" />

        <!-- 文本输入区域 -->
        <div class="text-input-section">
          <textarea
            v-model="inputText"
            :placeholder="t('video.textToVideo.textInputPlaceholder')"
            class="text-input"
            rows="8"
          ></textarea>
          <div class="optimize-btn">
            <button class="optimize-button" @click="optimizePromptHandler" :disabled="!inputText.trim() || optimizingPrompt">
              ✨ {{ optimizingPrompt ? t('video.textToVideo.optimizing') : t('video.textToVideo.oneClickOptimize') }}
            </button>
          </div>
        </div>

        <!-- 视频设置 -->
        <div class="video-settings">
          <div class="setting-item">
            <label>{{ t('video.textToVideo.aspectRatio') }}</label>
            <select v-model="aspectRatio" class="setting-select">
              <option value="16:9">16:9</option>
              <option value="9:16">9:16</option>
            </select>
          </div>

          <div class="setting-item">
            <label>{{ t('video.duration') }}</label>
            <select v-model="duration" class="setting-select">
              <option value="10">10s</option>
              <option value="15">15s</option>
            </select>
          </div>

          <!-- 高清模式暂时屏蔽
          <div class="setting-item">
            <label>{{ t('video.textToVideo.hdMode') }}</label>
            <div class="hd-setting">
              <a-switch v-model:checked="hdMode" />
              <span class="cost-text">{{ t('video.textToVideo.hdModeCost') }}</span>
            </div>
          </div>
          -->
        </div>

        <!-- 生成按钮 -->
        <CreationGenerateButton
          :button-text="isCreatingTask ? t('video.textToVideo.creatingTask') : t('video.textToVideo.startGenerate')"
          :points="30"
          :disabled="isCreatingTask"
          :is-authenticated="isAuthenticated"
          :login-prompt-text="t('video.textToVideo.pleaseLogin')"
          :login-text="t('video.textToVideo.loginRequired')"
          :login-button-text="t('video.textToVideo.loginNow')"
          @click="startGenerate"
          @login="goToLogin"
        />
      </div>

      <!-- 右侧预览区域 -->
      <div class="right-panel">
        <div class="preview-area">
          <!-- 任务状态显示 -->
          <div class="task-status" v-if="currentTask">
            <div class="status-header">
              <h3>{{ getStatusText(taskStatus) }}</h3>
              <div class="task-id">{{ t('home.textToVideo') }} {{ formatDate(currentTask.createdAt) }}</div>
            </div>

            <!-- 任务描述 -->
            <div class="task-description">
              {{ inputText }}
            </div>

            <!-- 视频预览区域 -->
            <div class="video-preview-container">
              <!-- 生成中的状态 -->
              <div v-if="inProgress" class="generating-container">
                <div class="generating-placeholder">
                  <div class="generating-text">{{ taskStatus === 'PENDING' ? t('video.textToVideo.queuing') : t('video.generating') }}</div>
                  <!-- 排队中：不确定进度条 -->
                  <div v-if="taskStatus === 'PENDING'" class="progress-bar-large indeterminate">
                    <div class="progress-fill-indeterminate"></div>
                  </div>
                  <!-- 生成中：动态进度条（进度为0时也显示动画） -->
                  <div v-else class="progress-bar-large" :class="{ 'indeterminate': taskProgress === 0 }">
                    <div v-if="taskProgress === 0" class="progress-fill-indeterminate"></div>
                    <div v-else class="progress-fill-large animated" :style="{ width: taskProgress + '%' }"></div>
                  </div>
                  <div class="progress-percentage" v-if="taskStatus !== 'PENDING'">{{ taskProgress }}%</div>
                </div>
              </div>
              
              <!-- 完成状态 -->
              <div v-else-if="taskStatus === 'COMPLETED'" class="completed-container">
                <!-- 任务信息头部 -->
                <div class="task-info-header">
                  <div class="task-checkbox">
                    <input type="checkbox" id="inProgress" v-model="showInProgress">
                    <label for="inProgress">{{ t('video.textToVideo.inProgress') }}</label>
                  </div>
                </div>

                <!-- 视频播放区域 -->
                <div class="video-player-container">
                  <div class="video-player">
                    <video
                      v-if="currentTask.resultUrl"
                      :src="currentTask.resultUrl"
                      controls
                      class="result-video"
                      poster=""
                    ></video>
                    <div v-else class="no-video-placeholder">
                      <div class="no-video-text">{{ t('video.textToVideo.noVideoUrl') }}</div>
                    </div>
                  </div>
                </div>

                <!-- 操作按钮区域 -->
                <div class="result-actions">
                  <button class="action-btn primary" @click="createSimilar">{{ t('video.textToVideo.createSimilar') }}</button>
                  <div class="action-icons">
                    <button class="icon-btn" @click="downloadVideo" :title="t('video.textToVideo.downloadVideo')">
                      <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                        <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
                      </svg>
                    </button>
                    <button class="icon-btn" @click="deleteWork" :title="t('video.textToVideo.deleteWork')">
                      <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                        <path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/>
                      </svg>
                    </button>
                  </div>
                </div>
              </div>
              
              <!-- 失败状态 -->
              <div v-else-if="taskStatus === 'FAILED'" class="failed-container">
                <div class="failed-placeholder">
                  <div class="failed-icon">❌</div>
                  <div class="failed-text">{{ t('video.textToVideo.generationFailed') }}</div>
                  <div class="failed-desc">{{ t('video.textToVideo.checkInputOrRetry') }}</div>
                  <div class="failed-reason" v-if="currentTask && currentTask.errorMessage">
                    <span class="reason-label">{{ t('video.failReason') }}：</span>
                    <span class="reason-text">{{ currentTask.errorMessage }}</span>
                  </div>
                </div>
                <div class="result-actions">
                  <button class="action-btn primary" @click="retryTask">{{ t('video.textToVideo.regenerate') }}</button>
                </div>
              </div>

              <!-- 其他状态 -->
              <div v-else class="status-placeholder">
                <div class="status-text">{{ getStatusText(taskStatus) }}</div>
              </div>
            </div>

          </div>

          <!-- 初始状态 -->
          <div class="preview-content" v-else>
            <div class="preview-placeholder">
              <div class="placeholder-text">{{ t('video.textToVideo.startCreating') }}</div>
            </div>
          </div>

          <!-- 历史记录区域 -->
          <div class="history-section" v-if="historyTasks.length > 0">
            <div class="history-list">
              <div
                v-for="task in historyTasks"
                :key="task.taskId"
                class="history-item"
              >
                <!-- 顶部状态复选框 -->
                <div class="history-status-checkbox" v-if="task.status === 'PENDING' || task.status === 'PROCESSING'">
                  <input type="checkbox" :checked="true" disabled>
                  <label>{{ t('video.textToVideo.inProgress') }}</label>
                </div>

                <!-- 头部信息 -->
                <div class="history-item-header">
                  <span class="history-type">{{ t('home.textToVideo') }}</span>
                  <span class="history-date">{{ formatDate(task.createdAt) }}</span>
                </div>

                <!-- 描述文字 -->
                <div class="history-prompt">{{ task.prompt || t('video.textToVideo.noDescription') }}</div>

                <!-- 预览区域 -->
                <div class="history-preview" :class="{ 'vertical': task.aspectRatio === '9:16' }">
                  <div v-if="task.status === 'PENDING' || task.status === 'PROCESSING'" class="history-placeholder">
                    <div class="queue-text">{{ task.status === 'PENDING' ? t('video.textToVideo.queuing') : t('video.generating') }}</div>
                    <!-- 排队中：不确定进度条 -->
                    <div v-if="task.status === 'PENDING'" class="history-progress-bar indeterminate">
                      <div class="progress-fill-indeterminate"></div>
                    </div>
                    <!-- 生成中：动态进度条（进度为0时也显示动画） -->
                    <div v-else class="history-progress-bar" :class="{ 'indeterminate': (task.progress || 0) === 0 }">
                      <div v-if="(task.progress || 0) === 0" class="progress-fill-indeterminate"></div>
                      <div v-else class="progress-fill-large animated" :style="{ width: (task.progress || 0) + '%' }"></div>
                    </div>
                  </div>
                  <div v-else-if="task.status === 'COMPLETED' && task.resultUrl" class="history-video-thumbnail">
                    <video
                      :ref="el => setVideoRef(task.taskId, el)"
                      :src="processHistoryUrl(task.resultUrl)"
                      controls
                      preload="metadata"
                      @loadedmetadata="handleVideoLoaded"
                      @error="handleVideoError"
                    ></video>
                  </div>
                  <div v-else class="history-placeholder">
                    <div class="no-result-text">{{ t('video.textToVideo.noResult') }}</div>
                  </div>
                </div>

                <!-- 操作按钮 -->
                <div class="history-actions">
                  <button class="similar-btn" @click="createSimilarFromHistory(task)">{{ t('video.textToVideo.createSimilar') }}</button>
                  <button 
                    v-if="task.status === 'COMPLETED' && task.resultUrl" 
                    class="download-btn" 
                    @click="downloadHistoryVideo(task)"
                    :title="t('video.textToVideo.downloadVideo')"
                  >
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
                      <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
                    </svg>
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    
  </div>
</template>

<script setup>
import { ref, onUnmounted, computed, onMounted, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { textToVideoApi } from '@/api/textToVideo'
import { useUserStore } from '@/stores/user'
import { StarOutlined } from '@ant-design/icons-vue'
import CreationGenerateButton from '@/components/CreationGenerateButton.vue'
import { message, Modal } from 'ant-design-vue'
import { optimizePrompt } from '@/api/promptOptimizer'
import { getProcessingWorks, getMyWorksByType } from '@/api/userWorks'
import CreationHeader from '@/components/CreationHeader.vue'
import CreationTabs from '@/components/CreationTabs.vue'

const router = useRouter()
const route = useRoute()
const userStore = useUserStore()
const { t } = useI18n()

// 计算是否已登录
const isAuthenticated = computed(() => userStore.isAuthenticated)

// 响应式数据
const inputText = ref('')
const aspectRatio = ref('9:16')
const duration = ref(15)
const hdMode = ref(false)
const inProgress = ref(false)
const currentTask = ref(null)
const taskProgress = ref(0)
const taskStatus = ref('')
const stopPolling = ref(null)
const showInProgress = ref(false)
const optimizingPrompt = ref(false) // 优化提示词状态
const historyTasks = ref([]) // 历史记录
const playingVideos = ref({}) // 正在播放的视频
const videoRefs = ref({}) // 视频元素引用
const isCreatingTask = ref(false) // 标记是否正在创建任务，避免重复恢复
const hasRestoredTask = ref(false) // 标记是否已经恢复过任务

// 跳转到登录页面
const goToLogin = () => {
  router.push({
    path: '/login',
    query: { redirect: router.currentRoute.value.fullPath }
  })
}

const startGenerate = async () => {
  // 检查登录状态
  if (!userStore.isAuthenticated) {
    message.warning(t('video.textToVideo.pleaseLoginFirst'))
    goToLogin()
    return
  }

  // 注：允许多任务并发，后端会检查最大任务数限制（最多3个）

  // 验证表单
  if (!inputText.value.trim()) {
    message.error(t('video.textToVideo.pleaseEnterText'))
    return
  }

  // 标记正在创建任务
  isCreatingTask.value = true

  // 显示加载状态
  const LoadingOutlined = ElLoading.service({
    lock: true,
    text: t('video.textToVideo.creatingTask'),
    background: 'rgba(0, 0, 0, 0.7)'
  })

  try {
    // 调用API创建任务
    const params = {
      prompt: inputText.value.trim(),
      aspectRatio: aspectRatio.value,
      duration: parseInt(duration.value),
      hdMode: hdMode.value
    }

    const response = await textToVideoApi.createTask(params)

    if (response.data && response.data.success) {
      currentTask.value = response.data.data
      inProgress.value = true
      taskProgress.value = 0
      taskStatus.value = 'PROCESSING'  // 直接显示生成中

      message.success(t('video.textToVideo.taskCreated'))

      // 更新用户积分信息（任务创建后积分已被扣除）
      // 使用 setTimeout 确保不阻塞任务流程
      setTimeout(async () => {
        try {
          await userStore.fetchCurrentUser()
          console.log('[Points Update]', t('video.textToVideo.pointsUpdated'))
        } catch (error) {
          console.error('[Points Update Error]', t('video.textToVideo.pointsUpdateFailed'), error)
          // 积分更新失败不影响任务流程
        }
      }, 0)

      // 开始轮询任务状态
      startPollingTask()
      
      // 延迟重置创建标志，避免立即触发恢复逻辑
      setTimeout(() => {
        isCreatingTask.value = false
      }, 2000)
    } else {
      // 任务创建失败，重置所有状态
      const errorMsg = response.data?.message || t('video.textToVideo.createTaskFailed')
      
      // 检测积分不足
      if (errorMsg.includes('积分不足') || errorMsg.includes('insufficient') || errorMsg.includes('points')) {
        Modal.confirm({
          title: t('common.insufficientPoints'),
          content: t('common.insufficientPointsMessage'),
          okText: t('common.goRecharge'),
          cancelText: t('common.cancel'),
          onOk() { router.push('/subscription') }
        })
      } else {
        message.error(errorMsg)
      }
      
      inProgress.value = false
      isCreatingTask.value = false
      currentTask.value = null
      taskStatus.value = ''
      taskProgress.value = 0
    }

  } catch (error) {
    console.error('[Create Task Error]', t('video.textToVideo.createTaskFailed'), error)
    const errorMsg = error.response?.data?.message || error.message || ''
    
    // 检测积分不足
    if (errorMsg.includes('积分不足') || errorMsg.includes('insufficient') || errorMsg.includes('points')) {
      Modal.confirm({
        title: t('common.insufficientPoints'),
        content: t('common.insufficientPointsMessage'),
        okText: t('common.goRecharge'),
        cancelText: t('common.cancel'),
        onOk() { router.push('/subscription') }
      })
    } else {
      message.error(t('video.textToVideo.createTaskFailed'))
    }
    
    // 异常情况，重置所有状态
    inProgress.value = false
    isCreatingTask.value = false
    currentTask.value = null
    taskStatus.value = ''
    taskProgress.value = 0
  } finally {
    loading.close()
  }
}

// 开始轮询任务状态
const startPollingTask = () => {
  if (!currentTask.value) return
  
  stopPolling.value = textToVideoApi.pollTaskStatus(
    currentTask.value.taskId,
    // 进度回调
    (progressData) => {
      if (progressData && typeof progressData.progress === 'number') {
        taskProgress.value = progressData.progress
      }
      if (progressData && progressData.status) {
        taskStatus.value = progressData.status
      }
      // 更新resultUrl（如果存在且不为空）
      if (progressData && progressData.resultUrl && progressData.resultUrl.trim() && currentTask.value) {
        currentTask.value.resultUrl = progressData.resultUrl
        console.log('[Result URL Updated]', progressData.resultUrl.substring(0, 50) + '...')
      }
      console.log('[Task Progress]', progressData)
    },
    // 完成回调
    async (taskData) => {
      inProgress.value = false
      taskProgress.value = 100
      taskStatus.value = 'COMPLETED'
      // 更新currentTask的resultUrl
      if (taskData && taskData.resultUrl && taskData.resultUrl.trim() && currentTask.value) {
        currentTask.value.resultUrl = taskData.resultUrl
        console.log('[Task Completed] Result URL:', taskData.resultUrl.substring(0, 50) + '...')
      } else if (currentTask.value && !currentTask.value.resultUrl) {
        console.warn('[Task Completed] No result URL found')
      }
      message.success(t('video.textToVideo.videoCompleted'))

      // 更新用户积分信息（使用 setTimeout 确保不阻塞）
      setTimeout(async () => {
        try {
          await userStore.fetchCurrentUser()
          console.log('[Points Update]', t('video.textToVideo.pointsUpdated'))
        } catch (error) {
          console.error('[Points Update Error]', t('video.textToVideo.pointsUpdateFailed'), error)
          // 积分更新失败不影响任务流程
        }
      }, 0)

      // 可以在这里跳转到结果页面或显示结果
      console.log('[Task Completed]', taskData)
    },
    // 错误回调 - 静默处理，只在页面上显示失败状态，不弹窗
    (error) => {
      inProgress.value = false
      taskStatus.value = 'FAILED'
      console.error('[Task Failed]', error)
    }
  )
}


// 获取状态文本
const getStatusText = (status) => {
  const statusMap = {
    'PENDING': t('video.textToVideo.statusPending'),
    'PROCESSING': t('video.textToVideo.statusProcessing'),
    'COMPLETED': t('video.completed'),
    'FAILED': t('video.failed'),
    'CANCELLED': t('video.textToVideo.statusCancelled')
  }
  return statusMap[status] || t('video.textToVideo.statusUnknown')
}

// 获取状态样式类
const getStatusClass = (status) => {
  const classMap = {
    'PENDING': 'status-pending',
    'PROCESSING': 'status-processing',
    'COMPLETED': 'status-completed',
    'FAILED': 'status-failed',
    'CANCELLED': 'status-cancelled'
  }
  return classMap[status] || ''
}

// 格式化日期
const formatDate = (dateString) => {
  if (!dateString) return ''
  const date = new Date(dateString)
  const year = date.getFullYear()
  const month = String(date.getMonth() + 1).padStart(2, '0')
  const day = String(date.getDate()).padStart(2, '0')
  const hours = String(date.getHours()).padStart(2, '0')
  const minutes = String(date.getMinutes()).padStart(2, '0')
  return t('video.textToVideo.dateFormat', { year, month, day, hours, minutes })
}

// 优化提示词
const optimizePromptHandler = async () => {
  if (!inputText.value.trim()) {
    message.warning(t('video.textToVideo.pleaseEnterPrompt'))
    return
  }

  // 长度检查
  if (inputText.value.length > 2000) {
    message.warning(t('video.textToVideo.promptTooLong'))
    return
  }

  try {
    optimizingPrompt.value = true
    const LoadingOutlined = ElLoading.service({
      lock: false,
      text: t('video.textToVideo.optimizingPrompt'),
      background: 'rgba(0, 0, 0, 0.3)'
    })

    const response = await optimizePrompt(inputText.value.trim(), 'text-to-video')

    loading.close()

    if (response.data && response.data.success) {
      const data = response.data.data
      const optimized = data.optimizedPrompt

      // 检查是否真正优化了
      if (data.optimized && optimized !== inputText.value.trim()) {
        inputText.value = optimized
        message.success(t('video.textToVideo.optimizeSuccess'))
      } else {
        message.warning(t('video.textToVideo.optimizeNoChange'))
      }
    } else {
      message.error(response.data?.message || t('video.textToVideo.optimizeFailed'))
    }
  } catch (error) {
    console.error('[Optimize Prompt Error]', error)

    let errorMessage = t('video.textToVideo.optimizeFailed')
    if (error.response) {
      const status = error.response.status
      if (status === 400) {
        errorMessage = error.response.data?.message || t('video.textToVideo.requestParamError')
      } else if (status === 408 || error.code === 'ECONNABORTED') {
        errorMessage = t('video.textToVideo.requestTimeout')
      } else if (status >= 500) {
        errorMessage = t('video.textToVideo.serverError')
      } else {
        errorMessage = error.response.data?.message || t('video.textToVideo.optimizeFailed')
      }
    } else if (error.request) {
      errorMessage = t('video.textToVideo.networkError')
    } else if (error.code === 'ERR_NETWORK') {
      errorMessage = t('video.textToVideo.networkConnectionError')
    } else {
      errorMessage = error.message || t('video.textToVideo.optimizeFailed')
    }

    message.error(errorMessage)
  } finally {
    optimizingPrompt.value = false
  }
}

// 创建同款
const createSimilar = () => {
  // 保持当前设置，重新生成
  startGenerate()
}

// 下载视频
const downloadVideo = async () => {
  if (!currentTask.value || !currentTask.value.resultUrl) {
    message.error(t('video.textToVideo.videoUrlNotAvailable'))
    return
  }
  
  try {
    message.info(t('common.preparingDownload'))
    const { downloadVideo: downloadVideoUtil } = await import('@/utils/download')
    const success = await downloadVideoUtil(currentTask.value.resultUrl, currentTask.value.taskId || Date.now())
    if (success) {
      message.success(t('video.textToVideo.downloadStarted'))
    }
  } catch (error) {
    console.error('下载失败:', error)
    window.open(currentTask.value.resultUrl, '_blank')
  }
}

// 下载历史记录中的视频
const downloadHistoryVideo = async (task) => {
  if (!task || !task.resultUrl) {
    message.error(t('video.textToVideo.videoUrlNotAvailable'))
    return
  }
  
  try {
    message.info(t('common.preparingDownload'))
    const videoUrl = processHistoryUrl(task.resultUrl)
    const { downloadVideo: downloadVideoUtil } = await import('@/utils/download')
    const success = await downloadVideoUtil(videoUrl, task.taskId || Date.now())
    if (success) {
      message.success(t('video.textToVideo.downloadStarted'))
    }
  } catch (error) {
    console.error('下载失败:', error)
    window.open(processHistoryUrl(task.resultUrl), '_blank')
  }
}

// 重新生成（复用原task_id）
const retryTask = async () => {
  // 检查是否有失败的任务可以重试
  if (!currentTask.value || !currentTask.value.taskId) {
    message.error(t('common.noRetryableTask'))
    return
  }
  
  const taskId = currentTask.value.taskId
  console.log('[Retry] 开始重试任务:', taskId)
  
  // 显示加载状态
  const LoadingOutlined = ElLoading.service({
    lock: true,
    text: t('common.regenerating'),
    background: 'rgba(0, 0, 0, 0.7)'
  })
  
  try {
    // 调用后端重试API，复用原task_id
    const response = await textToVideoApi.retryTask(taskId)
    
    if (response.data && response.data.success) {
      // 更新任务状态
      currentTask.value = response.data.data
      inProgress.value = true
      taskProgress.value = 0
      taskStatus.value = 'PROCESSING'  // 直接显示生成中
      
      message.success(t('common.retrySubmitted'))
      
      // 开始轮询任务状态
      startPollingTask()
    } else {
      const errorMsg = response.data?.message || t('common.updateFailed')
      message.error(errorMsg)
    }
  } catch (error) {
    console.error('[Retry] 重试任务失败:', error)
    message.error(error.response?.data?.message || error.message || t('common.updateFailed'))
  } finally {
    loading.close()
  }
}

// 投稿功能（已移除按钮，保留函数已删除）

// 删除作品
const deleteWork = () => {
  if (!currentTask.value) {
    message.error(t('video.textToVideo.noWorkToDelete'))
    return
  }

  // 确认删除
  message.confirm(t('video.textToVideo.deleteConfirm'), t('video.textToVideo.confirmDelete'), {
    confirmButtonText: t('common.confirm'),
    cancelButtonText: t('common.cancel'),
    type: 'warning'
  }).then(() => {
    // 这里可以调用删除API
    currentTask.value = null
    taskStatus.value = ''
    message.success(t('video.textToVideo.workDeleted'))
  }).catch(() => {
    message.info(t('video.textToVideo.deleteCancelled'))
  })
}

// 处理历史记录URL
const processHistoryUrl = (url) => {
  if (!url) return ''
  // data: 协议（Base64 图片等）直接返回，避免被当成相对路径错误加前缀
  if (url.startsWith('data:')) {
    return url
  }
  // 如果是相对路径，确保格式正确
  if (url.startsWith('/') || !url.startsWith('http')) {
    if (!url.startsWith('/uploads/') && !url.startsWith('/api/')) {
      return url.startsWith('/') ? url : `/${url}`
    }
  }
  return url
}

// 加载历史记录（从user_works表获取TEXT_TO_VIDEO类型的作品）
const loadHistory = async () => {
  // 只有登录用户才能查看历史记录
  if (!userStore.isAuthenticated) {
    historyTasks.value = []
    return
  }

  try {
    // 从user_works表获取TEXT_TO_VIDEO类型的作品
    const response = await getMyWorksByType('TEXT_TO_VIDEO', { page: 0, size: 1000 })
    if (response.data && response.data.success) {
      // 转换数据格式，适配历史记录展示
      const works = response.data.data || []

      historyTasks.value = works.map(work => ({
        taskId: work.taskId,
        prompt: work.prompt,
        aspectRatio: work.aspectRatio,
        duration: work.duration ? parseInt(work.duration) : 5,
        hdMode: work.quality === 'HD',
        status: work.status,
        resultUrl: work.resultUrl ? processHistoryUrl(work.resultUrl) : null,
        createdAt: work.createdAt,
        progress: work.status === 'PROCESSING' ? 50 : (work.status === 'COMPLETED' ? 100 : 0)
      }))

      console.log('[History Load]', t('video.textToVideo.historyLoadSuccess', { count: historyTasks.value.length }))
    }
  } catch (error) {
    console.error('[History Load Error]', t('video.textToVideo.historyLoadFailed'), error)
    historyTasks.value = []
  }
}

// 从历史记录创建同款
const createSimilarFromHistory = (task) => {
  if (task.prompt) {
    inputText.value = task.prompt
  }
  if (task.aspectRatio) {
    aspectRatio.value = task.aspectRatio
  }
  if (task.duration) {
    duration.value = task.duration
  }
  if (task.hdMode !== undefined) {
    hdMode.value = task.hdMode
  }
  // 滚动到顶部
  window.scrollTo({ top: 0, behavior: 'smooth' })
  message.success(t('video.textToVideo.historyParamsFilled'))
}

// 取消任务
const cancelTask = async (taskId) => {
  try {
    message.info(t('video.textToVideo.cancelFunctionTBD'))
    // TODO: 实现取消任务API
  } catch (error) {
    console.error('[Cancel Task Error]', error)
  }
}

// 处理视频加载
const handleVideoLoaded = (event) => {
  // 视频元数据加载完成，可以显示缩略图
  const video = event.target
  video.currentTime = 1 // 跳转到第1秒作为缩略图
  // 监听播放状态
  video.addEventListener('play', () => {
    const taskId = Object.keys(videoRefs.value).find(id => videoRefs.value[id] === video)
    if (taskId) {
      playingVideos.value[taskId] = true
    }
  })
  video.addEventListener('pause', () => {
    const taskId = Object.keys(videoRefs.value).find(id => videoRefs.value[id] === video)
    if (taskId) {
      playingVideos.value[taskId] = false
    }
  })
  video.addEventListener('ended', () => {
    const taskId = Object.keys(videoRefs.value).find(id => videoRefs.value[id] === video)
    if (taskId) {
      playingVideos.value[taskId] = false
      video.currentTime = 1 // 播放结束后回到第1秒作为缩略图
    }
  })
}

// 处理视频加载错误
const handleVideoError = (event) => {
  console.error('[History Video Load Error]', event.target.src)
  // 可以在这里添加错误处理逻辑，比如显示占位图
}

// 设置视频引用
const setVideoRef = (taskId, el) => {
  if (el) {
    videoRefs.value[taskId] = el
  }
}

// 切换历史记录视频播放
const toggleHistoryVideo = (task) => {
  const video = videoRefs.value[task.taskId]
  if (!video) return
  
  if (playingVideos.value[task.taskId]) {
    video.pause()
    playingVideos.value[task.taskId] = false
  } else {
    // 解除静音并设置音量，确保有声音
    try {
      video.muted = false
      video.volume = 1
      // 一些浏览器需要用户交互后才能带声音播放，这里已由点击触发
      video.play()
    } catch (_) {
      // 忽略播放失败（策略限制），用户可再次点击
      video.play()
    }
    playingVideos.value[task.taskId] = true
  }
}

// 组件挂载时加载历史记录
// 监听登录状态变化，登录后自动加载历史记录
watch(() => userStore.isAuthenticated, (isAuth) => {
  if (isAuth) {
    loadHistory()
    // 延迟恢复任务，避免与创建任务冲突
    setTimeout(() => {
      if (!isCreatingTask.value) {
        restoreProcessingTask()
      }
    }, 500)
  } else {
    historyTasks.value = []
    hasRestoredTask.value = false
  }
})

// 恢复正在进行中的任务
const restoreProcessingTask = async () => {
  if (!userStore.isAuthenticated) {
    return false
  }
  
  // 如果正在创建任务，跳过恢复逻辑
  if (isCreatingTask.value) {
    console.log('[Task Restore] 跳过恢复：正在创建新任务')
    return false
  }
  
  // 如果已经恢复过任务且当前有任务在进行中，跳过
  if (hasRestoredTask.value && currentTask.value) {
    console.log('[Task Restore] 跳过恢复：已有任务在进行中')
    return true
  }

  try {
    const response = await getProcessingWorks()
    if (response.data && response.data.success && response.data.data) {
      const works = response.data.data
      // 只恢复文生视频类型的任务（包括 PROCESSING 和 PENDING 状态）
      const textToVideoWorks = works.filter(work => work.workType === 'TEXT_TO_VIDEO')

      if (textToVideoWorks.length > 0) {
        // 取最新的一个任务
        const work = textToVideoWorks[0]

        // 只有真正在进行中的任务才恢复
        const workStatus = work.status || 'PROCESSING'
        if (workStatus === 'PROCESSING' || workStatus === 'PENDING') {
          // 恢复任务状态
          currentTask.value = {
            taskId: work.taskId,
            prompt: work.prompt,
            aspectRatio: work.aspectRatio,
            duration: work.duration,
            resultUrl: work.resultUrl,
            createdAt: work.createdAt
          }

          // 恢复输入参数
          if (work.prompt) {
            inputText.value = work.prompt
          }
          if (work.aspectRatio) {
            aspectRatio.value = work.aspectRatio
          }
          if (work.duration) {
            duration.value = parseInt(work.duration) || 10
          }

          inProgress.value = true
          taskStatus.value = workStatus
          taskProgress.value = 50 // 初始进度设为50%

          console.log('[Task Restored]', work.taskId, 'Status:', workStatus)
          // 静默恢复，不显示弹窗
          
          // 标记已恢复任务
          hasRestoredTask.value = true

          // 开始轮询任务状态
          startPollingTask()
          return true
        } else {
          // 如果任务已完成、失败或取消，不恢复任何参数
          console.log('[Task Skip]', work.taskId, 'Status:', workStatus, '- 不是进行中状态')
          return false
        }
      }
    }
  } catch (error) {
    console.error('[Task Restore Error]', error)
  }
  return false
}

// 检查最近一条任务的状态（如果失败则显示失败状态和提示词）
const checkLastTaskStatus = async () => {
  if (!userStore.isAuthenticated) return

  try {
    const response = await textToVideoApi.getTasks(0, 1)
    if (response.data && response.data.success && response.data.data && response.data.data.length > 0) {
      const lastTask = response.data.data[0]
      
      // 只关注 FAILED 状态，显示失败UI和提示词
      if (lastTask.status === 'FAILED') {
        console.log('[Last Task Failed]', lastTask)
        
        currentTask.value = lastTask
        taskStatus.value = 'FAILED'
        
        // 恢复提示词，让用户看到失败任务的内容
        if (lastTask.prompt) {
          inputText.value = lastTask.prompt
        }
        // 恢复其他参数
        if (lastTask.aspectRatio) {
          aspectRatio.value = lastTask.aspectRatio
        }
        if (lastTask.duration) {
          duration.value = lastTask.duration
        }
        if (lastTask.hdMode !== undefined) {
          hdMode.value = lastTask.hdMode
        }
      }
      // 如果最近一条任务是成功的，不需要处理
    }
  } catch (error) {
    console.error('CheckOutlined last task status error', error)
  }
}

// 处理全局点击事件，关闭用户菜单
const handleClickOutside = (event) => {
  if (!showUserMenu.value) return
  
  // 检查点击是否在用户头像或菜单内
  const menuElement = document.querySelector('.user-menu-teleport')
  const avatarElement = userAvatarRef.value
  
  if (menuElement && menuElement.contains(event.target)) {
    // 点击在菜单内，不关闭
    return
  }
  
  if (avatarElement && avatarElement.contains(event.target)) {
    // 点击在头像上，不关闭（由 toggleUserMenu 处理）
    return
  }
  
  // 点击在外部，关闭菜单
  showUserMenu.value = false
}

onMounted(async () => {
  // 只有已登录用户才刷新用户信息
  if (userStore.isAuthenticated) {
    await userStore.fetchCurrentUser()
  }
  
  // 处理"做同款"传递的路由参数
  if (route.query.prompt) {
    inputText.value = route.query.prompt
    if (route.query.aspectRatio) {
      aspectRatio.value = route.query.aspectRatio
    }
    if (route.query.duration) {
      duration.value = parseInt(route.query.duration) || 10
    }
    // 静默填充，不显示弹窗提示（减少干扰）
    // 清除URL中的query参数，避免刷新页面重复填充
    router.replace({ path: route.path })
  }
  
  loadHistory()
  // 延迟恢复任务，避免与创建任务冲突
  setTimeout(async () => {
    if (!isCreatingTask.value) {
      const restored = await restoreProcessingTask()
      // 如果没有恢复进行中的任务，则检查最近一条是否失败
      if (!restored) {
        checkLastTaskStatus()
      }
    }
  }, 500)
  
  // 添加全局点击监听器
  document.addEventListener('click', handleClickOutside)
})

// 组件卸载时清理资源
onUnmounted(() => {
  // 停止轮询
  if (stopPolling.value) {
    stopPolling.value()
    stopPolling.value = null
  }
  
  // 移除全局点击监听器
  document.removeEventListener('click', handleClickOutside)
})
</script>

<style scoped>
.text-to-video-create-page {
  height: 100vh;
  background: var(--bg-base);
  color: var(--text-primary);
  display: flex;
  flex-direction: column;
  font-family: var(--font-sans);
}

/* ==================== Main Content ==================== */
.main-content {
  flex: 1;
  display: grid;
  grid-template-columns: 480px 1fr;
  height: calc(100vh - var(--header-height));
  overflow: hidden;
}

/* ==================== Left Panel ==================== */
.left-panel {
  background: var(--bg-elevated);
  border-right: 1px solid var(--border-subtle);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  overflow-y: auto;
}

/* Text Input */
.text-input-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.text-input {
  width: 100%;
  min-height: 140px;
  padding: var(--space-4);
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  color: var(--text-primary);
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
  resize: vertical;
  outline: none;
  transition: var(--transition-all);
  font-family: var(--font-sans);
}

.text-input:focus {
  border-color: var(--primary-500);
  box-shadow: 0 0 0 3px var(--primary-glow-light);
}

.text-input::placeholder {
  color: var(--text-tertiary);
}

.optimize-btn {
  display: flex;
  justify-content: flex-end;
}

.optimize-button {
  background: var(--primary-500);
  color: var(--text-inverse);
  border: none;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  cursor: pointer;
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  transition: var(--transition-all);
}

.optimize-button:hover:not(:disabled) {
  background: var(--primary-400);
  box-shadow: var(--glow-primary);
  transform: translateY(-1px);
}

.optimize-button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Video Settings */
.video-settings {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.setting-item {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.setting-item label {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  font-weight: var(--font-medium);
}

.setting-select {
  padding: var(--space-3) var(--space-4);
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-size: var(--text-sm);
  outline: none;
  cursor: pointer;
  transition: var(--transition-all);
  font-family: var(--font-sans);
}

.setting-select:focus {
  border-color: var(--primary-500);
  box-shadow: 0 0 0 3px var(--primary-glow-light);
}

.setting-select:hover {
  border-color: var(--border-strong);
}

.cost-text {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}


/* ==================== Right Panel ==================== */
.right-panel {
  background: var(--bg-base);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
}

.preview-area {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  min-height: min-content;
}

.preview-content {
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-xl);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
  margin-bottom: var(--space-5);
  width: 80%;
  max-width: 1000px;
  min-height: 300px;
  transition: var(--transition-all);
}

.preview-content:hover {
  border-color: var(--border-strong);
}

.preview-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.placeholder-text {
  font-size: var(--text-lg);
  color: var(--text-tertiary);
  font-weight: var(--font-medium);
}

/* ==================== Responsive ==================== */
@media (max-width: 1200px) {
  .main-content {
    grid-template-columns: 400px 1fr;
  }
}

@media (max-width: 1024px) {
  .main-content {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr;
  }

  .left-panel {
    border-right: none;
    border-bottom: 1px solid var(--border-subtle);
  }
}

@media (max-width: 768px) {
  .top-header {
    padding: 0 var(--space-4);
  }

  .left-panel,
  .right-panel {
    padding: var(--space-4);
  }

  .creation-tabs {
    flex-direction: column;
  }
}

/* ==================== Task Status ==================== */
.task-status {
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-xl);
  padding: var(--space-6);
  margin-bottom: var(--space-5);
  width: 80%;
  max-width: 1000px;
  min-height: 300px;
}

.status-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--space-4);
}

.status-header h3 {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: var(--font-semibold);
  color: var(--text-primary);
}

.task-id {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  font-family: var(--font-mono);
}

.status-pending  { color: var(--warning-500); }
.status-processing { color: var(--primary-500); }
.status-completed { color: var(--success-500); }
.status-failed   { color: var(--error-500); }
.status-cancelled { color: var(--text-disabled); }

.task-description {
  color: var(--text-secondary);
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
  margin-bottom: var(--space-4);
  white-space: pre-wrap;
  word-wrap: break-word;
}

/* ==================== Video Preview ==================== */
.video-preview-container {
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-xl);
  min-height: 200px;
  max-height: 50vh;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: var(--space-4) 0;
  overflow: hidden;
  padding: var(--space-5);
  width: 80%;
  max-width: 1000px;
}

.generating-container {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.generating-placeholder {
  text-align: center;
  padding: var(--space-10);
}

.generating-text {
  font-size: var(--text-lg);
  color: var(--primary-400);
  font-weight: var(--font-semibold);
  margin-bottom: var(--space-5);
}

.progress-bar-large {
  width: 200px;
  height: 4px;
  background: var(--bg-hover);
  border-radius: var(--radius-full);
  overflow: hidden;
}

.progress-fill-large {
  height: 100%;
  background: var(--primary-500);
  border-radius: var(--radius-full);
  transition: width 0.3s ease;
}

.progress-fill-large.animated {
  background: linear-gradient(90deg, var(--primary-500), var(--accent-500), var(--primary-500));
  background-size: 200% 100%;
  animation: progress-sweep 1.5s ease-in-out infinite, gradient-flow 2s linear infinite;
}

.progress-bar-large.indeterminate {
  overflow: hidden;
}

.progress-fill-indeterminate {
  width: 30%;
  height: 100%;
  background: linear-gradient(90deg, transparent, var(--primary-500), var(--accent-500), var(--primary-500), transparent);
  border-radius: var(--radius-full);
  animation: progress-sweep 1.5s ease-in-out infinite;
}

.progress-percentage {
  font-size: var(--text-sm);
  color: var(--primary-400);
  font-weight: var(--font-semibold);
  margin-top: var(--space-3);
}

/* History Progress */
.history-progress-bar {
  width: 80%;
  max-width: 200px;
  height: 4px;
  background: var(--bg-hover);
  border-radius: var(--radius-full);
  overflow: hidden;
  margin: var(--space-3) 0;
}

.history-progress-bar.indeterminate .progress-fill-indeterminate {
  width: 30%;
  height: 100%;
  background: linear-gradient(90deg, transparent, var(--primary-500), var(--accent-500), var(--primary-500), transparent);
  border-radius: var(--radius-full);
  animation: progress-sweep 1.5s ease-in-out infinite;
}

.history-progress-bar .progress-fill-large {
  height: 100%;
  background: linear-gradient(90deg, var(--primary-500), var(--accent-500), var(--primary-500));
  background-size: 200% 100%;
  border-radius: var(--radius-full);
  animation: progress-sweep 1.5s ease-in-out infinite, gradient-flow 2s linear infinite;
}

/* ==================== Completed State ==================== */
.completed-container {
  width: 100%;
  max-height: 70vh;
  display: flex;
  flex-direction: column;
  padding: var(--space-5);
  overflow-y: auto;
}

.task-info-header {
  margin-bottom: var(--space-4);
}

.task-checkbox {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.task-checkbox input[type="checkbox"] {
  accent-color: var(--primary-500);
}

.task-checkbox label {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  cursor: pointer;
}

.video-player-container {
  flex: 1;
  margin-bottom: var(--space-5);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
  max-height: calc(70vh - 100px);
}

.video-player {
  width: 80%;
  max-width: 1000px;
  aspect-ratio: 16/9;
  background: #000;
  border-radius: var(--radius-lg);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.result-video {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.no-video-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-tertiary);
}

.no-video-text {
  font-size: var(--text-base);
}

/* ==================== Action Buttons ==================== */
.result-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.action-btn {
  padding: var(--space-2) var(--space-5);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  cursor: pointer;
  transition: var(--transition-all);
  border: none;
}

.action-btn.primary {
  background: var(--primary-500);
  color: var(--text-inverse);
}

.action-btn.primary:hover {
  background: var(--primary-400);
  box-shadow: var(--glow-primary);
  transform: translateY(-1px);
}

.action-icons {
  display: flex;
  gap: var(--space-2);
}

.icon-btn {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-md);
  background: var(--bg-hover);
  border: 1px solid var(--border-default);
  color: var(--text-secondary);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: var(--transition-all);
}

.icon-btn:hover {
  background: var(--bg-active);
  color: var(--text-primary);
  transform: translateY(-1px);
}

/* ==================== Failed State ==================== */
.failed-container {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--space-10);
}

.failed-placeholder {
  text-align: center;
  margin-bottom: var(--space-5);
}

.failed-icon {
  font-size: 48px;
  margin-bottom: var(--space-4);
}

.failed-text {
  font-size: var(--text-xl);
  color: var(--error-500);
  font-weight: var(--font-semibold);
  margin-bottom: var(--space-2);
}

.failed-desc {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
}

.failed-reason {
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--error-glow);
  border-radius: var(--radius-md);
  border: 1px solid rgba(239, 68, 68, 0.2);
  text-align: left;
  max-width: 400px;
}

.failed-reason .reason-label {
  color: var(--error-500);
  font-weight: var(--font-medium);
  font-size: var(--text-xs);
}

.failed-reason .reason-text {
  color: var(--error-400);
  font-size: var(--text-xs);
  word-break: break-word;
}

.status-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.status-text {
  font-size: var(--text-lg);
  color: var(--text-tertiary);
}

/* ==================== History ==================== */
.history-section {
  margin-top: var(--space-6);
  padding-top: var(--space-6);
  border-top: 1px solid var(--border-subtle);
}

.history-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.history-item {
  padding: 0;
}

.history-status-checkbox {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.history-status-checkbox input[type="checkbox"] {
  accent-color: var(--primary-500);
}

.history-status-checkbox label {
  color: var(--text-secondary);
  font-size: var(--text-sm);
}

.history-item-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--space-3);
}

.history-type {
  color: var(--primary-400);
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
}

.history-date {
  color: var(--text-tertiary);
  font-size: var(--text-xs);
}

.history-prompt {
  color: var(--text-secondary);
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
  margin-bottom: var(--space-4);
  white-space: pre-wrap;
  word-wrap: break-word;
  width: 80%;
  max-width: 1000px;
}

.history-preview {
  width: 80%;
  max-width: 1000px;
  aspect-ratio: 16/9;
  background: #000;
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-bottom: var(--space-4);
}

.history-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--bg-surface);
  gap: var(--space-3);
}

.queue-text {
  font-size: var(--text-lg);
  font-weight: var(--font-semibold);
  color: var(--text-primary);
}

.no-result-text {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
}

.history-video-thumbnail {
  width: 100%;
  height: 100%;
  cursor: pointer;
  overflow: hidden;
}

.history-video-thumbnail video {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

.history-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.similar-btn {
  padding: var(--space-2) var(--space-4);
  background: var(--bg-surface);
  color: var(--text-secondary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  font-weight: var(--font-medium);
  cursor: pointer;
  transition: var(--transition-all);
}

.similar-btn:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
  border-color: var(--border-strong);
}

.download-btn {
  width: 36px;
  height: 36px;
  background: var(--text-primary);
  color: var(--bg-base);
  border: none;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: var(--transition-all);
}

.download-btn:hover {
  opacity: 0.9;
  transform: translateY(-1px);
}
</style>

<!-- 非 scoped 样式：使用设计系统已定义的 progress-sweep 和 gradient-flow 动画 -->
