const { FFmpeg } = FFmpegWASM; // 假设 FFmpegWASM 全局可用或通过模块导入

class DebugHelper
{
  static debugMode = true;
  static log(...args)
  {
    if (DebugHelper.debugMode)
    {
      console.log(...args);
    }
  }

  static error(...args)
  {
    if (DebugHelper.debugMode)
    {
      console.error(...args);
    }
  }

  static warn(...args)
  {
    if (DebugHelper.debugMode)
    {
      console.warn(...args);
    }
  }

  static info(...args)
  {
    if (DebugHelper.debugMode)
    {
      console.info(...args);
    }
  }
}

// FFmpeg 实例和加载状态
var ffmpeg = new FFmpeg();
// ffmpeg.on('log', ({ message }) => DebugHelper.log(message));
var isLoaded = false;

async function initializeFFmpeg() {
  if (isLoaded) {
    DebugHelper.log("FFmpeg 已经加载")
    return
  }
  try {
    await ffmpeg.load({
      coreURL: `/assets/ffmpeg/core-mt/ffmpeg-core.js`,
      wasmURL: `/assets/ffmpeg/core-mt/ffmpeg-core.wasm`,
      workerURL: `/assets/ffmpeg/core-mt/ffmpeg-core.worker.js`,
    });
    DebugHelper.log("FFmpeg SIMD 加载成功");
    isLoaded = true;
  } catch (error) {
    DebugHelper.log("FFmpeg SIMD 加载失败:", error);
    try {
      await ffmpeg.load({
        coreURL: `/assets/ffmpeg/core/ffmpeg-core.js`,
        wasmURL: `/assets/ffmpeg/core/ffmpeg-core.wasm`,
      });
      DebugHelper.log("FFmpeg 标准版本加载成功");
      isLoaded = true;
    } catch (fallbackError) {
      DebugHelper.log("FFmpeg 标准版本加载失败:", fallbackError);
      throw new Error("无法加载 FFmpeg 处理引擎");
    }
  }
  DebugHelper.log(ffmpeg, "FFmpeg 实例");
}

async function fetchWithTimeout(url, options = {}, timeout = 60000, signal) {
  // 合并超时信号和外部信号
  const signals = [AbortSignal.timeout(timeout), signal].filter(Boolean);
  const combinedSignal = signals.length > 0 ? AbortSignal.any(signals) : undefined;

  try {
    const response = await fetch(url, { ...options, signal: combinedSignal });
    if (!response.ok) throw new Error(`HTTP 错误！状态：${response.status}`);
    return response;
  } catch (error) {
    throw error; // 直接抛出错误，保持简洁
  }
}

function ParallelVideoProcessor(config) {
  DebugHelper.log("ParallelVideoProcessor 初始化", config);
  this.ffmpeg = ffmpeg;
  this.segmentDuration = config.segmentDuration || 30;
  this.retryCount = config.retryCount || 3;
  this.concurrencyLimit = config.concurrencyLimit || 5;
  this.apiUrl = config.apiUrl;
  this.language = config.language || "en";
  this.taskId = config.taskId;
  this.contentType = config.contentType;
  this.fileExtension = config.fileExtension;
  this.transcribeSlots = config.transcribeSlots || 5;
  this.duration = config.duration;
}

ParallelVideoProcessor.prototype.load = async function () {
  await initializeFFmpeg();
};

ParallelVideoProcessor.prototype.getFileExtensionFromContentType = (contentType, url) => {
  const mimeToExt = {
    "video/mp4": ".mp4",
    "video/webm": ".webm",
    "video/ogg": ".ogv",
    "video/quicktime": ".mov",
    "video/x-msvideo": ".avi",
    "video/x-flv": ".flv",
    "video/x-matroska": ".mkv",
    "video/mpeg": ".mpeg",
    "video/3gpp": ".3gp",
    "video/3gpp2": ".3g2",
    "audio/mpeg": ".mp3",
    "audio/ogg": ".ogg",
    "audio/wav": ".wav",
    "audio/webm": ".weba",
    "audio/aac": ".aac",
    "audio/x-m4a": ".m4a",
    "audio/x-ms-wma": ".wma",
    "audio/x-wav": ".wav",
  };
  const ext = mimeToExt[contentType] || "";
  if (ext) return ext;

  try {
    const urlObj = new URL(url);
    const pathname = urlObj.pathname;
    const match = pathname.match(/\.([a-zA-Z0-9]+)$/);
    return match ? `.${match[1].toLowerCase()}` : ".mp4";
  } catch (e) {
    return ".mp4";
  }
}

