<!-- 视频对比面板，负责源视频上传、结果预览、双播放器同步和历史结果操作。 -->
<script setup lang="ts">
import { ref, toRefs } from "vue";
import {
  UploadFilled,
  VideoCamera,
  ArrowLeft,
  ArrowRight,
  ArrowUp,
  Download,
  Loading,
  Clock,
  CircleClose,
  Check,
  Delete,
} from "@element-plus/icons-vue";
import type { GenerationOutput, ProjectDetail, Storyboard } from "@/api/creative";
import LoadingMedia from "./LoadingMedia.vue";
import VideoPlayer from "./VideoPlayer.vue";
import { downloadMedia } from "@/utils/download";
import { useToastStore } from "@/stores/toast";
const props = defineProps<{
  sourceUrl?: string;
  sourceMediaId?: string;
  resultUrl?: string;
  resultMediaId?: string;
  storyboard?: Storyboard;
  project?: ProjectDetail;
  uploading?: boolean;
  dragging?: boolean;
  resultLoading?: boolean;
  resultError?: string;
  resultErrorTitle?: string;
  historyCount?: number;
  historyOutputs?: GenerationOutput[];
  historyLoading?: boolean;
  activatingHistory?: string;
}>();
const { sourceUrl, resultUrl, storyboard, uploading, dragging, resultLoading, resultError, historyCount } =
  toRefs(props);
const historyVisible = ref(false);
const resultPlaying = ref(false);
const toast = useToastStore();
const emit = defineEmits<{
  upload: [file: File];
  history: [];
  "history-preview": [output: GenerationOutput];
  "history-activate": [output: GenerationOutput];
  "history-delete": [output: GenerationOutput];
}>();
const sourcePlayer = ref<InstanceType<typeof VideoPlayer>>();
const resultPlayer = ref<InstanceType<typeof VideoPlayer>>();
const eraNames: Record<string, string> = {
  modern_city: "现代都市",
  ancient_history: "古代历史",
  ancient_fantasy: "古代玄幻",
  ancient_xianxia: "古代仙侠",
  future_scifi: "未来科幻",
  other: "其他",
};
const localizationNames: Record<string, string> = {
  china: "中国",
  uk_us: "英美",
  korea: "韩国",
  japan: "日本",
  france: "法国",
  russia: "俄罗斯",
  vietnam: "越南",
  thailand: "泰国",
  india: "印度",
};
/** 获取项目年代的中文展示名称，并兼容自定义年代。 */
function eraLabel() {
  const project = props.project;
  if (!project) return "";
  return project.era_type === "other" && project.custom_era?.trim()
    ? project.custom_era.trim()
    : eraNames[project.era_type] || project.era_type;
}
/** 获取项目本土化地区的中文展示名称。 */
function localizationLabel() {
  const value = props.project?.localization || "";
  return localizationNames[value] || value;
}
/** 从文件输入框读取视频并沿用原上传事件通知父组件。 */
function choose(e: Event) {
  const input = e.target as HTMLInputElement;
  const file = input.files?.[0];
  input.value = "";
  if (file) emit("upload", file);
}
/** 下载当前转绘结果，并将下载失败转换为用户提示。 */
async function downloadResult() {
  if (!props.resultUrl) return;
  try {
    await downloadMedia(
      props.resultMediaId,
      props.resultUrl,
      `分镜${props.storyboard?.sequence_no || ""}-转绘结果.mp4`,
    );
  } catch (error) {
    toast.error(error instanceof Error ? error.message : "视频下载失败");
  }
}
/** 关闭历史弹层并预览指定生成结果。 */
function previewHistory(output: GenerationOutput) {
  historyVisible.value = false;
  emit("history-preview", output);
}
/** 将当前分镜起始毫秒换算为源视频秒偏移。 */
function sourceOffset() {
  return (props.storyboard?.start_ms || 0) / 1000;
}
/** 结果视频播放时，将源视频定位到对应分镜时间并同步播放。 */
function syncSourcePlay(time: number) {
  resultPlaying.value = true;
  sourcePlayer.value?.setCurrentTime(sourceOffset() + time);
  sourcePlayer.value?.play();
}
/** 结果视频暂停时同步暂停源视频。 */
function syncSourcePause() {
  resultPlaying.value = false;
  sourcePlayer.value?.pause();
}
/** 结果视频跳转时同步更新源视频播放位置。 */
function syncSourceSeek(time: number) {
  sourcePlayer.value?.setCurrentTime(sourceOffset() + time);
}
/** 结果视频结束时重置播放状态并暂停源视频。 */
function syncSourceEnded() {
  resultPlaying.value = false;
  sourcePlayer.value?.pause();
}
/** 在结果视频仍播放时恢复拖动后的源视频同步位置。 */
function restoreSourceAfterSeek() {
  if (!resultPlaying.value) return;
  sourcePlayer.value?.setCurrentTime(sourceOffset() + (resultPlayer.value?.currentTime() || 0));
}
/** 源视频取消静音时自动静音结果视频，避免双路声音叠加。 */
function sourceVolumeChanged(muted: boolean) {
  if (!muted) resultPlayer.value?.setMuted(true);
}
/** 结果视频取消静音时自动静音源视频，避免双路声音叠加。 */
function resultVolumeChanged(muted: boolean) {
  if (!muted) sourcePlayer.value?.setMuted(true);
}
/** 源视频进入全屏前暂停结果视频。 */
function pauseResultForSourceFullscreen() {
  resultPlayer.value?.pause();
}
/** 结果视频进入全屏前暂停源视频。 */
function pauseSourceForResultFullscreen() {
  sourcePlayer.value?.pause();
}
</script>
<style scoped>
.compare-panel > header {
  display: grid !important;
  grid-template-columns: 1fr auto 1fr;
}

