import { useState } from 'react';
import { invoke } from '@tauri-apps/api/core';
import { getLocalFileUrl } from '../../../utils/fileUrl';
import { getFriendlyErrorMessage } from '../../../utils/errorMessage';
import { toast } from '../../../store/uiStore';
import { useProjectStore, saveMetaToLocalFile } from '../../../store';
import { useProgressStore } from '../../../store/progressStore';
import { usePointStore } from '../../../store';
import { useAuthStore } from '../../../store';

import {
  concatVideoClips,
  generateEmptyShotClip,
  uploadVideoFile,
  downloadFile,
} from '../../../api/modules/videoCompose';
import { createTask, getTaskStatus } from '../../../api/modules/task';
import { pointsApi } from '../../../api/modules/points';
import { getCurrentProjectId, localProjectApi } from '../../../api/modules/localStorage';
import { computeAssignedIntervals } from '../utils/videoTimeline';
import type { ScriptShot } from '../../../types/project';

export interface UseVideoGenerationParams {
  selectedAvatarMaterial: { id: string; name: string; duration: number; path: string } | null;
  materialMatchMap: Record<string, { url: string; duration: number } | null>;
  estimatedVideoPoints: number;
  videoMultiplier: number;
  onSuccess: () => void;
  checkBalance: (points: number, bizName: string, strict?: boolean) => Promise<boolean>;
  handleError: (error: unknown, bizName: string, requiredPoints?: number) => boolean;
}

export interface UseVideoGenerationResult {
  isComposing: boolean;
  handleGenerate: () => Promise<void>;
}

/**
 * 视频生成核心流程 Hook
 * - Step 1: 对每个 segment 裁剪人物素材 → 上传 → 提交 Vidu 对口型
 * - Step 2: 轮询 Vidu 任务 → 下载生成视频
 * - Step 3: 生成各分镜标准化片段（空镜+分镜裁剪）
 * - Step 4: 拼接所有片段 → 扣费 → 清理中间产物
 */