ParallelVideoProcessor.prototype.getAudioFormatByMime = function (mimeType) {
  const mimeFormatMap = [
    {
      mime: "audio/mpeg",
      ext: "mp3",
      codec: "copy", // 不转码
    },
    {
      mime: "audio/mp4",
      ext: "m4a",
      codec: "copy",
    },
    {
      mime: "video/mp4",
      ext: "m4a",
      codec: "copy", // 提取音频流，通常是 AAC
    },
    {
      mime: "audio/webm",
      ext: "webm",
      codec: "copy",
    },
    {
      mime: "video/webm",
      ext: "webm",
      codec: "copy", // 提取 Opus 音频流
    },
    {
      mime: "audio/ogg",
      ext: "ogg",
      codec: "copy",
    },
    {
      mime: "audio/wav",
      ext: "wav",
      codec: "copy",
    },
    {
      mime: "video/quicktime",
      ext: "m4a",
      codec: "copy",
    },
    // 兜底：未知类型统一转码为 mp3
    {
      mime: "*",
      ext: "mp3",
      codec: "libmp3lame", // 转码为 MP3
    }
  ];

  const match = mimeFormatMap.find(item => item.mime === mimeType)
      || mimeFormatMap.find(item => item.mime === "*");

  return {
    ext: match.ext,
    codec: match.codec,
  };
}

ParallelVideoProcessor.prototype.transcribeSegment = async function (segment, taskState, startTime) {
  const subtitles = [];
  const { signal } = taskState;

  for (let i = 0; i <= this.retryCount; i++) {
    if (signal?.aborted) throw new Error("Abort");
    try {
      const formData = new FormData();
      formData.append("video", segment.blob, segment.name);
      const blobSlice = segment.blob.slice(0, 100);
      const first100Bytes = await blobSlice.arrayBuffer();
      let signRes = await window?.videoSeekVideoSign(first100Bytes)
      formData.append('videoMd5', signRes.videoMd5)
      formData.append('expire', signRes.expire)
      formData.append('signature',  signRes.signature)
      formData.append('check', '1')
      formData.append("language", this.language);
      formData.append("task", "transcribe");
      DebugHelper.log(segment, '转录参数');

      const response = await fetchWithTimeout(this.apiUrl, {
        method: "POST",
        body: formData
      }, 30000, signal);
      const result = await response.json();
      // DebugHelper.log(result?.data, '转录结果');

      if (result.success) {
        result.data.segments.forEach(({ start, end, text }) => {
          subtitles.push({ start: start + startTime, end: end + startTime, text });
        });
        return subtitles;
      }
      if (i === this.retryCount) throw new Error(result.message || "转录失败");
    } catch (error) {
      if (i === this.retryCount || signal?.aborted) throw error;
    }
  }
  return subtitles;
};

ParallelVideoProcessor.prototype.getFileExtension = function (fileName) {
  return fileName.slice(fileName.lastIndexOf("."));
};

ParallelVideoProcessor.prototype.splitSingleSegment = async function (
    videoFile,
    startTime,
    endTime,
    isVideo,
    taskState
) {
  try {
    // 准备文件名
    const inputFileName = this.taskId + "_input-video" + this.getFileExtension(videoFile.name);
    if (!taskState.genedFiles.find(e => e === inputFileName)) {
      taskState.genedFiles.push(inputFileName)
    }

    const mimeType = videoFile.type;
    const { ext, codec } = this.getAudioFormatByMime(mimeType);
    DebugHelper.log(ext, codec, 'mimeType')
    const segmentName = `${this.taskId}-segment-${Math.floor(startTime).toString().padStart(4, '0')}.${ext}`;
    
    // 检查文件是否已经在虚拟文件系统中
    let fileExists = false;
    try {
      await this.ffmpeg.readFile(inputFileName);
      fileExists = true;
    } catch (e) {
      fileExists = false;
    }
    DebugHelper.info(fileExists, 'fileExists');
    // 如果文件不存在，则写入
    if (!fileExists) {
      const fileData = await videoFile.arrayBuffer();
      DebugHelper.log(`文件大小: ${fileData.byteLength} 字节`, 'inputFileName:', inputFileName);
      if (fileData.byteLength === 0) {
        throw new Error("输入文件数据为空");
      }
      await this.ffmpeg.writeFile(inputFileName, new Uint8Array(fileData));
    }

    // ["-i", inputFileName, "-vn", "-acodec", "copy", audioFileName]
    const args = codec === "copy"
        ? [
          "-ss",
          `${startTime}`,
          "-i",
          inputFileName,
          "-t",
          `${endTime - startTime}`,
          "-vn", // 禁用视频流
          "-acodec",
          "copy", // 使用MP3编码
          segmentName,
        ]
        : [
          "-ss",
          `${startTime}`,
          "-i",
          inputFileName,
          "-t",
          `${endTime - startTime}`,
          "-vn", // 禁用视频流
          "-acodec",
          "libmp3lame", // 使用MP3编码
          "-q:a",
          "2", // 音频质量 (0-9, 0是最高质量)
          segmentName,
        ];
    DebugHelper.info(args, 'args');
    for (let i = 0; i < 3; i++) {
      try {
        await this.ffmpeg.exec(args);
        break;
      } catch (error) {
        DebugHelper.error("执行FFmpeg切割音频命令时出错:", error);
        if (i === 2) {
          throw error;
        }
      }
    }
    

    // 读取输出文件
    const data = await this.ffmpeg.readFile(segmentName);
    taskState.genedFiles.push(segmentName);

    // 创建Blob并添加到片段数组
    const blob = new Blob([data], { type: mimeType });
    const url = URL.createObjectURL(blob);

    // 删除临时文件
    await this.ffmpeg.deleteFile(segmentName);
    taskState.genedFiles = taskState.genedFiles.filter(f => f !== segmentName);

    return {
      name: segmentName,
      startTime,
      endTime,
      blob,
      url,
    };
  } catch (error) {
    DebugHelper.error("分割单个片段失败:", error);
    throw error;
  }
}

