import { BaseElement } from './BaseElement.js';
import { DEFAULT_IMAGE_CONFIG } from '../types/constants.js';
import { deepMerge } from '../utils/helpers.js';
import { ElementType } from '../types/enums.js';
import { createCanvas } from '@napi-rs/canvas';
import execa from 'execa';
import { getVideoDuration, readFileStreams, calculateVideoScale, getInputCodec, buildVideoFFmpegArgs, rawVideoToFrames, createAudioStream } from '../utils/video-utils.js';
import paper from '@chnak/paper';
import { calculateImageFit } from '../utils/image-fit.js';
import path from 'path';
import os from 'os';

/**
 * 视频元素
 */
class VideoElement extends BaseElement {
  constructor(config = {}) {
    super(config);
    this.type = ElementType.VIDEO;
    // 重新合并配置，确保传入的config优先级最高
    this.config = deepMerge({}, DEFAULT_IMAGE_CONFIG, config);
    
    this.videoPath = config.src  || config.videoPath || null;
    this.fit = config.fit || 'cover'; // cover, contain, fill, none
    this.cutFrom = config.cutFrom||0; // 开始时间（秒）
    this.cutTo = config.cutTo||this.config.duration; // 结束时间（秒）
    this.speedFactor = config.speedFactor || config.speed || 1; // 播放速度倍数
    this.loop = config.loop || false; // 是否循环播放
    this.mute = config.mute !== undefined ? config.mute : true; // 默认静音
    this.volume = config.volume !== undefined ? config.volume : 1.0; // 音量
    
    // 视频处理相关
    this.videoProcessor = null;
    this.frameIterator = null;
    this.frameBuffer = new Map(); // Map<frameIndex, Buffer> 有界缓冲，防止 OOM
    this.maxFrameBufferSize = config.maxFrameBufferSize || 600; // 默认600帧（20秒），1080p约4.8GB
    this.totalBufferedFrames = 0; // 已提取的总帧数（含已被淘汰的）
    this.currentFrameIndex = 0;
    this.videoInfo = null;
    this.initialized = false;
    
    // 帧缓存（用于多进程渲染优化）
    this.frameImageCache = new Map(); // Map<progress, Image>
    this.frameImageCacheSize = 10; // 最多缓存10帧
    this.preloadNextFrame = null; // 预加载的下一帧 Promise
    
    // 与 ImageElement 一致的属性
    this.imageData = null; // 当前帧的 Image/Canvas 对象
    this.loaded = false; // 是否已加载（对于视频，表示是否已初始化）
    
    // 音频相关
    this.audioStream = null;
    this.audioPath = null; // 提取的音频文件路径
  }

  async ready() {
    await super.ready();

    if (this.videoPath) {
      try {
        const { effectiveDuration } = await getVideoDuration(this.videoPath, {
          cutFrom: this.config.cutFrom,
          cutTo: this.config.cutTo,
        });
        if (effectiveDuration > 0 && this.config.duration > effectiveDuration) {
          this.config.loop = true;
        }
      } catch (err) {
        console.warn(`[VideoElement] getVideoDuration failed: ${err.message}`);
      }
    }

    return true;
  }

  /**
   * 设置视频的起始播放时间（用于分块渲染时 seek 到指定位置）
   * @param {number} seekTime - 起始时间（秒）
   */
  setSeekTime(seekTime) {
    this.cutFrom = seekTime;
    // 清除 cutTo，因为它相对于视频开头，重新设置 cutFrom 后需要重新计算
    this.cutTo = undefined;
    // 清除 actualCutTo 和 actualCutFrom，确保下次 ready() 时重新计算
    this.actualCutTo = undefined;
    this.actualCutFrom = undefined;
  }
  /**
   * 设置视频适配方式（与 ImageElement 一致）
   */
  setFit(fit) {
    this.config.fit = fit; // cover, contain, fill, none
    this.fit = fit;
  }

