<template>
  <FullWidthLayout :show-padding="false">
    <div class="kling-page">
      <div class="kling-content">
        <!-- 左侧配置 -->
        <div class="upload-panel">
          <!-- 文案输入 -->
          <div class="section">
            <h3>文案</h3>
            <a-textarea
              v-model:value="ttsText"
              :placeholder="textareaPlaceholder"
              :rows="4"
              :maxlength="maxTextLength"
              :show-count="true"
              class="tts-textarea"
            />
          </div>

          <!-- 音色选择 -->
          <div class="section">
            <h3>音色</h3>
            <VoiceSelector
              :synth-text="ttsText"
              :speech-rate="speechRate"
              @select="handleVoiceSelect"
            />
          </div>

          <!-- TTS 控制 -->
          <div class="section">
            <h3>语音控制</h3>
            <div class="control-group">
              <div class="control-label">语速</div>
              <div class="slider-card">
                <div class="slider-info">
                  <div class="slider-value">{{ speechRateDisplay }}</div>
                  <button class="reset-btn" @click="speechRate = 1">重置</button>
                </div>
                <a-slider
                  v-model:value="speechRate"
                  :min="0.5"
                  :max="2"
                  :step="0.1"
                  :marks="speechRateMarks"
                  :tooltip-open="false"
                />
              </div>
            </div>
          </div>

          <!-- 视频选择 -->
          <div class="section">
            <h3>视频</h3>

            <!-- 双选项卡片 -->
            <div class="video-selection-cards">
              <!-- 上传选项 -->
              <div
                class="video-option-card"
                :class="{ selected: videoState.videoSource === 'upload' }"
                @click="handleSelectUpload"
              >
                <div class="card-icon">
                  <UploadOutlined />
                </div>
                <div class="card-content">
                  <h4>点击上传新视频</h4>
                  <p>支持 MP4、MOV 格式</p>
                </div>
              </div>

              <!-- 选择选项 -->
              <div
                class="video-option-card"
                :class="{ selected: videoState.videoSource === 'select' }"
                @click="handleSelectFromLibrary"
              >
                <div class="card-icon">
                  <PictureOutlined />
                </div>
                <div class="card-content">
                  <h4>从素材库选择</h4>
                  <p>选择已上传的视频</p>
                </div>
              </div>
            </div>

            <!-- 已选择视频的预览 -->
            <div v-if="videoState.selectedVideo" class="selected-video-preview">
              <div class="preview-thumbnail">
                <img
                  :src="getVideoPreviewUrl(videoState.selectedVideo)"
                  :alt="videoState.selectedVideo.fileName"
                />
                <div class="video-duration-badge">
                  {{ formatDuration(videoState.selectedVideo.duration) }}
                </div>
              </div>
              <div class="preview-info">
                <div class="video-title">{{ videoState.selectedVideo.fileName }}</div>
                <div class="video-meta">
                  <span>{{ formatFileSize(videoState.selectedVideo.fileSize) }}</span>
                  <span>{{ formatDuration(videoState.selectedVideo.duration) }}</span>
                </div>
              </div>
              <a-button type="link" size="small" @click="replaceVideo">
                更换
              </a-button>
            </div>

            <!-- 上传区域（仅在选择上传时显示） -->
            <div
              v-if="videoState.videoSource === 'upload'"
              class="upload-zone"
              @drop.prevent="handleDrop"
              @dragover.prevent="dragOver = true"
              @dragleave.prevent="dragOver = false"
            >
              <input ref="fileInput" type="file" accept=".mp4,.mov" style="display: none" @change="handleFileSelect" />

              <div v-if="!videoState.uploadedVideo" class="upload-placeholder">
                <h3>拖拽或点击上传视频文件</h3>
                <p>支持 MP4、MOV</p>
                <a-button type="primary" size="large" @click="triggerFileSelect">
                  选择文件
                </a-button>
              </div>

              <div v-else class="video-preview">
                <video :src="videoState.uploadedVideo" controls class="preview-video"></video>
                <p>{{ videoState.videoFile?.name }}</p>
                <a-button type="link" size="small" @click="replaceVideo">
                  更换
                </a-button>
              </div>
            </div>
          </div>

          <!-- 配音生成（仅在 Pipeline 到达 ready 状态后显示） -->
          <div v-if="isPipelineReady" class="section audio-section">
            <!-- 已生成音频 -->
            <div v-if="audioState.generated" class="audio-generated">
              <div class="audio-header">
                <span class="audio-title">配音</span>
                <span class="audio-duration">{{ audioDurationSec }}秒</span>
              </div>

              <!-- 音频播放器 -->
              <div v-if="audioUrl" class="audio-player-wrapper">
                <audio :src="audioUrl" controls class="audio-player" />
              </div>

              <!-- 校验失败提示 -->
              <div v-if="!validationPassed" class="validation-warning">
                <span class="warning-icon">⚠️</span>
                <span class="warning-text">音频时长({{ audioDurationSec }}秒)超过视频人脸区间({{ faceDurationSec }}秒)，请缩短文案或调整语速</span>
              </div>

              <!-- 重新生成 -->
              <a-button type="link" size="small" :loading="audioState.generating" @click="generateAudio">
                重新生成
              </a-button>
            </div>
          </div>

          <!-- Pipeline 进度条 -->
          <PipelineProgress
            v-if="isPipelineBusy || isPipelineReady || isPipelineFailed || isPipelineCompleted"
            :state="pipelineState"
            :progress="pipelineProgress"
            :is-busy="isPipelineBusy"
            :is-ready="isPipelineReady"
            :is-failed="isPipelineFailed"
            :is-completed="isPipelineCompleted"
            :error="pipelineError"
            @retry="retryPipeline"
            @reset="resetPipeline"
          />

          <!-- 按钮组 -->
          <div class="action-buttons">
            <!-- 准备阶段：先运行到 ready -->
            <a-button
              v-if="!isPipelineReady"
              type="primary"
              size="large"
              :disabled="!canGenerate"
              :loading="isPipelineBusy"
              block
              @click="generateAudio"
            >
              {{ isPipelineBusy ? '处理中...' : '生成配音并验证' }}
            </a-button>

            <!-- Ready 后：生成数字人视频 -->
            <a-button
              v-else
              type="primary"
              size="large"
              :loading="isPipelineBusy"
              block
              @click="generateDigitalHuman"
            >
              {{ isPipelineBusy ? '处理中...' : '生成数字人视频' }}
            </a-button>

          </div>
        </div>

        <!-- 右侧结果 -->
        <ResultPanel @videoLoaded="handleVideoLoaded" />
      </div>

      <!-- 视频选择器弹窗 -->
      <VideoSelector v-model:open="videoState.selectorVisible" @select="handleVideoSelect" />
    </div>
  </FullWidthLayout>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { UploadOutlined, PictureOutlined } from '@ant-design/icons-vue'
