/**
 * 字幕烧录页面 (Step 4)
 * =====================
 *
 * 单视频模式：不再按分镜打轴，对成品视频统一打轴后压制字幕。
 * 布局：左侧操作区 + 右侧预览区（使用 step-layout 标准布局）
 */

import { useState, useRef, useEffect, useMemo } from 'react';
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
import { useProjectStore, saveMetaToLocalFile } from '../../store';
import { getCurrentProjectId } from '../../api/modules/localStorage';
import { scriptApi } from '../../api/modules/script';
import { pointsApi } from '../../api/modules/points';
import { usePointStore, useAuthStore } from '../../store';
import RechargeModal from '../../components/RechargeModal/RechargeModal';
import ConfirmModal from '../../components/Modal/ConfirmModal';

import { useLocalVideo } from '../../hooks/useLocalVideo';
import { useCanvasSubtitleRenderer } from '../../hooks/useCanvasSubtitleRenderer';
import { generateAssFromAlignment, saveAssFile, htmlColorToAss, AssStyle, trimTrailingPunctuation } from '../../utils/assGenerator';
import { useProgressStore } from '../../store/progressStore';
import { toast } from '../../store/uiStore';
import { getFriendlyErrorMessage } from '../../utils/errorMessage';
import { usePointsCheck } from '../../hooks/usePointsCheck';

import './SubtitleBurning.css';

// 标题预设
interface TitlePreset {
  id: string;
  name: string;
  primaryColor: string;
  outlineColor: string;
  backColor: string;
  borderStyle: number;
  outline: number;
}

// 大标题预设（可以有背景色块）
const MAIN_TITLE_PRESETS: TitlePreset[] = [
  { id: 'yellow-box', name: '黄黑撞色', primaryColor: '#000000', outlineColor: '#000000', backColor: '#FFD700', borderStyle: 3, outline: 0 },
  { id: 'beige-brown', name: '蓝黄对比', primaryColor: '#FFD700', outlineColor: '#00008B', backColor: '#00008B', borderStyle: 3, outline: 0 },
  { id: 'white-box', name: '红白高亮', primaryColor: '#FFFFFF', outlineColor: '#CC0000', backColor: '#CC0000', borderStyle: 3, outline: 0 },
  { id: 'black-box', name: '黑白极简', primaryColor: '#FFFFFF', outlineColor: '#FFFFFF', backColor: '#000000', borderStyle: 3, outline: 0 },
];

// 小标题预设（只做描边，无背景色）
const SUB_TITLE_PRESETS: TitlePreset[] = [
  { id: 'white', name: '暖黄棕边', primaryColor: '#FFF8DC', outlineColor: '#5C4033', backColor: '#000000', borderStyle: 1, outline: 4 },
  { id: 'black', name: '白灰描边', primaryColor: '#FFFFFF', outlineColor: '#555555', backColor: '#000000', borderStyle: 1, outline: 4 },
  { id: 'teal', name: '浅灰描边', primaryColor: '#D0D0D0', outlineColor: '#000000', backColor: '#000000', borderStyle: 1, outline: 4 },
  { id: 'yellow', name: '亮黄描边', primaryColor: '#FFD700', outlineColor: '#000000', backColor: '#000000', borderStyle: 1, outline: 4 },
];

// 字幕预设（固定64px，只做描边）
const SUBTITLE_PRESETS: TitlePreset[] = [
  { id: 'white', name: '经典纯白', primaryColor: '#FFFFFF', outlineColor: '#000000', backColor: '#000000', borderStyle: 1, outline: 2 },
  { id: 'black', name: '极简纯黑', primaryColor: '#000000', outlineColor: '#FFFFFF', backColor: '#000000', borderStyle: 1, outline: 2 },
  { id: 'teal', name: '银灰描边', primaryColor: '#D0D0D0', outlineColor: '#000000', backColor: '#000000', borderStyle: 1, outline: 2 },
  { id: 'yellow', name: '明黄描边', primaryColor: '#FFD700', outlineColor: '#000000', backColor: '#000000', borderStyle: 1, outline: 2 },
];