  /**
   * 初始化方法 - 使用 FFmpeg 提取视频帧
   */
  async initialize() {
    await super.initialize();
    if (this.initialized || !this.videoPath) {
      return;
    }
    
    // 防止并发初始化（使用 Promise 缓存）
    if (this._initializingPromise) {
      console.log(`[VideoElement] 等待正在进行的初始化... (${this.videoPath})`);
      await this._initializingPromise;
      return;
    }
    
    // 检测是否在 Worker 线程中
    const isWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
    const threadInfo = isWorker ? '[Worker]' : '[Main]';
    console.log(`${threadInfo} [VideoElement] 开始初始化: ${this.videoPath}`);
    
    // 创建初始化 Promise
    this._initializingPromise = (async () => {
      try {
        // 获取视频流信息
      const streams = await readFileStreams(this.videoPath);
      const videoStream = streams.find(s => s.codec_type === 'video');
      
      if (!videoStream) {
        throw new Error(`无法找到视频流: ${this.videoPath}`);
      }

      // 解析帧率
      // 优先使用 avg_frame_rate（平均帧率，更准确），如果没有则使用 r_frame_rate（编码帧率）
      let fps = 30;
      if (videoStream.avg_frame_rate) {
        const [num, den] = videoStream.avg_frame_rate.split('/').map(Number);
        if (den && den > 0) {
          fps = num / den;
        }
      } else if (videoStream.r_frame_rate) {
        const [num, den] = videoStream.r_frame_rate.split('/').map(Number);
        if (den && den > 0) {
          fps = num / den;
        }
      }

      this.videoInfo = {
        width: videoStream.width,
        height: videoStream.height,
        duration: parseFloat(videoStream.duration) || 0,
        fps: fps,
      };
      

      // 计算目标尺寸
      const viewSize = paper.view && paper.view.viewSize ? paper.view.viewSize : { width: 1920, height: 1080 };
      const context = { 
        width: this.canvasWidth || viewSize.width, 
        height: this.canvasHeight || viewSize.height 
      };

      // 使用 BaseElement 的通用方法转换尺寸
      let { width: targetWidth, height: targetHeight } = this.convertSize(
        this.config.width,
        this.config.height,
        context
      );

      if (!targetWidth) targetWidth = context.width;
      if (!targetHeight) targetHeight = context.height;

      // 计算缩放参数
      const scaleParams = calculateVideoScale({
        inputWidth: this.videoInfo.width,
        inputHeight: this.videoInfo.height,
        requestedWidth: targetWidth,
        requestedHeight: targetHeight,
        resizeMode: this.fit
      });

      const { targetWidth: finalWidth, targetHeight: finalHeight, scaleFilter } = scaleParams;
      const roundedWidth = Math.round(finalWidth);
      const roundedHeight = Math.round(finalHeight);
      this.finalWidth = roundedWidth;
      this.finalHeight = roundedHeight;

      // 获取输入编解码器
      const inputCodec = getInputCodec(videoStream.codec_name);

      // 计算实际需要提取的视频时长
      // 如果元素有 duration，只提取 duration 对应的帧数
      // 否则使用 cutTo - cutFrom（如果指定了）
      let actualCutFrom = this.cutFrom || 0;
      let actualCutTo = this.cutTo;
      
      if (this.duration && this.duration > 0) {
        // 元素有 duration，只提取 duration 对应的时长
        actualCutTo = actualCutFrom + this.duration;
        
        // 如果原始 cutTo 存在且更短，使用原始 cutTo
        if (this.cutTo !== undefined && this.cutTo < actualCutTo) {
          actualCutTo = this.cutTo;
        }
        
        // 确保不超过视频总时长
        if (this.videoInfo.duration > 0 && actualCutTo > this.videoInfo.duration) {
          actualCutTo = this.videoInfo.duration;
        }
      }
      
      // 保存实际使用的 cutFrom 和 cutTo（用于音频提取等）
      this.actualCutFrom = actualCutFrom;
      this.actualCutTo = actualCutTo;

      // 使用视频的实际 FPS，而不是固定的 30
      // 这样可以确保提取的帧数正确
      const targetFps = this.fps || this.videoInfo.fps || 30;
      
      // 计算提取参数
      const extractDuration = actualCutTo - actualCutFrom;
      const expectedFrames = Math.ceil(extractDuration * targetFps);
      this.expectedFrames = expectedFrames;
      
      // 创建初始化 Promise
      const initializingPromise = (async () => {
        try {
          // 构建 FFmpeg 参数
          const args = buildVideoFFmpegArgs({
          inputPath: this.videoPath,
          inputCodec,
          cutFrom: actualCutFrom,
          cutTo: actualCutTo,
          speedFactor: this.speedFactor,
          framerate: targetFps,
          scaleFilter,
          mute: this.mute,
          volume: 1
        });

        // 创建转换流
        const controller = new AbortController();
        const transform = rawVideoToFrames({
          width: roundedWidth,
          height: roundedHeight,
          channels: 4, // RGBA
          signal: controller.signal
        });

        // 检测是否在 Worker 线程中
        // Worker 线程中的 process.stderr 是 WritableWorkerStdio，不能直接传递给 execa
        // 使用多种方法检测 Worker 线程
        let isWorkerThread = false;
        try {
          // 方法1: 检查 WorkerGlobalScope
          if (typeof WorkerGlobalScope !== 'undefined' && typeof self !== 'undefined' && self instanceof WorkerGlobalScope) {
            isWorkerThread = true;
          }
          // 方法2: 检查 process.stderr 的类型
          else if (process.stderr && typeof process.stderr === 'object') {
            // Worker 线程中的 process.stderr 是 WritableWorkerStdio
            const stderrType = process.stderr.constructor?.name || '';
            if (stderrType === 'WritableWorkerStdio' || (process.stderr._writableState && !process.stderr.isTTY)) {
              isWorkerThread = true;
            }
          }
        } catch (e) {
          // 如果检测失败，默认不在 Worker 线程中
          isWorkerThread = false;
        }
        
        const stderrOption = isWorkerThread ? 'pipe' : (process.stderr || 'pipe');
        
        // 启动 FFmpeg 进程
        const ps = execa('ffmpeg', args, {
          encoding: 'buffer',
          buffer: false,
          stdin: 'ignore',
          stdout: 'pipe', // 使用 pipe，然后手动连接
          stderr: stderrOption, // Worker 线程中使用 'pipe'，主线程使用 process.stderr
          cancelSignal: controller.signal
        });

        // 处理流的错误
        // 注意：在缓冲帧的过程中，transform 流可能会因为 FFmpeg 进程结束而触发错误
        // 这是正常的，只要已经缓冲了帧就可以继续
        let transformError = null;
        transform.on('error', (err) => {
          // 记录错误，但不立即中止（因为可能已经缓冲了足够的帧）
          transformError = err;
          console.warn('[VideoElement] Transform stream error (可能不影响缓冲):', err.message);
          // 只有在进程未结束时才中止
          if (!ps.killed && ps.exitCode === null) {
            controller.abort();
          }
        });

        // 在 Worker 线程中，stderr 使用 pipe，需要手动处理
        if (isWorkerThread && ps.stderr) {
          ps.stderr.on('data', (data) => {
            // Worker 线程中可以选择忽略 stderr 或记录到日志
            // 这里选择忽略，避免输出过多信息
          });
          ps.stderr.on('error', (err) => {
            // 忽略 stderr 错误
          });
        }

        // 等待 stdout 可用（CommonJS 环境中可能需要等待）
        if (!ps.stdout) {
          let waited = 0;
          const maxWait = 200; // 增加到 200ms，给更多时间
          while (!ps.stdout && waited < maxWait) {
            await new Promise(resolve => setTimeout(resolve, 10));
            waited += 10;
            // 重新检查 ps.stdout（可能在等待过程中变为可用）
            if (ps.stdout) break;
          }
        }

        // 检查 stdout 是否可用
        if (!ps.stdout) {
          // 如果 stdout 不可用，尝试等待进程启动
          // 在某些情况下，execa 可能需要更多时间
          console.warn('[VideoElement] FFmpeg stdout 初始不可用，等待进程启动...');
          await new Promise(resolve => setTimeout(resolve, 50));
          
          // 再次检查
          if (!ps.stdout) {
            console.error('[VideoElement] FFmpeg stdout is not available after waiting');
            throw new Error('FFmpeg stdout is not available');
          }
        }

        // 保存 stdout 引用，避免后续访问时它可能被关闭
        const stdout = ps.stdout;
        
        stdout.on('error', (err) => {
          // stdout 错误通常表示 FFmpeg 进程已经结束或出现问题
          // 但如果已经缓冲了帧，这可能不是致命错误
          console.warn('[VideoElement] FFmpeg stdout error (可能不影响缓冲):', err.message);
          // 不立即 destroy transform，让缓冲过程自然结束
          // transform.destroy(err); // 注释掉，避免触发 transform 错误事件
        });

        // 将 FFmpeg 的 stdout 管道连接到 transform
        // 设置 highWaterMark 以避免缓冲区溢出
        stdout.pipe(transform, { end: false });

        // 处理进程错误
        ps.catch((err) => {
          // FFmpeg 进程错误
          // 如果进程被取消（exitCode 143）或正常结束，这是正常的
          if (!err.isCanceled && err.exitCode !== 143) {
            console.warn('[VideoElement] FFmpeg process error (可能不影响缓冲):', err.message);
          }
          // 不立即 destroy，让缓冲过程自然结束
          // transform.destroy(); // 注释掉，避免在缓冲过程中中断
        });

        // 当进程正常结束时，结束 transform 流
        ps.then(() => {
          // 进程正常结束，结束 transform 流
          transform.end();
        }).catch(() => {
          // 进程异常结束，但不立即 destroy（可能正在缓冲）
        });

        // 转换为迭代器 - 使用 transform 流的迭代器
        this.frameIterator = transform[Symbol.asyncIterator]();
        this.controller = controller;
        this.ps = ps;
        this.finalWidth = roundedWidth;
        this.finalHeight = roundedHeight;

        // 缓冲所有帧（用于随机访问）
        let frameCount = 0;
        let bufferError = null;
        const frameSize = roundedWidth * roundedHeight * 4; // RGBA
        
        try {
          while (true) {
            try {
              const { value, done } = await this.frameIterator.next();
              if (done) {
                break;
              }
              if (value) {
                this.frameBuffer.set(frameCount, Buffer.from(value));
                frameCount++;
                // FIFO 淘汰：超过上限时移除最旧帧
                while (this.frameBuffer.size > this.maxFrameBufferSize) {
                  const oldestKey = Math.min(...this.frameBuffer.keys());
                  this.frameBuffer.delete(oldestKey);
                }
              }
            } catch (iterError) {
              if (frameCount === 0) {
                bufferError = iterError;
              }
              break;
            }
          }
        } catch (error) {
          if (frameCount === 0) {
            bufferError = error;
          }
        }
        
        // 只有在没有缓冲到任何帧时才抛出错误
        if (frameCount === 0) {
          const errorMsg = bufferError 
            ? `无法缓冲视频帧: ${bufferError.message}` 
            : (transformError 
              ? `无法缓冲视频帧: Transform stream error - ${transformError.message}` 
              : '无法缓冲视频帧: 未知错误');
          throw new Error(errorMsg);
        }
        
        // 保存总帧数（用于进度计算），即使部分帧已被淘汰
        this.totalBufferedFrames = frameCount;

        // 如果不禁音，提取视频中的音频
        if (!this.mute) {
          try {
            const outputDir = path.join(os.tmpdir(), 'fknew-video-audio');
            await import('fs-extra').then(fs => fs.ensureDir(outputDir));
            
            this.audioStream = await createAudioStream({
              source: this.videoPath,
              cutFrom: this.actualCutFrom,
              cutTo: this.actualCutTo,
              speedFactor: this.speedFactor,
              volume: this.volume,
              outputDir: outputDir
            });
            
            if (this.audioStream && this.audioStream.path) {
              this.audioPath = this.audioStream.path;
              console.log(`[VideoElement] 视频音频已提取: ${this.audioPath}`);
            } else {
              console.log(`[VideoElement] 视频 ${this.videoPath} 没有音频轨道或音频提取失败`);
            }
          } catch (error) {
            console.warn(`[VideoElement] 提取视频音频失败: ${error.message}`);
          }
        }

        this.initialized = true;
        
        const isWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
        const threadInfo = isWorker ? '[Worker]' : '[Main]';
        console.log(`${threadInfo} [VideoElement] 初始化完成: ${this.videoPath}, 缓冲了 ${this.frameBuffer.size} 帧 (最大 ${this.maxFrameBufferSize})`);
        
        // 调用 onLoaded 回调（注意：此时还没有 paperItem，所以传递 null）
        this._callOnLoaded(this.startTime || 0, null, null);
      } catch (error) {
        const isWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
        const threadInfo = isWorker ? '[Worker]' : '[Main]';
        console.error(`${threadInfo} [VideoElement] 初始化失败: ${this.videoPath}`, error);
        this.initialized = false;
        throw error;
      }
    })();
    
    await initializingPromise;
      } catch (error) {
        const isWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
        const threadInfo = isWorker ? '[Worker]' : '[Main]';
        console.error(`${threadInfo} [VideoElement] 外层初始化失败: ${this.videoPath}`, error);
        this.initialized = false;
        throw error;
      } finally {
        // 清除初始化 Promise，允许后续重新初始化
        this._initializingPromise = null;
      }
    })();
    
    await this._initializingPromise;
  }