import { useVoiceCopyStore } from '@/stores/voiceCopy'
import VideoSelector from '@/components/VideoSelector.vue'
import VoiceSelector from '@/components/VoiceSelector.vue'
import ResultPanel from '@/components/ResultPanel.vue'
import FullWidthLayout from '@/layouts/components/FullWidthLayout.vue'
import PipelineProgress from '@/components/PipelineProgress.vue'

// Controller Hook
import { useIdentifyFaceController } from './hooks/useIdentifyFaceController'

const voiceStore = useVoiceCopyStore()
const dragOver = ref(false)

// ==================== 初始化 Controller ====================

// Controller 内部直接创建和管理两个子 Hook
const controller = useIdentifyFaceController()

// 解构 controller 以简化模板调用
const {
  // 语音生成相关
  ttsText,
  speechRate,
  audioState,
  generateAudio,

  // 数字人生成相关
  videoState,
  getVideoPreviewUrl,

  // 计算属性
  canGenerate,
  maxTextLength,
  textareaPlaceholder,
  speechRateMarks,
  speechRateDisplay,
  faceDurationSec,
  audioDurationSec,
  audioUrl,
  validationPassed,

  // Pipeline 状态（单一状态源）
  pipelineState,
  isPipelineBusy,
  isPipelineReady,
  isPipelineFailed,
  isPipelineCompleted,
  pipelineProgress,
  pipelineError,
  retryPipeline,
  resetPipeline,

  // 事件处理方法
  handleVoiceSelect,
  handleFileSelect,
  handleDrop,
  triggerFileSelect,
  handleSelectUpload,
  handleSelectFromLibrary,
  handleVideoSelect,
  handleVideoLoaded,
  replaceVideo,
  generateDigitalHuman,

  // UI 辅助方法
  formatDuration,
  formatFileSize,
} = controller