export function useVideoGeneration({
  selectedAvatarMaterial,
  materialMatchMap,
  estimatedVideoPoints,
  videoMultiplier,
  onSuccess,
  checkBalance,
  handleError,
}: UseVideoGenerationParams): UseVideoGenerationResult {
  const [isComposing, setIsComposing] = useState(false);
  const segments = useProjectStore((state) => state.segments);
  const dubbingAudioPath = useProjectStore((state) => state.dubbingAudioPath);
  const updateSegment = useProjectStore((state) => state.updateSegment);
  const projectId = getCurrentProjectId();
  const fetchBalance = usePointStore((state) => state.fetchBalance);


  const handleGenerate = async () => {
    if (!selectedAvatarMaterial) {
      toast.warning('请选择人物出镜素材');
      return;
    }

    if (!projectId) {
      toast.error('项目ID不存在，请保存项目后再试');
      return;
    }

    const shots = segments;
    if (shots.length === 0) {
      toast.warning('暂无分镜数据，请先生成脚本');
      return;
    }

    // 严格检查：所有 shot 必须有 actualDuration
    const missingDuration = shots.find((s: ScriptShot) => s.actualDuration === undefined);
    if (missingDuration) {
      toast.error(`镜头 ${missingDuration.id} 缺少时长数据，请返回第二步重新生成配音`);
      return;
    }

    // 检查空镜素材是否都已匹配
    const emptyShots = shots.filter((s: ScriptShot) => s.type === 'empty_shot');
    const unmatched = emptyShots.filter((s: ScriptShot) => !materialMatchMap[String(s.id)]);
    if (unmatched.length > 0) {
      toast.warning(`有 ${unmatched.length} 个空镜未匹配到素材，请检查场景描述`);
      return;
    }

    // 积分预检
    const totalPlannedDuration = shots.reduce((sum: number, shot: ScriptShot) => {
      const durStr = typeof shot.duration === 'string' ? shot.duration : '5s';
      return sum + parseFloat(durStr.replace(/[^0-9.]/g, '') || '0');
    }, 0);

    setIsComposing(true);
    const progress = useProgressStore.getState();

    try {
      // 前置积分检查（严格模式）
      const canProceed = await checkBalance(estimatedVideoPoints, '视频生成', true);
      if (!canProceed) {
        setIsComposing(false);
        return;
      }

      progress.show('视频生成');
      const avatarDuration = selectedAvatarMaterial.duration;
      const segmentShots = shots.filter((s: ScriptShot) => s.type === 'segment');

      // ========== Step 1: 对每个 segment 提交视频生成任务 ==========
      for (let i = 0; i < segmentShots.length; i++) {
        const shot = segmentShots[i];
        progress.update('正在提交视频生成任务...');

        const duration = shot.actualDuration!;
        const durStr = typeof shot.duration === 'string' ? shot.duration : '5s';
        const plannedDuration = parseFloat(durStr.replace(/[^0-9.]/g, '') || '0');

        // 1a. 获取切割后视频保存路径
        const pathRes = await invoke<{ code: number; data?: string; message: string }>(
          'get_video_save_path',
          { projectId, filename: `segment_clip_${shot.id}_${Date.now()}.mp4` }
        );
        if (pathRes.code !== 200 || !pathRes.data) {
          throw new Error(pathRes.message || '获取保存路径失败');
        }
        const clipPath = pathRes.data;

        // 1d. 仅当该分镜有 clipAudioUrl 时才截取+上传+提交 Vidu（避免无意义的上传垃圾）
        if (!shot.clipAudioUrl) {
          continue;
        }

        // 1b. 从人物素材中随机截取对应时长的片段
        const maxStart = Math.max(0, avatarDuration - duration);
        const startTime = maxStart > 0 ? Math.random() * maxStart : 0;

        // 保存 startTime（后续拼接用视频生成结果，startTime=0，但需记录原始截取点）
        updateSegment(shot.id, { lipSyncStartTime: startTime });

        const clipRes = await invoke<{ code: number; message: string }>('extract_video_segment', {
          args: {
            inputPath: selectedAvatarMaterial.path,
            start: startTime,
            duration,
            outputPath: clipPath,
          },
        });
        if (clipRes.code !== 200) {
          throw new Error(`截取视频片段失败: ${clipRes.message}`);
        }

        // 1c. 上传切割后的视频到七牛云
        const clipUrl = await uploadVideoFile(clipPath);

        // 上传成功后删除本地临时截取片段
        try {
          await invoke<{ code: number; message: string }>('delete_project_file', {
            projectId,
            filePath: clipPath,
          });
        } catch {
          // no-op: 删除临时文件失败不影响主流程
        }

        const lipSyncRes = await createTask('video', {
          videoUrl: clipUrl,
          audioUrl: shot.clipAudioUrl,
          plannedDuration,
          totalPlannedDuration,
        });

        // 1e. 保存 taskId 到 segment
        updateSegment(shot.id, { lipSyncTaskId: lipSyncRes.taskId, lipSyncState: 'processing' });
      }

      // 保存 segments.json（包含 lipSyncTaskId）
      const currentSegments = useProjectStore.getState().segments;
      await localProjectApi.saveSegments(projectId, currentSegments);

      // ========== Step 2: 轮询视频生成任务状态 ==========
      const currentSegmentsForPoll = useProjectStore.getState().segments;
      const segmentShotsWithTask = currentSegmentsForPoll.filter(
        (s: ScriptShot) => s.type === 'segment' && s.lipSyncTaskId
      );

      if (segmentShotsWithTask.length > 0) {
        progress.update('正在等待视频处理...');
        const pollInterval = 5000;
        const timeout = 600_000; // 10 分钟
        const start = Date.now();
        let isAllDone = false;

        while (Date.now() - start < timeout) {
          const states = await Promise.all(
            segmentShotsWithTask.map(async (shot: ScriptShot) => {
              const status = await getTaskStatus(shot.lipSyncTaskId!);
              return { shot, status };
            })
          );

          isAllDone = states.every(
            ({ status }) => status.status === 'completed' || status.status === 'failed'
          );

          if (isAllDone) {
            // 检查是否有失败的
            const failed = states.filter(({ status }) => status.status === 'failed');
            if (failed.length > 0) {
              const failedIds = failed.map(({ shot }) => shot.id).join(', ');
              throw new Error(`视频生成失败 (分镜 ${failedIds})`);
            }
            break;
          }

          const pendingCount = states.filter(
            ({ status }) => status.status !== 'completed' && status.status !== 'failed'
          ).length;
          progress.update(`视频处理中... 剩余 ${pendingCount}/${segmentShotsWithTask.length}`);

          await new Promise((resolve) => setTimeout(resolve, pollInterval));
        }

        if (!isAllDone) {
          throw new Error('视频生成超时，请稍后重试');
        }

        // 下载生成视频到本地
        progress.update('正在下载生成视频...');
        for (const shot of segmentShotsWithTask) {
          const status = await getTaskStatus(shot.lipSyncTaskId!);
          const videoUrl = (status.result as { videoUrl?: string } | null)?.videoUrl;
          if (status.status === 'completed' && videoUrl) {
            const pathRes = await invoke<{ code: number; data?: string; message: string }>(
              'get_video_save_path',
              { projectId, filename: `lipsync_${shot.id}_${Date.now()}.mp4` }
            );
            if (pathRes.code !== 200 || !pathRes.data) {
              throw new Error(pathRes.message || '获取保存路径失败');
            }
            await downloadFile(videoUrl, pathRes.data);
            updateSegment(shot.id, {
              lipSyncState: 'success',
              lipSyncVideoUrl: videoUrl,
              lipSyncVideoPath: pathRes.data,
            });
          }
        }

        // 保存 segments.json（包含 lipSyncVideoPath）
        const segmentsAfterDownload = useProjectStore.getState().segments;
        await localProjectApi.saveSegments(projectId, segmentsAfterDownload);
      }

      // ========== Step 3: 生成各分镜的标准化片段 ==========
      if (!dubbingAudioPath) {
        throw new Error('缺少配音音频文件，请回到第2步重新生成音频');
      }

      progress.update('正在生成视频片段...');

      const currentSegmentsForCompose = useProjectStore.getState().segments;
      const assignedMap = computeAssignedIntervals(currentSegmentsForCompose);
      const clipPaths: string[] = [];

      for (let i = 0; i < currentSegmentsForCompose.length; i++) {
        const shot = currentSegmentsForCompose[i];
        const assigned = assignedMap[shot.id];
        const duration = (assigned.assignedEnd - assigned.assignedStart) / 1000;

        if (shot.type === 'empty_shot') {
          progress.update('正在处理空镜片段...');
          const matched = materialMatchMap[String(shot.id)];
          if (!matched) {
            throw new Error(`空镜 ${shot.id} 未匹配到素材`);
          }

          const clipPathRes = await invoke<{ code: number; data?: string; message: string }>(
            'get_video_save_path',
            { projectId, filename: `empty_shot_${shot.id}_${Date.now()}.mp4` }
          );
          if (clipPathRes.code !== 200 || !clipPathRes.data) {
            throw new Error(clipPathRes.message || '获取片段保存路径失败');
          }

          await generateEmptyShotClip({
            videoSource: matched.url,
            audioSource: dubbingAudioPath,
            audioStart: assigned.assignedStart / 1000,
            duration,
            outputPath: clipPathRes.data,
          });

          clipPaths.push(clipPathRes.data);
          updateSegment(shot.id, { clipVideoPath: clipPathRes.data });
        } else {
          progress.update('正在处理分镜片段...');
          const segDuration = shot.actualDuration!;

          // 优先使用视频生成后的结果（已经是精确片段，startTime=0）
          // 未视频生成时 fallback 到原始素材，使用保存的 lipSyncStartTime 或重新计算
          const hasLipSync = !!shot.lipSyncVideoPath;
          const source = shot.lipSyncVideoPath || selectedAvatarMaterial.path;
          let startTime = 0;
          if (!hasLipSync) {
            if (shot.lipSyncStartTime !== undefined) {
              startTime = shot.lipSyncStartTime;
            } else {
              const maxStart = Math.max(0, avatarDuration - segDuration);
              startTime = maxStart > 0 ? Math.random() * maxStart : 0;
            }
          }

          const clipPathRes = await invoke<{ code: number; data?: string; message: string }>(
            'get_video_save_path',
            { projectId, filename: `segment_clip_${shot.id}_${Date.now()}.mp4` }
          );
          if (clipPathRes.code !== 200 || !clipPathRes.data) {
            throw new Error(clipPathRes.message || '获取片段保存路径失败');
          }

          await invoke<{ code: number; message: string }>('extract_video_segment', {
            args: {
              inputPath: source,
              start: startTime,
              duration: segDuration,
              outputPath: clipPathRes.data,
            },
          });

          clipPaths.push(clipPathRes.data);
          updateSegment(shot.id, { clipVideoPath: clipPathRes.data });
        }
      }

      // ========== Step 4: 拼接所有片段（音视频一起，不做替换）==========
      progress.update('正在拼接视频...');

      if (clipPaths.length === 0) {
        throw new Error('没有可拼接的视频片段');
      }

      const concatResult = await concatVideoClips(projectId, clipPaths);
      const finalVideoPath = concatResult.outputPath;

      // 拼接成功后扣费：视频生成（按完整配音音频时长，允许欠费）
      //
      // ⚠️ 风险说明：扣费在前端执行，依赖用户保持应用开启直到拼接完成。
      // 如果用户在 Vidu 任务提交后关闭应用，Vidu 任务仍会在云端跑完
      //（callback 更新后端 registry），但前端不会再执行扣费逻辑。
      // 当前接受此风险：桌面应用用户通常会等待操作完成，关闭概率较低。
      // 后续若需根治，应将扣费移到后端 vidu_callback 中（每个分镜独立扣费）。
      //
      // 优先使用 store 中已保存的音频时长（与按钮预估完全一致），降级时实时读取
      const storeAudioDuration = useProjectStore.getState().dubbingAudioDuration;
      let totalAudioDuration: number;
      if (storeAudioDuration && storeAudioDuration > 0) {
        totalAudioDuration = storeAudioDuration;
      } else {
        const audioUrl = await getLocalFileUrl(dubbingAudioPath);
        totalAudioDuration = await new Promise<number>((resolve, reject) => {
          const audio = new Audio(audioUrl);
          const timeoutId = setTimeout(() => {
            reject(new Error('读取音频时长超时，请检查音频文件'));
          }, 15000);
          audio.onloadedmetadata = () => {
            clearTimeout(timeoutId);
            resolve(audio.duration);
          };
          audio.onerror = () => {
            clearTimeout(timeoutId);
            reject(new Error('读取音频时长失败'));
          };
        });
      }
      const baseVideoPoints = Math.ceil(totalAudioDuration) * videoMultiplier;
      // 系统素材额外收费：每个空镜使用的系统素材额外 2 积分
      const systemMaterialCount = shots.filter((s: ScriptShot) => {
        if (s.type !== 'empty_shot') {return false;}
        const matched = materialMatchMap[String(s.id)];
        return matched && (matched.url.startsWith('http://') || matched.url.startsWith('https://'));
      }).length;
      const actualVideoPoints = baseVideoPoints + systemMaterialCount * 2;
      await pointsApi.consumePoints({
        points: actualVideoPoints,
        sourceType: 'video',
        sourceId: `video_${useAuthStore.getState().user?.id || 'unknown'}_${Date.now()}`,
        description: '视频生成',
      });
      await fetchBalance();

      // 清理中间产物：删除 lipSyncVideoPath 和 clipVideoPath 对应的文件
      const segmentsToClean = useProjectStore.getState().segments;
      for (const shot of segmentsToClean) {
        if (shot.lipSyncVideoPath) {
          try {
            await invoke<{ code: number; message: string }>('delete_project_file', {
              projectId,
              filePath: shot.lipSyncVideoPath,
            });
          } catch {
            // no-op: 删除生成视频失败不影响主流程
          }
        }
        if (shot.clipVideoPath) {
          try {
            await invoke<{ code: number; message: string }>('delete_project_file', {
              projectId,
              filePath: shot.clipVideoPath,
            });
          } catch {
            // no-op: 删除片段视频失败不影响主流程
          }
        }
      }
      // 清除 store 中的中间路径，同时把最终素材匹配结果写回 segments
      const finalSegments = segmentsToClean.map((s: ScriptShot) => ({
        ...s,
        lipSyncVideoPath: undefined,
        lipSyncVideoUrl: undefined,
        clipVideoPath: undefined,
        emptyShotMaterial:
          s.type === 'empty_shot' ? materialMatchMap[String(s.id)] || s.emptyShotMaterial : undefined,
      }));
      useProjectStore.setState({ segments: finalSegments });
      await localProjectApi.saveSegments(projectId, finalSegments);

      // 保存最终结果到 meta.json，同时同步 store（确保当前页面立即生效）
      useProjectStore.setState({ composedVideoPath: finalVideoPath });
      await saveMetaToLocalFile({
        composedVideoPath: finalVideoPath,
        currentStep: 3,
      });

      useProjectStore.getState().clearStepDirty(3);
      useProjectStore.getState().markStepsDirty(3);
      onSuccess();

      progress.success('视频生成完成', actualVideoPoints);
    } catch (error) {
      console.error('[VideoGeneration] 生成失败:', error);
      const msg = getFriendlyErrorMessage(error, '视频生成失败，请稍后重试');
      if (handleError(error, '视频生成', estimatedVideoPoints)) {
        progress.hide();
      } else {
        progress.error(msg);
      }
    } finally {
      setIsComposing(false);
    }
  };

  return { isComposing, handleGenerate };
}