  /**
   * 获取当前帧的 Canvas 对象
   * @param {number} progress - 进度 (0-1)，基于元素的 duration
   * @returns {Promise<Canvas|null>} 当前帧的 Canvas 对象
   */
  async getFrameAtProgress(progress) {
    if (!this.initialized) {
      await this.initialize();
    }

    if (!this.initialized) {
      return null;
    }

    try {
      // 检查缓存
      const cacheKey = Math.floor(progress * 1000) / 1000; // 保留3位小数作为缓存键
      if (this.frameImageCache.has(cacheKey)) {
        return this.frameImageCache.get(cacheKey);
      }

      let rgba = null;
      let frameIndex = 0;

      if (this.frameBuffer.size > 0 && this.videoInfo) {
        // frameBuffer 现在只包含元素 duration 对应的帧
        const elementDuration = this.duration || 0;
        const videoFps = this.videoInfo.fps || 30;

        // 计算帧索引
        if (elementDuration > 0) {
          const videoTime = progress * elementDuration;
          frameIndex = Math.floor(videoTime * videoFps);
        } else {
          frameIndex = Math.floor(progress * this.totalBufferedFrames);
        }

        // 处理循环播放
        if (this.loop && this.totalBufferedFrames > 0) {
          // 循环模式：使用取模运算，允许超出范围的索引循环回开头
          frameIndex = ((frameIndex % this.totalBufferedFrames) + this.totalBufferedFrames) % this.totalBufferedFrames;
        } else {
          // 非循环模式：限制帧索引在有效范围内
          frameIndex = Math.max(0, Math.min(frameIndex, this.totalBufferedFrames - 1));
        }

        // 获取帧（从 Map 中），如果无效则尝试使用相邻帧
        rgba = this.frameBuffer.get(frameIndex);

        // 如果当前帧无效，尝试使用相邻帧
        if (!rgba || !Buffer.isBuffer(rgba) || rgba.length === 0) {
          // 尝试使用前一帧
          if (frameIndex > 0) {
            rgba = this.frameBuffer.get(frameIndex - 1);
          }
          // 如果前一帧也无效，尝试使用后一帧
          if ((!rgba || !Buffer.isBuffer(rgba) || rgba.length === 0) && frameIndex < this.totalBufferedFrames - 1) {
            rgba = this.frameBuffer.get(frameIndex + 1);
          }
          // 如果都无效，使用第一帧
          if ((!rgba || !Buffer.isBuffer(rgba) || rgba.length === 0) && this.frameBuffer.size > 0) {
            const firstKey = Math.min(...this.frameBuffer.keys());
            rgba = this.frameBuffer.get(firstKey);
          }
        }
      } else if (this.frameIterator) {
        // 如果没有缓冲，尝试从迭代器获取
        try {
          const { value, done } = await this.frameIterator.next();
          if (!done && value) {
            rgba = Buffer.from(value);
          }
        } catch (err) {
          // 忽略迭代器错误
        }
      }

      // 如果仍然无法获取有效帧，返回 null（避免黑屏，render 方法会跳过渲染）
      if (!rgba || !Buffer.isBuffer(rgba) || rgba.length === 0) {
        return null;
      }

      // 从 RGBA Buffer 创建 Canvas 对象（与 ImageElement 一致，直接使用 Canvas）
      // 添加重试逻辑，Skia surface 失败时清除缓存重试
      let frameCanvas = null;
      let retryCount = 0;
      const maxRetries = 2;

      while (retryCount < maxRetries) {
        try {
          frameCanvas = createCanvas(this.finalWidth, this.finalHeight);
          const ctx = frameCanvas.getContext('2d');
          const imageData = ctx.createImageData(this.finalWidth, this.finalHeight);
          // Node.js Buffer 转换为 Uint8Array 以兼容 Uint8ClampedArray.set()
          const uint8Data = new Uint8Array(rgba.buffer, rgba.byteOffset, rgba.byteLength);
          imageData.data.set(uint8Data);
          ctx.putImageData(imageData, 0, 0);
          break; // 成功则退出循环
        } catch (canvasError) {
          retryCount++;
          console.warn(`[VideoElement] Skia surface 创建失败 (尝试 ${retryCount}/${maxRetries}): ${canvasError.message}`);
          // 清除所有缓存后重试
          this.frameImageCache.clear();
          if (retryCount >= maxRetries) {
            throw canvasError;
          }
        }
      }

      // 缓存 Canvas 对象
      if (this.frameImageCache.size >= this.frameImageCacheSize) {
        // 删除最旧的缓存（FIFO）
        const firstKey = this.frameImageCache.keys().next().value;
        this.frameImageCache.delete(firstKey);
      }
      this.frameImageCache.set(cacheKey, frameCanvas);

      return frameCanvas;
    } catch (error) {
      console.error('Failed to get video frame:', error);
      return null;
    }
  }
  