.compare-panel > header > strong {
  justify-self: start;
}

.compare-panel > header > i {
  justify-self: end;
}

.project-meta {
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  color: var(--web-text-meta);
  font-size: 11px;
  white-space: nowrap;
}

.project-meta span {
  position: relative;
}

.project-meta span + span:before {
  content: "";
  position: absolute;
  left: -7px;
  top: 50%;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background: var(--web-divider-soft);
  transform: translate(-50%, -50%);
}

@media (max-width: 1000px) {
  .project-meta {
    gap: 8px;
    font-size: 10px;
  }

  .project-meta span + span:before {
    left: -4px;
  }
}
</style>
<template>
  <section class="compare-panel">
    <header>
      <strong>镜头查看</strong>
      <div
        v-if="project"
        class="project-meta"
      >
        <span>{{ eraLabel() }}</span
        ><span>{{ project.style_name }}</span
        ><span>{{ localizationLabel() }}</span
        ><span>{{ project.aspect_ratio }}</span>
      </div>
      <i></i>
    </header>
    <div class="compare-grid">
      <article
        class="video-pane"
        :class="{ dragging }"
      >
        <div class="pane-label">
          <span>原片</span>
          <div
            v-if="sourceUrl"
            class="frame-actions"
          >
            <button
              type="button"
              title="上一帧"
              @click="sourcePlayer?.previousFrame()"
            >
              <el-icon>
                <ArrowLeft />
              </el-icon></button
            ><button
              type="button"
              title="下一帧"
              @click="sourcePlayer?.nextFrame()"
            >
              <el-icon>
                <ArrowRight />
              </el-icon></button
            ><button
              type="button"
              title="导出静帧"
              @click="sourcePlayer?.exportFrame()"
            >
              <el-icon>
                <ArrowUp />
              </el-icon>
            </button>
          </div>
        </div>
        <div
          v-loading="uploading"
          class="video-stage"
        >
          <VideoPlayer
            v-if="sourceUrl"
            ref="sourcePlayer"
            :src="sourceUrl"
            :media-id="sourceMediaId"
            :start-time="storyboard ? storyboard.start_ms / 1000 : 0"
            :aspect-ratio="project?.aspect_ratio"
            @seek-end="restoreSourceAfterSeek"
            @volume="sourceVolumeChanged"
            @fullscreen-open="pauseResultForSourceFullscreen"
          /><label
            v-else
            class="upload-video"
            ><input
              type="file"
              accept="video/mp4,video/webm,video/quicktime,video/x-m4v,.mp4,.webm,.mov,.m4v"
              @change="choose"
            /><el-icon> <UploadFilled /> </el-icon><strong>上传原视频</strong
            ><small>点击右上角“上传视频”，或将文件拖拽到页面</small
            ><small>支持 MP4、WebM、MOV、M4V，最大 50MB，时长不超过 3 分钟</small></label
          >
        </div>
      </article>
      <article class="video-pane">
        <div class="pane-label">
          <span>转绘结果</span>
          <div class="result-tools">
            <el-popover
              v-model:visible="historyVisible"
              trigger="click"
              placement="bottom-end"
              :width="project?.aspect_ratio === '9:16' ? 330 : 450"
              :show-after="0"
              :hide-after="0"
              popper-class="creative-history-popover"
              @show="emit('history')"
            >
              <template #reference
                ><button
                  type="button"
                  class="history-button"
                >
                  <el-icon> <Clock /> </el-icon>历史记录{{ historyCount ? ` (${historyCount})` : "" }}
                </button></template
              >
              <div
                v-loading="historyLoading"
                class="history-list"
                :class="{ portrait: project?.aspect_ratio === '9:16' }"
              >
                <div
                  v-for="output in historyOutputs"
                  :key="output.id"
                  class="history-item"
                  :class="{ active: output.active }"
                  @click="previewHistory(output)"
                >
                  <LoadingMedia
                    type="video"
                    compact
                    :src="output.public_url"
                    muted
                    playsinline
                    preload="metadata"
                    disablepictureinpicture
                  />
                  <span
                    v-if="!output.active"
                    class="history-item-actions"
                    @click.stop
                  >
                    <button
                      type="button"
                      title="设为主视频"
                      :disabled="activatingHistory === output.id"
                      @click="emit('history-activate', output)"
                    >
                      <el-icon><Check /></el-icon>
                    </button>
                    <button
                      type="button"
                      title="删除历史视频"
                      @click="emit('history-delete', output)"
                    >
                      <el-icon><Delete /></el-icon>
                    </button>
                  </span>
                </div>
                <span
                  v-if="!historyOutputs?.length && !historyLoading"
                  class="history-empty"
                  >暂无历史视频</span
                >
              </div>
            </el-popover>
            <div
              v-if="resultUrl"
              class="frame-actions"
            >
              <button
                type="button"
                title="上一帧"
                @click="resultPlayer?.previousFrame()"
              >
                <el-icon>
                  <ArrowLeft />
                </el-icon></button
              ><button
                type="button"
                title="下一帧"
                @click="resultPlayer?.nextFrame()"
              >
                <el-icon>
                  <ArrowRight />
                </el-icon></button
              ><button
                type="button"
                title="导出静帧"
                @click="resultPlayer?.exportFrame()"
              >
                <el-icon>
                  <ArrowUp />
                </el-icon></button
              ><button
                type="button"
                title="下载转绘视频"
                @click="downloadResult"
              >
                <el-icon>
                  <Download />
                </el-icon>
              </button>
            </div>
          </div>
        </div>
        <div class="video-stage">
          <VideoPlayer
            v-if="resultUrl && !resultError"
            ref="resultPlayer"
            :src="resultUrl"
            :media-id="resultMediaId"
            :aspect-ratio="project?.aspect_ratio"
            @play="syncSourcePlay"
            @pause="syncSourcePause"
            @seek="syncSourceSeek"
            @ended="syncSourceEnded"
            @volume="resultVolumeChanged"
            @fullscreen-open="pauseSourceForResultFullscreen"
          />
          <div
            v-else-if="resultError"
            class="result-empty result-error"
          >
            <el-icon><CircleClose /></el-icon><strong>{{ resultErrorTitle || "生成失败" }}</strong>
            <p>{{ resultError }}</p>
          </div>
          <div
            v-else
            class="result-empty"
          >
            <el-icon>
              <Loading
                v-if="resultLoading"
                class="spin"
              />
              <VideoCamera v-else /> </el-icon
            ><strong>{{ resultLoading ? "正在生成当前分镜" : "尚未生成当前分镜" }}</strong>
          </div>
        </div>
      </article>
    </div>
  </section>