ParallelVideoProcessor.prototype.handleExtractSubtitles = async function (audioFile, isVideo, taskState){
  if (!audioFile) return;
  await this.load();
  const { signal } = taskState;

  taskState.progress = 20;

  try {
    DebugHelper.info(audioFile, "audioFile")
    let results = [];

    // 获取音频时长
    const duration = this.duration;
    DebugHelper.log(duration, 'duration');

    // 计算需要处理的时间段
    const timeSegments = [];
    DebugHelper.log(this)
    for (let startTime = 0; startTime < duration; startTime += this.segmentDuration) {
      const endTime = Math.min(startTime + this.segmentDuration, duration);
      timeSegments.push({
        startTime,
        endTime,
        name: `segment_${startTime}_${endTime}`
      });
    }
    DebugHelper.log(timeSegments, 'timeSegments');
    DebugHelper.log(`总共需要处理 ${timeSegments.length} 个时间段`);
    let totalSegments = timeSegments.length;

    DebugHelper.log("开始处理音频片段...");
    const startTime = Date.now();

    // 创建任务队列
    const taskQueue = [...timeSegments];
    // 活跃任务列表
    const activeTasks = [];
    // 最大并发数
    const maxConcurrent = this.concurrencyLimit;
    let handledSegments = 0;

    // 处理单个时间段的函数
    const processTimeSegment = async (timeSegment) => {
      try {
        // 1. 切割单个片段
        DebugHelper.log(`正在切割片段: ${timeSegment.startTime}-${timeSegment.endTime}`);
        const segment = await this.splitSingleSegment(
            audioFile,
            timeSegment.startTime,
            timeSegment.endTime,
            isVideo,
            taskState
        );

        // 2. 上传并处理片段
        const result = await this.transcribeSegment(segment, taskState, timeSegment.startTime);
        handledSegments++;
        // 3. 更新结果
        results = [...results, ...result];
        let newProcess = Math.floor((handledSegments / totalSegments) * 90);
        taskState.progress = newProcess >= 100 ? 99 : newProcess;

        // 4. 处理完成后释放内存
        if (segment.blob) {
          URL.revokeObjectURL(segment.url);
          segment.blob = null;
          segment.url = null;
        }

        return result;
      } catch (error) {
        DebugHelper.error(`处理片段 ${timeSegment.name} 失败:`, error);
        taskState.error =  error.message ? error.message : error.toString();
        try {
          signal.abort()
        } catch (e) {
          DebugHelper.log('abort error:', e)
        }
        throw error;
      } finally {
        // 从活跃任务中移除当前任务
        const index = activeTasks.findIndex(task =>
            task.timeSegment.startTime === timeSegment.startTime &&
            task.timeSegment.endTime === timeSegment.endTime
        );
        if (index !== -1) {
          activeTasks.splice(index, 1);
        }

        // 如果队列中还有任务，添加新任务
        if (taskQueue.length > 0) {
          const nextSegment = taskQueue.shift();
          const promise = processTimeSegment(nextSegment);
          activeTasks.push({ timeSegment: nextSegment, promise });
        }
      }
    };

    // 初始填充活跃任务队列
    for (let i = 0; i < Math.min(maxConcurrent, taskQueue.length); i++) {
      if (signal?.aborted) throw new Error("Abort");
      const timeSegment = taskQueue.shift();
      const promise = processTimeSegment(timeSegment);
      activeTasks.push({ timeSegment, promise });
    }

    // 等待所有任务完成
    while (activeTasks.length > 0) {
      if (taskState.error) throw new Error(taskState.error);
      if (signal?.aborted) throw new Error("Abort");
      // 等待任意一个任务完成
      await Promise.race(activeTasks.map(task => task.promise));
      // 注意：不需要在这里手动移除完成的任务，因为processTimeSegment的finally块会处理
    }

    // 清理计时器
    const endTime = Date.now();
    const totalTime = (endTime - startTime) / 1000;
    DebugHelper.log(`处理完成!耗时 ${totalTime} 秒`);
    DebugHelper.log(results, '转录完成')

    return results.sort((a, b) => a.start - b.start);
  } catch (error) {
    DebugHelper.error("字幕提取失败:", error);
    throw error;
  }
};