  /**
   * 清理帧缓存
   */
  clearFrameCache() {
    this.frameImageCache.clear();
    this.preloadNextFrame = null;
  }

  /**
   * 应用视觉效果（滤镜、边框、阴影等）
   * @param {paper.Raster} raster - Paper.js Raster 对象
   * @param {Object} state - 元素状态
   * @param {number} width - 元素宽度
   * @param {number} height - 元素高度
   * @param {Object} paperInstance - Paper.js 实例 { project, paper }
   * @returns {paper.Group|paper.Raster} 应用效果后的对象
   */
  applyVisualEffects(raster, state, width, height, paperInstance = null) {
    // 获取 Paper.js 实例
    const { paper: p } = this.getPaperInstance(paperInstance);
    // 检查是否有视觉效果（与 ImageElement 一致）
    const hasBorder = state.borderWidth > 0;
    const hasShadow = state.shadowBlur > 0;
    const hasFlip = state.flipX || state.flipY;
    const hasBlendMode = state.blendMode && state.blendMode !== 'normal';
    const hasFilter = state.filter || 
      (state.brightness !== 1 || state.contrast !== 1 || state.saturation !== 1 || 
       state.hue !== 0 || state.grayscale > 0);
    const hasGlassEffect = state.glassEffect;

    if (!hasBorder && !hasShadow && !hasFlip && !hasBlendMode && !hasFilter && !hasGlassEffect) {
      return raster;
    }

    // 创建组来包含所有效果
    const group = new p.Group();
    
    // 应用翻转
    if (hasFlip) {
      if (state.flipX) {
        raster.scale(-1, 1, raster.position);
      }
      if (state.flipY) {
        raster.scale(1, -1, raster.position);
      }
    }

    // 应用混合模式
    if (hasBlendMode) {
      raster.blendMode = state.blendMode;
    }

    // 应用阴影（通过创建阴影层）
    if (hasShadow) {
      const shadowRaster = raster.clone();
      shadowRaster.position = new p.Point(
        raster.position.x + (state.shadowOffsetX || 0),
        raster.position.y + (state.shadowOffsetY || 0)
      );
      shadowRaster.opacity = 0.3; // 阴影透明度
      
      // 应用阴影颜色（通过 tint）
      if (state.shadowColor) {
        const shadowColor = new p.Color(state.shadowColor);
        shadowRaster.tint = shadowColor;
      }
      
      // 应用模糊（通过降低分辨率模拟）
      if (state.shadowBlur > 0) {
        const blurFactor = Math.max(1, state.shadowBlur / 10);
        shadowRaster.size = new p.Size(
          shadowRaster.size.width * (1 + blurFactor * 0.1),
          shadowRaster.size.height * (1 + blurFactor * 0.1)
        );
      }
      
      group.addChild(shadowRaster);
    }

    // 添加主视频帧
    group.addChild(raster);

    // 应用边框（通过绘制边框路径）
    if (hasBorder) {
      const borderPath = new p.Path.Rectangle({
        rectangle: new p.Rectangle(
          raster.position.x - width / 2,
          raster.position.y - height / 2,
          width,
          height
        ),
        radius: state.borderRadius || 0,
      });
      borderPath.strokeColor = new p.Color(state.borderColor || '#000000');
      borderPath.strokeWidth = state.borderWidth;
      borderPath.fillColor = null;
      group.addChild(borderPath);
    }

    // 毛玻璃效果：添加边框（如果启用）
    if (hasGlassEffect && state.glassBorder) {
      const glassBorderPath = new p.Path.Rectangle({
        rectangle: new p.Rectangle(
          raster.position.x - width / 2,
          raster.position.y - height / 2,
          width,
          height
        ),
        radius: state.borderRadius || 0,
      });
      glassBorderPath.strokeColor = new p.Color(state.glassBorderColor || '#ffffff');
      glassBorderPath.strokeWidth = state.glassBorderWidth || 1;
      glassBorderPath.fillColor = null;
      glassBorderPath.opacity = 0.5; // 半透明边框
      group.addChild(glassBorderPath);
    }

    return group.children.length > 1 ? group : raster;
  }