export default function SubtitleBurning() {

  const projectId = getCurrentProjectId();

  // 成品视频
  const composedVideoUrl = useProjectStore(state => state.composedVideoUrl);
  const composedVideoPath = useProjectStore(state => state.composedVideoPath);

  const actualVideoUrl = composedVideoUrl;
  const actualVideoPath = composedVideoPath;

  // 打轴结果直接从 Step 2 复用
  const alignment = useProjectStore(state => state.subtitleAlignment);
  const [isBurning, setIsBurning] = useState(false);
  const [previewMode, setPreviewMode] = useState<'style' | 'result'>('style');
  const [isGeneratingMainTitle, setIsGeneratingMainTitle] = useState(false);
  const [isGeneratingSubTitle, setIsGeneratingSubTitle] = useState(false);
  const [showPointsModal, setShowPointsModal] = useState(false);
  const showRechargeModal = usePointStore(state => state.showRechargeModal);
  const setShowRechargeModal = usePointStore(state => state.setShowRechargeModal);
  const fetchBalance = usePointStore(state => state.fetchBalance);

  // 视频播放相关
  const videoRef = useRef<HTMLVideoElement>(null);
  const canvasRef = useRef<HTMLCanvasElement>(null);

  // 预览用
  const { videoUrl: loadedVideoUrl } = useLocalVideo(actualVideoUrl || actualVideoPath);
  const burnedVideoPath = useProjectStore(state => state.burnedVideoPath);
  const stepDirtyFlags = useProjectStore(state => state.stepDirtyFlags);
  const isStepDirty = (stepDirtyFlags?.[4] ?? false) && !!burnedVideoPath;
  const { videoUrl: burnedVideoUrl } = useLocalVideo(burnedVideoPath || undefined);
  const hasBurnedVideo = !!burnedVideoPath;

  // 大标题 / 小标题（从 store 读取，非必填，无默认值）
  const storeMainTitle = useProjectStore(state => state.mainTitle);
  const storeSubTitle = useProjectStore(state => state.subTitle);
  const storeMainTitlePreset = useProjectStore(state => state.mainTitlePreset);
  const storeSubTitlePreset = useProjectStore(state => state.subTitlePreset);
  const storeCaptionPreset = useProjectStore(state => state.captionPreset);

  const mainTitle = storeMainTitle ?? '';
  const subTitle = storeSubTitle ?? '';
  const mainTitlePreset = storeMainTitlePreset || 'yellow-box';
  const subTitlePreset = storeSubTitlePreset || 'white';
  const captionPreset = storeCaptionPreset || 'white';

  const setMainTitle = (value: string) => {
    useProjectStore.setState({ mainTitle: value });
  };
  const setSubTitle = (value: string) => {
    useProjectStore.setState({ subTitle: value });
  };

  const { checkBalance } = usePointsCheck();

  // 智能生成标题
  const handleGenerateTitle = async (titleType: 'main' | 'sub') => {
    const utterances = alignment?.utterances;
    if (!utterances || utterances.length === 0) {
      toast.error('暂无脚本内容，请先生成脚本');
      return;
    }
    // 前置积分检查
    const titlePoints = usePointStore.getState().getRule('title')?.points || 1;
    const canProceed = await checkBalance(titlePoints, '标题生成', true);
    if (!canProceed) {return;}

    const scriptContent = utterances.map(u => u.text).join('\n');
    const maxLength = titleType === 'main' ? 8 : 10;

    if (titleType === 'main') {
      setIsGeneratingMainTitle(true);
    } else {
      setIsGeneratingSubTitle(true);
    }
    try {
      const res = await scriptApi.generateTitle({
        scriptContent,
        titleType,
        maxLength,
        usage: 'video',
      });
      if (titleType === 'main') {
        setMainTitle(res.title);
      } else {
        setSubTitle(res.title);
      }
      toast.success(`${titleType === 'main' ? '大标题' : '小标题'}生成成功`);
    } catch (e) {
      const msg = getFriendlyErrorMessage(e, '标题生成失败，请稍后重试');
      if (msg.includes('积分不足')) {
        setShowPointsModal(true);
        return;
      }
      toast.error(msg);
    } finally {
      if (titleType === 'main') {
        setIsGeneratingMainTitle(false);
      } else {
        setIsGeneratingSubTitle(false);
      }
    }
  };
  const setMainTitlePreset = (value: string) => {
    useProjectStore.setState({ mainTitlePreset: value });
    setPreviewMode('style');
  };
  const setSubTitlePreset = (value: string) => {
    useProjectStore.setState({ subTitlePreset: value });
    setPreviewMode('style');
  };
  const setCaptionPreset = (value: string) => {
    useProjectStore.setState({ captionPreset: value });
    setPreviewMode('style');
  };

  // 构建 ASS Style 参数
  const videoDurationMs = (alignment?.duration || 0) * 1000;

  const buildSubtitleStyle = (preset: TitlePreset): Partial<AssStyle> => ({
    fontSize: 64,
    primaryColor: htmlColorToAss(preset.primaryColor),
    outlineColor: htmlColorToAss(preset.outlineColor),
    backColor: htmlColorToAss(preset.backColor),
    borderStyle: preset.borderStyle,
    outline: preset.outline,
    shadow: 0,
    alignment: 2,
    marginV: 480,
    marginL: 160,
    marginR: 160,
  });

  const buildMainTitleStyle = (preset: TitlePreset): Partial<AssStyle> => ({
    fontSize: 96,
    primaryColor: htmlColorToAss(preset.primaryColor),
    outlineColor: htmlColorToAss(preset.outlineColor),
    backColor: htmlColorToAss(preset.backColor),
    borderStyle: preset.borderStyle,
    outline: preset.outline,
    shadow: 0,
    alignment: 8,
    marginV: 320,
    marginL: 160,
    marginR: 160,
  });

  const buildSubTitleStyle = (preset: TitlePreset): Partial<AssStyle> => ({
    fontSize: 80,
    primaryColor: htmlColorToAss(preset.primaryColor),
    outlineColor: htmlColorToAss(preset.outlineColor),
    backColor: htmlColorToAss(preset.backColor),
    borderStyle: preset.borderStyle,
    outline: preset.outline,
    shadow: 0,
    alignment: 8,
    marginV: 440,
    marginL: 160,
    marginR: 160,
  });

  // 用 text-shadow 模拟 ASS 外描边（比 WebkitTextStroke 的居中描边更准确）
  const getOutlineShadow = (color: string, outlineWidth: number): string => {
    const w = Math.max(0.5, outlineWidth * 0.3);
    return `${-w}px ${-w}px 0 ${color}, ${w}px ${-w}px 0 ${color}, ${-w}px ${w}px 0 ${color}, ${w}px ${w}px 0 ${color}, ${-w}px 0px 0 ${color}, ${w}px 0px 0 ${color}, 0px ${-w}px 0 ${color}, 0px ${w}px 0 ${color}`;
  };

  // Canvas 2D 统一字幕渲染（替代 assjs + CSS 标题叠加）
  // 统一去除字幕末尾标点（预览和压制共用）
  const processedUtterances = alignment?.utterances?.map(u => ({
    text: trimTrailingPunctuation(u.text),
    startTime: u.start_time,
    endTime: u.end_time,
  })) ?? [];

  const subtitleEnabled = !!loadedVideoUrl && processedUtterances.length > 0;

  const subtitleStyle = useMemo(() => {
    const preset = SUBTITLE_PRESETS.find(p => p.id === captionPreset);
    return preset ? buildSubtitleStyle(preset) : {};
  }, [captionPreset]);

  const mainTitleStyle = useMemo(() => {
    const preset = MAIN_TITLE_PRESETS.find(p => p.id === mainTitlePreset);
    return preset ? buildMainTitleStyle(preset) : undefined;
  }, [mainTitlePreset]);

  const subTitleStyle = useMemo(() => {
    const preset = SUB_TITLE_PRESETS.find(p => p.id === subTitlePreset);
    return preset ? buildSubTitleStyle(preset) : undefined;
  }, [subTitlePreset]);

  useCanvasSubtitleRenderer({
    videoRef,
    canvasRef,
    utterances: processedUtterances,
    mainTitle: mainTitle || undefined,
    subTitle: subTitle || undefined,
    subtitleStyle,
    mainTitleStyle,
    subTitleStyle,
    enabled: previewMode === 'style' && subtitleEnabled,
  });

  // 监听 FFmpeg 进度事件（支持多步分段）
  const totalDurationSeconds = (alignment?.duration || 0);
  const phaseRef = useRef({ step: 1, total: 1 });
  useEffect(() => {
    let canceled = false;
    let unlistenProgress: (() => void) | undefined;
    let unlistenPhase: (() => void) | undefined;
    if (isBurning) {
      // 监听 phase 切换
      listen('ffmpeg-phase-start', event => {
        const info = event.payload as { step: number; total: number };
        phaseRef.current = { step: info.step, total: info.total };
      }).then(u => {
        if (canceled) {
          u();
        } else {
          unlistenPhase = u;
        }
      }).catch(err => console.error('Failed to listen to ffmpeg phase:', err));

      // 监听进度
      listen('ffmpeg-progress', event => {
        const timeStr = event.payload as string; // e.g. 00:00:05.12
        const parts = timeStr.split(':').map(parseFloat);
        if (parts.length === 3 && totalDurationSeconds > 0) {
          const elapsed = parts[0] * 3600 + parts[1] * 60 + parts[2];
          const p = Math.min(99, (elapsed / totalDurationSeconds) * 100);
          const { step, total } = phaseRef.current;
          // 分段映射：每步占 1/total 的比例
          const overall = ((step - 1) + p / 100) / total * 100;
          const rounded = Math.round(overall);
          useProgressStore.getState().setProgress(rounded);
          useProgressStore.getState().update(`正在压制字幕... ${rounded}%`);
        }
      })
        .then(u => {
          if (canceled) {
            u();
          } else {
            unlistenProgress = u;
          }
        })
        .catch(err => console.error('Failed to listen to ffmpeg progress:', err));
    }
    return () => {
      canceled = true;
      if (unlistenProgress) {unlistenProgress();}
      if (unlistenPhase) {unlistenPhase();}
    };
  }, [isBurning, totalDurationSeconds]);

  // 压制字幕：单次压制全局字幕到成品视频
  const handleBurn = async () => {
    if (!projectId) {
      toast.error('项目ID不存在');
      return;
    }
    if (!actualVideoPath) {
      toast.error('成品视频不存在');
      return;
    }
    if (!alignment?.utterances?.length) {
      toast.error('请先完成字幕打轴');
      return;
    }

    // 积分预检：字幕烧录
    const subtitlePoints = usePointStore.getState().getRule('subtitle_burn')?.points || 2;
    await fetchBalance();
    const currentBalance = usePointStore.getState().balance;
    if (currentBalance < subtitlePoints) {
      setShowPointsModal(true);
      return;
    }
    setShowPointsModal(false);

    setIsBurning(true);
    useProgressStore.getState().show('字幕烧录');
    useProgressStore.getState().setProgress(0);

    // 先保存配置（防止压制过程中刷新丢失）
    await saveMetaToLocalFile({
      mainTitle: useProjectStore.getState().mainTitle,
      subTitle: useProjectStore.getState().subTitle,
      mainTitlePreset: useProjectStore.getState().mainTitlePreset,
      subTitlePreset: useProjectStore.getState().subTitlePreset,
      captionPreset: useProjectStore.getState().captionPreset,
    });

    try {
      // 1. 生成标题 PNG（大标题/小标题走 overlay，不用 ASS）
      let overlayImagePath: string | undefined;
      const mtPreset = MAIN_TITLE_PRESETS.find(p => p.id === mainTitlePreset);
      const stPreset = SUB_TITLE_PRESETS.find(p => p.id === subTitlePreset);
      const mtStyle = mtPreset ? buildMainTitleStyle(mtPreset) : undefined;
      const stStyle = stPreset ? buildSubTitleStyle(stPreset) : undefined;

      if ((mainTitle && mtStyle) || (subTitle && stStyle)) {
        const { generateTitlePngDataUrl } = await import('../../utils/titlePngGenerator');
        const pngDataUrl = await generateTitlePngDataUrl(
          mainTitle || undefined,
          subTitle || undefined,
          mtStyle,
          stStyle,
        );
        // 去掉 data:image/png;base64, 前缀
        const base64 = pngDataUrl.replace(/^data:image\/png;base64,/, '');
        const pngSaveRes = await invoke<{ code: number; data?: string; message: string }>('save_project_asset', {
          projectId,
          filename: 'title_overlay.png',
          base64Data: base64,
        });
        if (pngSaveRes.code !== 200 || !pngSaveRes.data) {
          throw new Error(pngSaveRes.message || '标题图片保存失败');
        }
        overlayImagePath = pngSaveRes.data;
      }

      // 2. 生成 ASS 内容（不含大标题/小标题，它们走 PNG overlay）
      const assForBurn = generateAssFromAlignment(
        processedUtterances,
        {
          subtitleStyle: (() => {
            const preset = SUBTITLE_PRESETS.find(p => p.id === captionPreset);
            return preset ? buildSubtitleStyle(preset) : {};
          })(),
          videoDurationMs,
          includeTitlesInAss: false,
        }
      );

      // 3. 保存 ASS 文件
      const assPath = await saveAssFile(projectId, 'subtitle_global.ass', assForBurn);

      // 4. 确定输出路径
      const outputRes = await invoke<{ code: number; data?: string; message: string }>('get_video_save_path', {
        projectId,
        filename: `burned_${Date.now()}.mp4`,
      });
      if (outputRes.code !== 200 || !outputRes.data) {
        throw new Error(outputRes.message);
      }
      const outputPath = outputRes.data;

      // 5. 准备视频路径
      const resolvedVideoPath = actualVideoPath;

      // 6. 调用 Rust 压制字幕（带可选的 overlay 图片）
      const burnResult = await invoke<{ code: number; data?: string; message: string }>('burn_subtitle', {
        request: {
          video_path: resolvedVideoPath,
          ass_path: assPath,
          output_path: outputPath,
          overlay_image: overlayImagePath,
        },
      });
      if (burnResult.code !== 200 || !burnResult.data) {
        throw new Error(burnResult.message);
      }

      // 7. 保存压制结果
      useProjectStore.setState({ burnedVideoPath: outputPath });
      await saveMetaToLocalFile({ burnedVideoPath: outputPath });
      useProjectStore.getState().clearStepDirty(4);
      useProjectStore.getState().markStepsDirty(4);

      // 8. 扣费：字幕烧录（压制成功后才扣费）
      const subtitleBurnPoints = usePointStore.getState().getRule('subtitle_burn')?.points || 2;
      await pointsApi.consumePoints({
        points: subtitleBurnPoints,
        sourceType: 'subtitle_burn',
        sourceId: `subtitle_burn_${useAuthStore.getState().user?.id || 'unknown'}_${Date.now()}`,
        description: '字幕烧录',
      });
      await usePointStore.getState().fetchBalance();

      useProgressStore.getState().success('字幕烧录完成', subtitleBurnPoints);
    } catch (error: unknown) {
      const msg = error instanceof Error ? error.message : String(error);
      console.error('[SubtitleBurning] 压制失败:', error);
      if (typeof msg === 'string' && (msg.includes('402') || msg.includes('积分不足'))) {
        useProgressStore.getState().hide();
        setShowPointsModal(true);
      } else {
        useProgressStore.getState().error(msg || '压制失败');
        toast.error(msg || '压制失败');
      }
    } finally {
      setIsBurning(false);
    }
  };

  return (
    <div className="step-layout subtitle-burning">
      {isStepDirty && (
        <div className="step-dirty-banner" style={{ gridColumn: '1 / -1', margin: '0 0 var(--spacing-md) 0' }}>
          <span>上游数据已更新，当前字幕压制结果可能不匹配，建议重新压制</span>
        </div>
      )}
      <ConfirmModal
        open={showPointsModal}
        type="warning"
        title="积分不足"
        description="请先充值积分后再进行尝试"
        confirmText="立即充值"
        cancelText="稍后再说"
        confirmButtonType="danger"
        onConfirm={() => {
          setShowPointsModal(false);
          setShowRechargeModal(true);
        }}
        onCancel={() => setShowPointsModal(false)}
        onClose={() => setShowPointsModal(false)}
      />
      <RechargeModal
        open={showRechargeModal}
        onClose={() => setShowRechargeModal(false)}
        onRechargeSuccess={() => {
          fetchBalance();
          setShowPointsModal(false);
        }}
      />
      {/* 左侧操作区 */}
      <div className="step-panel-left">
        {/* 大标题设置 */}
        <div className="style-section">
          <label className="panel-label">大标题样式</label>
          <div className="title-input-row">
            <input
              type="text"
              className="title-input"
              placeholder="输入大标题"
              value={mainTitle}
              onChange={(e) => setMainTitle(e.target.value)}
              maxLength={8}
            />
            <button
              className="btn btn-ghost title-generate-btn"
              onClick={() => handleGenerateTitle('main')}
              disabled={isGeneratingMainTitle}
              title="根据脚本内容智能生成大标题"
            >
              {isGeneratingMainTitle ? '生成中...' : '智能生成'}
            </button>
          </div>
          <div className="style-presets main-title-presets">
            {MAIN_TITLE_PRESETS.map(preset => (
              <button
                key={preset.id}
                className={`preset-btn ${mainTitlePreset === preset.id ? 'active' : ''}`}
                onClick={() => setMainTitlePreset(preset.id)}
                title={preset.name}
              >
                <span
                  className="preset-preview"
                  style={{
                    color: preset.primaryColor,
                    backgroundColor: preset.borderStyle === 3 ? preset.backColor : undefined,
                    textShadow: preset.borderStyle === 1
                      ? getOutlineShadow(preset.outlineColor, preset.outline)
                      : undefined,
                    padding: preset.borderStyle === 3 ? '2px 6px' : undefined,
                    borderRadius: '2px',
                  }}
                >
                  Aa
                </span>
                <span className="preset-name">{preset.name}</span>
              </button>
            ))}
          </div>
        </div>

        {/* 小标题设置 */}
        <div className="style-section">
          <label className="panel-label">小标题样式</label>
          <div className="title-input-row">
            <input
              type="text"
              className="title-input"
              placeholder="输入小标题"
              value={subTitle}
              onChange={(e) => setSubTitle(e.target.value)}
              maxLength={10}
            />
            <button
              className="btn btn-ghost title-generate-btn"
              onClick={() => handleGenerateTitle('sub')}
              disabled={isGeneratingSubTitle}
              title="根据脚本内容智能生成小标题"
            >
              {isGeneratingSubTitle ? '生成中...' : '智能生成'}
            </button>
          </div>
          <div className="style-presets sub-title-presets">
            {SUB_TITLE_PRESETS.map(preset => (
              <button
                key={preset.id}
                className={`preset-btn ${subTitlePreset === preset.id ? 'active' : ''}`}
                onClick={() => setSubTitlePreset(preset.id)}
                title={preset.name}
              >
                <span
                  className="preset-preview"
                  style={{
                    color: preset.primaryColor,
                    textShadow: preset.borderStyle === 1
                      ? getOutlineShadow(preset.outlineColor, preset.outline)
                      : undefined,
                    backgroundColor: preset.borderStyle === 3 ? preset.backColor : undefined,
                    padding: preset.borderStyle === 3 ? '2px 6px' : undefined,
                    borderRadius: '2px',
                  }}
                >
                  Aa
                </span>
                <span className="preset-name">{preset.name}</span>
              </button>
            ))}
          </div>
        </div>

        {/* 字幕样式设置 */}
        <div className="style-section">
          <label className="panel-label">字幕样式</label>
          <div className="style-presets subtitle-presets">
            {SUBTITLE_PRESETS.map(preset => (
              <button
                key={preset.id}
                className={`preset-btn ${captionPreset === preset.id ? 'active' : ''}`}
                onClick={() => setCaptionPreset(preset.id)}
                title={preset.name}
              >
                <span
                  className="preset-preview"
                  style={{
                    color: preset.primaryColor,
                    textShadow: preset.borderStyle === 1
                      ? getOutlineShadow(preset.outlineColor, preset.outline)
                      : undefined,
                    backgroundColor: preset.borderStyle === 3 ? preset.backColor : undefined,
                    padding: preset.borderStyle === 3 ? '2px 6px' : undefined,
                    borderRadius: '2px',
                  }}
                >
                  Aa
                </span>
                <span className="preset-name">{preset.name}</span>
              </button>
            ))}
          </div>
        </div>

        {/* 压制字幕按钮 - 固定在底部 */}
        {hasBurnedVideo && !isBurning ? (
          <div className="burn-btn-group">
            <button
              className="btn btn-primary burn-btn"
              onClick={handleBurn}
              disabled={isBurning || !alignment?.utterances?.length}
            >
              重新压制字幕（{usePointStore.getState().getRule('subtitle_burn')?.points || 2} 积分）
            </button>
            <button
              className="btn btn-secondary burn-btn"
              onClick={() => setPreviewMode('result')}
              disabled={!burnedVideoUrl}
            >
              {previewMode === 'result' ? '视频预览中' : '视频预览'}
            </button>
          </div>
        ) : (
          <button
            className="btn btn-primary burn-btn"
            onClick={handleBurn}
            disabled={isBurning || !alignment?.utterances?.length}
          >
            压制字幕（{usePointStore.getState().getRule('subtitle_burn')?.points || 2} 积分）
          </button>
        )}
      </div>

      {/* 右侧预览区 */}
      <div className="step-panel-right subtitle-burning-right">
        <div className="video-preview-area">
          <div className="video-preview-header">视频预览</div>
          <div className="video-preview-wrapper">
            <div className="video-preview-container">
            {!loadedVideoUrl ? (
              <div className="video-placeholder">
                <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1">
                  <path d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" />
                </svg>
                <p>暂无视频</p>
                <p className="placeholder-sub">请先完成第3步：视频生成</p>
              </div>
            ) : previewMode === 'result' ? (
              burnedVideoUrl ? (
                <video
                  key={burnedVideoUrl}
                  src={burnedVideoUrl}
                  className="preview-video"
                  controls
                  autoPlay
                />
              ) : (
                <div className="video-placeholder">
                  <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1">
                    <path d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" />
                  </svg>
                  <p>正在加载压制后的视频...</p>
                </div>
              )
            ) : (
              <>
                <video
                  key={loadedVideoUrl}
                  ref={videoRef}
                  src={loadedVideoUrl}
                  className="preview-video"
                  controls
                  autoPlay
                />
                <canvas
                  ref={canvasRef}
                  className="subtitle-canvas-overlay"
                />

                {/* 无打轴数据时显示提示遮罩 */}
                {!alignment?.utterances?.length && (
                  <div
                    style={{
                      position: 'absolute',
                      top: 0,
                      left: 0,
                      right: 0,
                      bottom: '48px',
                      display: 'flex',
                      flexDirection: 'column',
                      alignItems: 'center',
                      justifyContent: 'flex-start',
                      backgroundColor: 'rgba(0,0,0,0.55)',
                      borderRadius: 'var(--radius-lg) var(--radius-lg) 0 0',
                      zIndex: 10,
                      color: '#fff',
                      textAlign: 'center',
                      padding: '80px var(--spacing-lg) var(--spacing-lg)',
                    }}
                  >
                    <svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" style={{ marginBottom: 'var(--spacing-sm)' }}>
                      <circle cx="12" cy="12" r="10" />
                      <path d="M12 6v6l4 2" />
                    </svg>
                    <p style={{ fontSize: 'var(--font-md)', fontWeight: 600, lineHeight: 1.4, color: '#fff' }}>字幕数据尚未就绪</p>
                    <p style={{ fontSize: 'var(--font-sm)', color: 'rgba(255,255,255,0.75)', marginTop: 'var(--spacing-xs)' }}>请返回第2步重新生成配音</p>
                  </div>
                )}
              </>
            )}
          </div>
        </div>
      </div>
    </div>
  </div>
  );
}