ParallelVideoProcessor.prototype.processVideo = async function (fileData, isVideo, taskState) {
  taskState.progress = 0;
  const fileExtension = this.getFileExtensionFromContentType(this.contentType, '');
  let file;
  if (fileData instanceof Uint8Array) {
    const blob = new Blob([fileData], { type: this.contentType });
    file = new File([blob], `${this.taskId}_downloaded${fileExtension}`, { type: this.contentType });
  } else {
    // 如果 fileData 是 number[]（兼容旧代码），转换回 Uint8Array
    const uint8Array = new Uint8Array(fileData);
    const blob = new Blob([uint8Array], { type: this.contentType });
    file = new File([blob], `${this.taskId}_downloaded${fileExtension}`, { type: this.contentType });
  }
  DebugHelper.log(file, 'file')

  return await this.handleExtractSubtitles(file, isVideo, taskState);
};

// 任务队列和状态管理
const taskQueue = [];
const taskStates = {};
const maxConcurrentTasks = 3; // 最大并发任务数
const activeTasks = new Set(); // 跟踪活跃任务

async function processTasksConcurrent() {
  if (activeTasks.size >= maxConcurrentTasks) return; // 超过并发限制或正在处理则返回

  try {
    while (taskQueue.length > 0 && activeTasks.size < maxConcurrentTasks) {
      const taskId = taskQueue.shift();
      if (!taskId) continue;

      activeTasks.add(taskId);
      DebugHelper.log(`开始处理任务: ${taskId}`)
      await processTask(taskId);
    }
  } catch (error) {
    DebugHelper.error("任务队列处理失败:", error);
  }
}

async function processTask(taskId) {
  const taskState = taskStates[taskId];
  if (!taskState) {
    try {
      activeTasks.delete(taskId); // 任务完成或失败后移除
    } catch (e) {
      DebugHelper.log('activeTasks删除任务报错:', e)
    }
    return
  }

  try {
    const finalBuffer = new Uint8Array(taskState.bufferInfo.totalSize);
    let verifiedSize = 0;

    const sortedChunks = Array.from(taskState.bufferInfo.chunks.entries())
        .sort((a, b) => a[1].start - b[1].start);

    for (const [_, chunk] of sortedChunks) {
      finalBuffer.set(chunk.data, chunk.start);
      verifiedSize += chunk.data.length;
    }

    if (verifiedSize !== taskState.bufferInfo.totalSize) {
      throw new Error(`数据不完整: 预期 ${taskState.bufferInfo.totalSize} 字节, 实际 ${verifiedSize} 字节`);
    }

    DebugHelper.log(taskState.taskInfo, 'taskInfo')
    const processor = new ParallelVideoProcessor({
      ...taskState.taskInfo.config,
      taskId,
      contentType: taskState.taskInfo.contentType,
      fileExtension: taskState.taskInfo.fileExtension,
      duration: taskState.taskInfo.duration,
    });

    taskState.subtitles = await processor.processVideo(
        Array.from(finalBuffer),
        taskState.taskInfo.contentType.includes("video"),
        taskState
    );

    taskState.progress = 100;
    taskState.lastUpdated = Date.now();
  } catch (error) {
    DebugHelper.error("任务处理失败:", error);
    taskState.error = error.message ?  error.message : error.toString();
    taskState.lastUpdated = Date.now();
    throw error;
  } finally {
    try {
      activeTasks.delete(taskId); // 任务完成或失败后移除
    } catch (e) {
      DebugHelper.log('activeTasks删除任务报错:', e)
    }
  }
}