  async render(layer, time, paperInstance = null) {
    if (!this.visible) return null;
    if (!this.isActiveAtTime(time)) return null;
    
    // 确保视频已初始化（与 ImageElement 的 loaded 检查一致）
    if (!this.initialized) {
      try {
        await Promise.race([
          this.initialize(),
          new Promise((_, reject) => setTimeout(() => reject(new Error('Video initialization timeout (5s)')), 5000))
        ]);
      } catch (error) {
        console.error(`[VideoElement] 初始化失败 (id: ${this.id}):`, error.message);
        return null;
      }
    }
    
    if (!this.initialized) {
      console.warn(`[VideoElement] 视频未初始化 (id: ${this.id})`);
      return null;
    }
    
    const { paper: p, project } = this.getPaperInstance(paperInstance);
    const viewSize = project?.view?.viewSize || { width: 1920, height: 1080 };
    const context = { width: viewSize.width, height: viewSize.height };
    const state = this.getStateAtTime(time, context);
    
    // 计算进度
    const progress = this.getProgressAtTime(time);
    
    // 获取当前帧并赋值给 this.imageData（与 ImageElement 一致）
    let imageData = null;
    try {
      imageData = await this.getFrameAtProgress(progress);
    } catch (frameError) {
      console.warn(`[VideoElement] 获取视频帧异常 (id: ${this.id}): ${frameError.message}`);
      return null;
    }
    if (!imageData) {
      console.warn(`[VideoElement] 无法获取视频帧 (id: ${this.id})`);
      return null;
    }
    
    // 计算容器尺寸（元素的目标尺寸，与 ImageElement 一致）
    const containerSize = this.convertSize(state.width, state.height, context);
    const containerWidth = containerSize.width || viewSize.width;
    const containerHeight = containerSize.height || viewSize.height;
    
    // 获取视频帧原始尺寸
    const imageWidth = imageData.width || this.finalWidth || containerWidth;
    const imageHeight = imageData.height || this.finalHeight || containerHeight;
    
    // 根据 fit 参数计算实际显示尺寸（与 ImageElement 一致）
    const fit = state.fit || this.config.fit || this.fit || 'cover';
    const fitResult = calculateImageFit({
      imageWidth,
      imageHeight,
      containerWidth,
      containerHeight,
      fit
    });
    
    // 使用适配后的尺寸
    const width = fitResult.width;
    const height = fitResult.height;
    const { x, y } = this.calculatePosition(state, context, { width, height });
    
    // 直接使用 Image/Canvas 对象创建 Raster（与 ImageElement 完全一致）
    const raster = new p.Raster(imageData);
    raster.position = new p.Point(x, y);
    
    // 使用 scale 来设置尺寸，而不是直接设置 size（与 ImageElement 一致）
    const sourceWidth = imageData.width || width;
    const sourceHeight = imageData.height || height;
    
    if (sourceWidth > 0 && sourceHeight > 0) {
      const scaleX = width / sourceWidth;
      const scaleY = height / sourceHeight;
      raster.scale(scaleX, scaleY, raster.position);
    } else {
      // 如果无法获取原始尺寸，直接设置 size
      raster.size = new p.Size(width, height);
    }
    
    // 应用变换
    this.applyTransform(raster, state, {
      applyPosition: false,
      paperInstance: p
    });
    
    // 应用视觉效果
    const finalItem = this.applyVisualEffects(raster, state, width, height, p);
    
    // 添加到图层
    if (layer) {
      layer.addChild(finalItem);
    }
    
    return finalItem;
  }