</template>
<style scoped>
.compare-panel {
  min-width: 0;
  background: var(--web-surface-soft);
  border: 1px solid var(--web-line-strong);
  border-radius: 11px;
  overflow: hidden;
}

.compare-panel > header {
  height: 40px;
  padding: 0 15px;
  display: flex;
  align-items: center;
  border-bottom: 1px solid var(--web-line-strong);
  color: var(--web-text-secondary);
}

.compare-grid {
  padding: 12px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.video-pane {
  min-width: 0;
  background: var(--web-surface-soft);
  border: 1px solid var(--web-line-strong);
  border-radius: 8px;
  overflow: hidden;
}

.video-pane.dragging {
  border-color: var(--web-primary);
  background: var(--web-surface);
}

.pane-label {
  height: 38px;
  padding: 0 11px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--web-text-secondary);
  border-bottom: 1px solid var(--web-line-strong);
  font-size: 12px;
}

.result-tools {
  display: flex;
  align-items: center;
  gap: 5px;
}

.history-button {
  height: 24px;
  padding: 0 7px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--web-text-secondary);
  background: var(--web-surface);
  border: 1px solid var(--web-line-strong);
  border-radius: 5px;
  font-size: 10px;
  cursor: pointer;
}

.history-button:hover {
  color: var(--web-accent-dark);
  border-color: var(--web-line-hover);
}

.history-button:focus,
.history-button:focus-visible {
  outline: none;
  box-shadow: none;
}

.frame-actions {
  display: flex;
  gap: 4px;
}