// ==================== 生命周期 ====================

onMounted(async () => {
  await voiceStore.refresh()
})
</script>

<style scoped lang="less">

/* ========== 页面布局 ========== */
.kling-page {
  padding: 0;
  background: var(--bg-secondary);
  min-height: 100vh;
}

.kling-content {
  display: grid;
  grid-template-columns: 480px 1fr;
  gap: 32px;
  margin: 0 auto;
}

/* 优化左侧配置面板 */
.upload-panel {
  background: var(--bg-primary);
  border-radius: 16px;
  padding: 28px;
  height: fit-content;
  position: sticky;
  top: 24px;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);

  /* 自定义滚动条 */
  &::-webkit-scrollbar {
    width: 6px;
  }

  &::-webkit-scrollbar-track {
    background: transparent;
  }

  &::-webkit-scrollbar-thumb {
    background: var(--border-light);
    border-radius: 3px;
  }

  &::-webkit-scrollbar-thumb:hover {
    background: var(--text-secondary);
  }
}

/* ========== 基础组件 ========== */
.section {
  margin-bottom: 24px;
}

.section h3 {
  color: var(--text-primary);
  margin-bottom: 12px;
  font-size: 16px;
  font-weight: 600;
}

.card-content h4 {
  color: var(--text-primary);
  font-size: 14px;
  margin-bottom: 12px;
}

.card-content p {
  color: var(--text-secondary);
  font-size: 13px;
  margin: 0;
}

/* ========== 表单控件 ========== */
.tts-textarea {
  background: var(--bg-secondary);
  border: 1px solid var(--border-light);
  border-radius: 8px;
  color: var(--text-primary);

  :deep(.ant-input) {
    background: transparent;
    border: none;
    color: var(--text-primary);
  }
}

.control-group {
  margin-bottom: 16px;
}

.control-label {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 8px;
}

.slider-card {
  border: 1px solid var(--border-light);
  border-radius: 8px;
  padding: 12px 16px;
  background: var(--bg-secondary);
}

.slider-info {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 12px;
  color: var(--text-secondary);
  margin-bottom: 8px;
}

.slider-value {
  font-size: 14px;
  font-weight: 600;
  color: var(--color-primary);
}

.reset-btn {
  padding: 4px 12px;
  border: 1px solid var(--border-light);
  background: var(--bg-primary);
  color: var(--text-secondary);
  border-radius: 4px;
  cursor: pointer;
  font-size: 12px;
}

/* ========== 视频选择 ========== */
.video-selection-cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin-bottom: 16px;
}

.video-option-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px;
  border: 1px solid var(--border-light);
  border-radius: 8px;
  background: var(--bg-primary);
  cursor: pointer;
  transition: all 0.2s;

  &:hover {
    border-color: var(--color-primary);
    background: var(--bg-secondary);
  }

  &.selected {
    border-color: var(--color-primary);
    background: rgba(var(--color-primary), 0.1);
  }
}