  /**
   * 获取音频元素（如果视频不禁音）
   * @returns {Promise<Array>} 音频元素数组
   */
  async getAudioElements() {
    if (this.mute || !this.audioPath) {
      return [];
    }
    
    // 如果还没有初始化，先初始化
    if (!this.initialized) {
      await this.initialize();
    }
    
    // 如果初始化后仍然没有音频路径，说明视频没有音频或提取失败
    if (!this.audioPath) {
      return [];
    }
    
    // 创建一个临时的音频元素来代表视频的音频
    const { AudioElement } = await import('./AudioElement.js');
    const audioElement = new AudioElement({
      type: 'audio',
      src: this.audioPath,
      startTime: this.startTime || 0,
      duration: this.duration || 0,
      volume: this.volume,
      canvasWidth: this.canvasWidth,
      canvasHeight: this.canvasHeight
    });
    
    return [audioElement];
  }

  /**
   * 获取音频配置（用于音频合并）
   * @returns {Object|null} 音频配置对象
   */
  getAudioConfig() {
    if (this.mute || !this.audioPath) {
      return null;
    }
    
    return {
      path: this.audioPath,
      startTime: this.startTime || 0,
      duration: this.duration || 0,
      volume: this.volume,
      fadeIn: 0,
      fadeOut: 0,
      loop: false,
    };
  }