.frame-actions button {
  width: 24px;
  height: 24px;
  padding: 0;
  display: grid;
  place-items: center;
  color: var(--web-text-secondary);
  background: var(--web-surface);
  border: 1px solid var(--web-line-strong);
  border-radius: 5px;
  cursor: pointer;
}

.frame-actions button:hover {
  color: var(--web-accent-dark);
  border-color: var(--web-line-hover);
}

.video-stage,
.video-stage :deep(.video-player),
.upload-video,
.result-empty {
  width: 100%;
  height: clamp(260px, 30vw, 450px);
}

.video-stage :deep(.el-loading-mask) {
  background: var(--web-loading-mask) !important;
}

.video-stage :deep(.el-loading-spinner .circular) {
  stroke: var(--web-primary-hover);
}

.upload-video,
.result-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  color: var(--web-text-secondary);
  cursor: pointer;
}

.upload-video input {
  display: none;
}

.upload-video small {
  margin-top: 7px;
  color: var(--web-text-tertiary);
  font-size: 10px;
  line-height: 1.45;
  text-align: center;
}

.upload-video small + small {
  margin-top: 3px;
  color: var(--web-text-detail);
}

.result-error {
  padding: 24px;
  text-align: center;
}

.result-empty.result-error .el-icon {
  color: var(--web-accent-dark);
}

.result-error p {
  max-width: 90%;
  margin: 8px 0 0;
  color: var(--web-accent-dark);
  font-size: 12px;
  line-height: 1.55;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.upload-video .el-icon,
.result-empty .el-icon {
  margin-bottom: 13px;
  color: var(--web-icon-muted);
  font-size: 34px;
}

.spin {
  animation: spin 1s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

@media (max-width: 780px) {
  .compare-grid {
    grid-template-columns: 1fr;
  }

  .video-stage,
  .video-stage :deep(.video-player),
  .upload-video,
  .result-empty {
    height: 300px;
  }
}
</style>
<style>
.creative-history-popover {
  padding: 8px !important;
  background: var(--web-surface-soft) !important;
  border: 1px solid var(--web-line-emphasis) !important;
}

.creative-history-popover .el-popper__arrow::before {
  background: var(--web-surface-soft) !important;
  border-color: var(--web-line-emphasis) !important;
}

.history-list {
  position: relative;
  max-height: 420px;
  padding-right: 4px;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
  scrollbar-color: var(--web-scrollbar-compact) transparent;
  scrollbar-gutter: stable;
  scrollbar-width: thin;
}

.history-list::-webkit-scrollbar {
  width: 6px;
}

.history-list::-webkit-scrollbar-track {
  background: transparent;
}

.history-list::-webkit-scrollbar-thumb {
  background: var(--web-divider-soft);
  border-radius: 6px;
}

.history-list::-webkit-scrollbar-thumb:hover {
  background: var(--web-scrollbar-thumb-hover);
}

.creative-history-popover .history-list .el-loading-mask {
  background: var(--web-loading-mask) !important;
}

.creative-history-popover .history-list .el-loading-spinner .circular {
  stroke: var(--web-primary-hover);
}

.history-item {
  position: relative;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  padding: 5px;
  overflow: hidden;
  background: var(--web-surface-soft);
  border: 1px solid var(--web-line-strong);
  border-radius: 6px;
  cursor: pointer;
}

.history-list.portrait .history-item {
  aspect-ratio: 9 / 16;
}

.history-item:hover,
.history-item.active {
  border-color: var(--web-line-active);
  background: var(--web-surface);
}

.history-item video,
.history-item :deep(.loading-media-element) {
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: var(--web-surface-soft);
  border-radius: 4px;
  pointer-events: none;
}

.history-item-actions {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: var(--web-history-overlay);
  opacity: 0;
  transition: opacity 0.12s ease;
}

.history-item:hover .history-item-actions,
.history-item:focus-visible .history-item-actions {
  opacity: 1;
}

.history-item-actions button {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  color: var(--web-text);
  background: var(--web-hover);
  border: 1px solid var(--web-line-emphasis);
  border-radius: 6px;
  cursor: pointer;
}

.history-item-actions button:hover {
  color: var(--web-accent-dark);
  border-color: var(--web-line-action-hover);
}

.history-item-actions button:last-child:hover {
  color: var(--web-accent-dark);
  border-color: var(--web-line-danger-hover);
}

.history-item-actions button:disabled {
  opacity: 0.55;
  cursor: wait;
}

.history-empty {
  padding: 18px 8px;
  color: var(--web-text-tertiary);
  text-align: center;
  font-size: 12px;
}

@media (max-width: 620px) {
  .creative-history-popover {
    max-width: calc(100vw - 28px) !important;
  }
  .history-list {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
</style>