.card-icon {
  font-size: 24px;
  color: var(--color-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  background: rgba(var(--color-primary), 0.1);
  border-radius: 8px;
  flex-shrink: 0;
}

/* ========== 视频预览 ========== */
.selected-video-preview {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px;
  background: var(--bg-secondary);
  border: 1px solid var(--border-light);
  border-radius: 8px;
  margin-bottom: 16px;
}
.preview-thumbnail {
  position: relative;
  width: 80px;
  height: 45px;
  border-radius: 6px;
  overflow: hidden;
  background: #374151;
  flex-shrink: 0;
}
.preview-thumbnail img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.video-duration-badge {
  position: absolute;
  bottom: 4px;
  right: 4px;
  background: rgba(0, 0, 0, 0.8);
  color: var(--text-primary);
  padding: 2px 4px;
  border-radius: 3px;
  font-size: 11px;
  font-weight: 600;
}
.preview-info {
  flex: 1;
  min-width: 0;
}
.video-title {
  color: var(--text-primary);
  font-size: 14px;
  font-weight: 600;
  margin-bottom: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.video-meta {
  display: flex;
  gap: 12px;
  font-size: 12px;
  color: var(--text-secondary);
}

/* ========== 上传区域 ========== */
.upload-zone {
  min-height: 280px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px dashed var(--border-light);
  border-radius: 8px;
  background: var(--bg-secondary);

  &.drag-over {
    border-color: var(--color-primary);
    background: rgba(var(--color-primary), 0.1);
  }
}

.upload-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 40px 20px;
  text-align: center;

  h3 {
    color: var(--text-primary);
    font-size: 16px;
    font-weight: 600;
    margin: 0;
  }

  p {
    color: var(--text-secondary);
    font-size: 14px;
    margin: 0;
  }

  .ant-btn {
    padding: 8px 24px;
    font-size: 14px;
    border-radius: 6px;
  }
}

.video-preview {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 20px;
  text-align: center;

  p {
    color: var(--text-secondary);
    font-size: 14px;
    margin: 0;
    padding: 8px 16px;
    background: var(--bg-primary);
    border-radius: 6px;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

.preview-video {
  width: 100%;
  max-height: 280px;
  border-radius: 8px;
}

/* ========== 音频区域 ========== */
.audio-section {
  margin-bottom: 24px;
}

.audio-generated {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.audio-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.audio-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
}

.audio-duration {
  font-size: 12px;
  color: var(--text-secondary);
}

.audio-player-wrapper {
  width: 100%;
}

.audio-player {
  width: 100%;
  height: 36px;
}

.validation-warning {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 10px 12px;
  background: rgba(var(--color-warning), 0.1);
  border: 1px solid rgba(var(--color-warning), 0.3);
  border-radius: 6px;
  font-size: 13px;
}

.warning-icon {
  flex-shrink: 0;
  font-size: 14px;
}

.warning-text {
  color: var(--text-secondary);
  line-height: 1.4;
}

.audio-prompt {
  text-align: center;
  padding: 20px;
  background: var(--bg-secondary);
  border-radius: 8px;
  border: 1px dashed var(--border-light);
}

.audio-prompt p {
  margin: 0 0 16px 0;
  font-size: 14px;
  color: var(--text-secondary);
}

/* ========== 操作按钮 ========== */
.action-buttons {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid var(--border-light);
}

.action-buttons .ant-btn[type="primary"] {
  height: 48px;
  font-size: 16px;
  font-weight: 600;
  border-radius: 8px;
}

/* ========== 响应式 ========== */
@media (max-width: 1024px) {
  .kling-content {
    grid-template-columns: 1fr;
    padding: 16px;
    gap: 20px;
  }

  .upload-panel {
    position: static;
    padding: 20px;
  }

  .video-selection-cards {
    grid-template-columns: 1fr;
    gap: 12px;
  }

  .upload-zone {
    min-height: 240px;
  }
}
</style>