  /**
   * 销毁元素，释放资源
   */
  destroy() {
    // 先关闭视频，kill FFmpeg 进程
    this.close();
    // 清理帧缓存
    this.clearFrameCache?.();
    this.frameBuffer.clear();
    this.totalBufferedFrames = 0;
    this.frameIterator = null;
    this.preloadNextFrame = null;
    this.videoInfo = null;
    this.audioStream = null;
    this.audioPath = null;
    this.initialized = false;
    this._initializingPromise = null;
    super.destroy();
  }

  /**
   * 关闭视频元素，清理资源
   */
  async close() {
    // 终止 FFmpeg 进程
    if (this.ps) {
      try {
        this.ps.kill?.('SIGTERM');
      } catch (_) {}
      this.ps = null;
    }

    if (this.controller) {
      this.controller.abort();
      this.controller = null;
    }

    // 关闭音频流
    if (this.audioStream && typeof this.audioStream.close === 'function') {
      try {
        await this.audioStream.close();
      } catch (_) {}
    }

    this.frameBuffer.clear();
    this.totalBufferedFrames = 0;
    this.frameIterator = null;
    this.audioStream = null;
    this.audioPath = null;
    this.initialized = false;
  }
}

export { VideoElement };
//# sourceMappingURL=VideoElement.js.map