// 定时清理未更新任务
function startResourceCleanup() {
  setInterval(async () => {
    const now = Date.now();
    const cleanupPromises = [];

    for (const taskId in taskStates) {
      const taskState = taskStates[taskId];
      if (now - taskState.lastUpdated > 60000) {
        DebugHelper.log(`检测到任务 ${taskId} 超过 1 分钟未更新，开始清理`);
        cleanupPromises.push(cleanupTaskResources(taskId));
      }
    }

    await Promise.all(cleanupPromises);
  }, 30000);
}

async function cleanupTaskResources(taskId) {
  try {
    const taskState = taskStates[taskId];
    if (!taskState) return;
    try {
      taskState.controller?.abort();
    } catch (error) {
      DebugHelper.warn("任务取消abort报错:", error);
    }

    if (taskState.genedFiles?.length > 0) {
      let processor = new ParallelVideoProcessor({ taskId });
      await processor.load();
      for (const fileName of taskState.genedFiles) {
        try {
          await processor.ffmpeg.deleteFile(fileName);
          DebugHelper.log(`已删除 FFmpeg 文件: ${fileName}`);
        } catch (error) {
          DebugHelper.warn(`删除 FFmpeg 文件 ${fileName} 失败:`, error);
        }
      }
      processor = null;
      taskState.genedFiles = [];
    }

    delete taskStates[taskId];
    DebugHelper.log(`已清理任务 ${taskId} 的所有资源`);
  } catch (error) {
    DebugHelper.error(`清理任务 ${taskId} 资源时出错:`, error);
  }
}

// 消息监听器
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
  DebugHelper.log("收到消息:", request);
  if (request.type === "process_video_task") {
    const taskId = request.data.taskId;

    if (!taskStates[taskId] && request.data.totalSize) {
      let taskInfo = {
        taskId: taskId,
        isVideo: request.data.isVideo,
        fileExtension: request.data.fileExtension,
        contentType: request.data.contentType,
        config: request.data.config,
        duration: request.data.duration,
      };
      taskStates[taskId] = {
        progress: 0,
        controller: new AbortController(),
        signal: null,
        error: null,
        subtitles: [],
        taskInfo: taskInfo,
        genedFiles: [],
        lastUpdated: Date.now(),
        bufferInfo: {
          totalSize: request.data.totalSize,
          receivedSize: 0,
          chunks: new Map(),
        }
      };
      taskStates[taskId].signal = taskStates[taskId].controller.signal;

      sendResponse({ success: true, taskId });
      return true;
    }

    if (taskStates[taskId] && request.data.rangeIndex !== undefined) {
      if (taskStates[taskId].signal?.aborted) {
        sendResponse({ success: false, taskId, error: "Abort" });
        return true;
      }

      const bufferInfo = taskStates[taskId].bufferInfo;
      const chunkData = new Uint8Array(request.data.fileData);

      bufferInfo.chunks.set(request.data.rangeIndex, {
        data: chunkData,
        start: request.data.rangeStart
      });
      bufferInfo.receivedSize += chunkData.length;
      taskStates[taskId].lastUpdated = Date.now();

      if (bufferInfo.receivedSize >= bufferInfo.totalSize) {
        taskQueue.push(taskId);
      }

      sendResponse({ success: true, taskId });
      return true;
    }
  } else if (request.type === "get_video_task_progress") {
    const taskId = request.data.taskId;
    const taskState = taskStates[taskId];
    if (taskState) {
      DebugHelper.log(taskState, 'get_video_task_progress');
      taskState.lastUpdated = Date.now();
      if (taskState.error) {
        sendResponse({ success: false, taskId, error: taskState.error });
        await cleanupTaskResources(taskId);
      } else {
        sendResponse({ success: true, taskId, progress: taskState.progress, subtitles: taskState.subtitles });
        processTasksConcurrent();
      }

      //完成就直接清理了
      if (taskState.progress === 100) {
        cleanupTaskResources(taskId);
      }
    } else {
      sendResponse({ success: false, taskId, error: "任务不存在" });
    }
    return true;
  } else if (request.type === "cancel_video_task") {
    const taskId = request.data.taskId;
    const taskState = taskStates[taskId];
    if (taskState) {
      try {
        taskState.controller.abort();
      } catch (error) {
        DebugHelper.warn("任务取消abort报错:", error);
      }
      setTimeout(async () => {
        await cleanupTaskResources(taskId);
      }, 2000)
      sendResponse({ success: true, taskId, message: "任务已取消" });
    } else {
      sendResponse({ success: false, taskId, error: "任务不存在" });
    }
    return true;
  }
  return false;
});

startResourceCleanup();