/**
 * 光湖视频AI系统 · 视频剪辑引擎 v2.0
 * D136+ · 铸渊 ICE-GL-ZY001
 *
 * 基于 video-composer.js (D135) 升级。
 * 差异：composer 只能拼。editor 能剪。
 *
 *   composer: [视频] + [视频] + fade = 拼接
 *   editor:   [视频×N] + [配音] + [BGM] + [字幕] = 成品
 *
 * 全 ffmpeg 驱动。零 GUI 依赖。每一个参数铸渊能改。
 *
 * 使用方式:
 *   const { edit } = require('./video-editor');
 *   await edit({
 *     timeline: [
 *       { shot: 'shot01.mp4', trim: 3.5, transition: 'fade',
 *         keyframes: { zoom: { from: 1.0, to: 1.15 }, pan: { from: [0,0], to: [60,-80] } } },
 *       { shot: 'shot02.mp4', trim: 2.0, transition: 'cut'  },
 *     ],
 *     audio: {
 *       voice:  'voiceover.wav',          // 配音
 *       bgm:    'bgm.mp3',                // 背景音乐
 *       sfx:    [{ at: 2.5, file: 'hit.wav' }],  // 音效
 *       bgmVolume: 0.3,                   // BGM音量
 *       voiceVolume: 1.0,
 *     },
 *     subtitle: 'subtitles.srt',          // SRT字幕文件
 *     colorGrade: {
 *       brightness: 0, contrast: 1.1, saturation: 1.05,
 *     },
 *     resolution: { w: 1920, h: 1080 },   // 统一分辨率
 *     fps: 24,
 *     output: './output/final.mp4',
 *   });
 */

const { execSync, spawnSync } = require('child_process');
const fs = require('fs');
const path = require('path');

// ==================== 输出目录 ====================
const OUT = '/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/视频';
const DEFAULT_OUT = fs.existsSync(OUT) ? OUT : path.resolve(__dirname, '../outputs');

// ==================== 主入口 ====================

/**
 * 一站式剪辑
 */
async function edit({
  timeline, audio, subtitle, colorGrade,
  resolution, fps, output,
}) {
  if (!timeline || timeline.length === 0) throw new Error('timeline 不能为空');

  const w = resolution?.w || 1920;
  const h = resolution?.h || 1080;
  const fpsVal = fps || 24;
  const color = colorGrade || {};
  const tmp = path.join(DEFAULT_OUT, `.editor-${Date.now()}`);
  fs.mkdirSync(tmp, { recursive: true });
  const outPath = output || path.join(DEFAULT_OUT, `edited-${Date.now()}.mp4`);

  console.log('[Editor] ─── 剪辑开始 ───');
  console.log(`[Editor] 镜数: ${timeline.length} | ${w}×${h} @${fpsVal}fps`);

  // ── Step 1: 预处理每镜 ──
  console.log('[Editor] 1/5 预处理镜头...');
  const prepped = [];
  for (let i = 0; i < timeline.length; i++) {
    const t = timeline[i];
    const pFile = path.join(tmp, `prep_${i}.mp4`);
    const dur = t.trim || probeSec(t.shot);
    prepShot(t.shot, pFile, { w, h, fps: fpsVal, dur, color, keyframes: t.keyframes });
    prepped.push({ file: pFile, dur, transition: t.transition || 'fade' });
    console.log(`        镜${i + 1}: ${path.basename(t.shot)} → trim ${dur}s`);
  }

  // ── Step 2: 拼接视频轨 ──
  console.log('[Editor] 2/5 拼接视频轨...');
  const videoOnly = path.join(tmp, 'video_track.mp4');
  await composeTimeline(prepped, videoOnly, fpsVal);

  // ── Step 3: 处理音频轨 ──
  let videoWithAudio = videoOnly;
  if (audio) {
    console.log('[Editor] 3/5 合成音频轨...');
    const audioMix = path.join(tmp, 'audio_mix.wav');
    await mixAudioTrack(audio, audioMix, probeSec(videoOnly));
    videoWithAudio = path.join(tmp, 'video_audio.mp4');
    mergeAudio(videoOnly, audioMix, videoWithAudio);
  }

  // ── Step 4: 烧录字幕 ──
  let final = videoWithAudio;
  if (subtitle && fs.existsSync(subtitle)) {
    console.log('[Editor] 4/5 烧录字幕...');
    final = path.join(tmp, 'subtitled.mp4');
    burnSubtitles(videoWithAudio, subtitle, final);
  } else {
    console.log('[Editor] 4/5 无字幕·跳过');
  }

  // ── Step 5: 最终编码 ──
  console.log('[Editor] 5/5 最终编码...');
  finalEncode(final, outPath);

  // 统计
  const d = probeSec(outPath);
  const s = fmtSize(outPath);
  console.log(`[Editor] ✅ ${outPath}\n[Editor]    时长: ${d.toFixed(1)}s  大小: ${s}`);

  // 清理
  try { fs.rmSync(tmp, { recursive: true }); } catch (_) {}

  return { outputPath: outPath, duration: d, size: s };
}

// ==================== Step 1: 镜头预处理 ====================

/**
 * 预处理单镜：统一分辨率/帧率/调色 → 关键帧动画 → 输出标准化片段
 *
 * keyframes 支持三种动画叠加:
 *   zoom:  { from: 1.0,  to: 1.2 }    → 慢推 20%
 *   pan:   { from: [0,0], to: [80,-40] } → 平移 (px, 从画面中心)
 *   rotate:{ from: 0,    to: 3 }      → 缓转 3°
 *
 * 示例: 镜1从全景慢推到苏白面部
 *   keyframes: { zoom: { from: 1.0, to: 1.15 }, pan: { from: [0,0], to: [60,-80] } }
 */
function prepShot(src, dest, { w, h, fps, dur, color, keyframes }) {
  const hasZoom  = keyframes?.zoom;
  const hasPan   = keyframes?.pan;
  const hasRotate= keyframes?.rotate;

  // ── 构建 filter 链 ──
  const vfParts = [];

  if (hasZoom || hasPan) {
    // zoompan 同时处理缩放+平移 — 替代 scale+pad
    vfParts.push(buildZoomPan({ w, h, fps, dur, keyframes }));
  } else {
    // 无动画: 固定缩放
    vfParts.push(`scale=${w}:${h}:force_original_aspect_ratio=decrease,pad=${w}:${h}:(ow-iw)/2:(oh-ih)/2`);
  }

  // 调色
  const eq = [];
  if (color?.brightness) eq.push(`brightness=${color.brightness}`);
  if (color?.contrast)   eq.push(`contrast=${color.contrast}`);
  if (color?.saturation) eq.push(`saturation=${color.saturation}`);
  if (eq.length) vfParts.push(`eq=${eq.join(':')}`);

  // 旋转
  if (hasRotate) {
    const angle = keyframes.rotate.to != null ? keyframes.rotate.to : keyframes.rotate;
    vfParts.push(`rotate=${angle}*PI/180:c=none:ow=rotw(${angle}*PI/180):oh=roth(${angle}*PI/180)`);
  }

  const vf = vfParts.join(',');
  const fpsFlag = fps ? `-r ${fps}` : '';

  execSync(
    `ffmpeg -y -i "${src}" -t ${dur} ${fpsFlag} ` +
    `-vf "${vf}" -c:v libx264 -preset ultrafast -crf 18 -an "${dest}" 2>/dev/null`,
    { timeout: 45000 }
  );

  if (!fs.existsSync(dest)) throw new Error(`预处理失败: ${path.basename(src)}`);
}

/**
 * 构建 zoompan filter 表达式
 *
 * zoompan 原理:
 *   z  = 缩放倍率 (0~10)
 *   d  = 1 (逐帧处理)
 *   x,y= 画面位移 (相对于输出尺寸的左上角偏移)
 *   s  = 输出分辨率
 *
 * on变量 = 当前处理的帧序号 (从1开始)
 * 首帧: if(eq(on,1), start_val, ...)
 * 后续: 在上一帧基础上增量
 */
function buildZoomPan({ w, h, fps, dur, keyframes }) {
  const frames = Math.round(dur * fps) || 30; // 总帧数
  const zf = keyframes.zoom?.from ?? 1.0;
  const zt = keyframes.zoom?.to   ?? zf;
  const zStep = (zt - zf) / frames;

  const panFromX = keyframes.pan?.from?.[0] ?? 0;
  const panFromY = keyframes.pan?.from?.[1] ?? 0;
  const panToX   = keyframes.pan?.to?.[0]   ?? panFromX;
  const panToY   = keyframes.pan?.to?.[1]   ?? panFromY;
  const pxStep = (panToX - panFromX) / frames;
  const pyStep = (panToY - panFromY) / frames;

  // zoom: 首帧=zf, 否则=zoom+zStep (基于表达式中的zoom变量累加)
  // pan: 首帧=偏移量, 逐帧累加
  const zExpr = `if(eq(on,1),${zf},zoom+${zStep.toFixed(8)})`;
  const xExpr = `iw/2-(iw/zoom/2)+if(eq(on,1),${panFromX},${panFromX}+on*${pxStep.toFixed(4)})`;
  const yExpr = `ih/2-(ih/zoom/2)+if(eq(on,1),${panFromY},${panFromY}+on*${pyStep.toFixed(4)})`;

  return `zoompan=z='${zExpr}':d=1:x='${xExpr}':y='${yExpr}':s=${w}x${h}`;
}

// ==================== Step 2: 视频拼接 ====================

function composeTimeline(shots, output, fps) {
  if (shots.length === 1) {
    fs.copyFileSync(shots[0].file, output);
    return;
  }
  composeXFadeChain(shots, output, fps);
}

function composeXFadeChain(shots, output, fps) {
  // 用 concat demuxer + xfade filter 链式拼接
  // 每镜之间的过渡由 transition 决定

  let filter = '';
  let prev = '0:v';
  let accumDur = shots[0].dur;
  const inputs = shots.map((s, i) => `-i "${s.file}"`).join(' ');

  for (let i = 1; i < shots.length; i++) {
    const fadeDur = shots[i].transition === 'cut' ? 0.1 : 0.3;
    const offset = (accumDur - fadeDur).toFixed(2);
    const label = i < shots.length - 1 ? `v${i}` : 'vout';
    filter += `[${prev}][${i}:v]xfade=transition=fade:duration=${fadeDur}:offset=${offset}[${label}];\n`;
    prev = label;
    accumDur += shots[i].dur - fadeDur;
  }

  execSync(
    `ffmpeg -y ${inputs} -filter_complex "${filter.trim()}" ` +
    `-map "[${prev}]" -r ${fps || 24} -c:v libx264 -preset fast -crf 23 -an "${output}" 2>/dev/null`,
    { timeout: 120000 }
  );
}

// ==================== Step 3: 音频混合 ====================

function mixAudioTrack({ voice, bgm, sfx, bgmVolume, voiceVolume }, output, duration) {
  // 构建 filter_complex: 多输入混合 + 音量控制
  const inputs = [];
  const filters = [];
  let streamIdx = 0;
  const mixInputs = [];

  // 配音轨
  if (voice && fs.existsSync(voice)) {
    inputs.push(`-i "${voice}"`);
    const vol = voiceVolume != null ? voiceVolume : 1.0;
    const padDur = duration > 0 ? `,adelay=0|0,apad=pad_dur=${duration}` : '';
    filters.push(`[${streamIdx}:a]volume=${vol}${padDur}[vce];`);
    mixInputs.push('[vce]');
    streamIdx++;
  }

  // BGM轨
  if (bgm && fs.existsSync(bgm)) {
    inputs.push(`-i "${bgm}"`);
    const vol = bgmVolume != null ? bgmVolume : 0.3;
    const padDur = duration > 0 ? `,adelay=0|0,apad=pad_dur=${duration}` : '';
    filters.push(`[${streamIdx}:a]volume=${vol}${padDur}[bgm];`);
    mixInputs.push('[bgm]');
    streamIdx++;
  }

  // 音效轨
  if (sfx && sfx.length > 0) {
    for (const s of sfx) {
      if (!fs.existsSync(s.file)) continue;
      inputs.push(`-i "${s.file}"`);
      const delay = (s.at || 0) * 1000; // 毫秒
      const padDur = duration > 0 ? `,adelay=${delay}|${delay},apad=pad_dur=${duration}` : `,adelay=${delay}|${delay}`;
      filters.push(`[${streamIdx}:a]volume=1.0${padDur}[sfx${streamIdx}];`);
      mixInputs.push(`[sfx${streamIdx}]`);
      streamIdx++;
    }
  }

  if (mixInputs.length === 0) {
    // 静音轨
    execSync(`ffmpeg -y -f lavfi -i anullsrc=r=44100:cl=stereo -t ${duration} -c:a pcm_s16le "${output}" 2>/dev/null`);
    return;
  }

  if (mixInputs.length === 1) {
    // 只有一个输入
    const filterFull = filters.join('\n').replace(/\[(vce|bgm|sfx\d+)\];/, '');
    execSync(
      `ffmpeg -y ${inputs.join(' ')} -filter_complex "${filterFull.trim()}" ` +
      `-c:a pcm_s16le "${output}" 2>/dev/null`,
      { timeout: 60000 }
    );
    return;
  }

  // 多输入混合
  const amix = mixInputs.join('');
  const mixFilter = `${filters.join('\n')}${amix}amix=inputs=${mixInputs.length}:duration=longest:dropout_transition=2[amix]`;
  execSync(
    `ffmpeg -y ${inputs.join(' ')} -filter_complex "${mixFilter}" ` +
    `-map "[amix]" -c:a pcm_s16le "${output}" 2>/dev/null`,
    { timeout: 60000 }
  );
}

function mergeAudio(videoFile, audioFile, output) {
  execSync(
    `ffmpeg -y -i "${videoFile}" -i "${audioFile}" ` +
    `-c:v copy -c:a aac -b:a 192k -shortest -map 0:v:0 -map 1:a:0 "${output}" 2>/dev/null`,
    { timeout: 60000 }
  );
}

// ==================== Step 4: 字幕烧录 ====================

function burnSubtitles(videoFile, srtFile, output) {
  // SRT 内嵌到视频帧
  // 用 subtitles filter: 中文兼容 · 字体回退
  execSync(
    `ffmpeg -y -i "${videoFile}" -vf ` +
    `"subtitles='${srtFile}':force_style='FontName=PingFang SC,Fontsize=28,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,Outline=1.5,Shadow=1,MarginV=30'" ` +
    `-c:v libx264 -preset fast -crf 23 -c:a copy "${output}" 2>/dev/null`,
    { timeout: 120000 }
  );
}

// ==================== Step 5: 最终编码 ====================

function finalEncode(src, dest) {
  execSync(
    `ffmpeg -y -i "${src}" ` +
    `-c:v libx264 -preset medium -crf 21 ` +
    `-c:a aac -b:a 192k -movflags +faststart "${dest}" 2>/dev/null`,
    { timeout: 60000 }
  );
}

// ==================== 工具 ====================

function probeSec(file) {
  try {
    const out = execSync(
      `ffprobe -v quiet -show_entries format=duration -of csv=p=0 "${file}"`,
      { timeout: 5000, encoding: 'utf8' }
    );
    return parseFloat(out.trim()) || 4.0;
  } catch (_) { return 4.0; }
}

function fmtSize(file) {
  try {
    const s = fs.statSync(file).size;
    return s < 1048576 ? `${(s / 1024).toFixed(0)}KB` : `${(s / 1048576).toFixed(1)}MB`;
  } catch (_) { return '?'; }
}

// ==================== 子工具：独立字幕生成 ====================

/**
 * 从文本生成 SRT 字幕（简单场景：每句均匀分配）
 * @param {string[]} lines - 字幕文本行
 * @param {number} duration - 视频总时长(秒)
 */
function generateSRT(lines, duration) {
  const perLine = duration / lines.length;
  let srt = '';
  for (let i = 0; i < lines.length; i++) {
    const start = i * perLine;
    const end = start + perLine;
    srt += `${i + 1}\n`;
    srt += `${fmtTime(start)} --> ${fmtTime(end)}\n`;
    srt += `${lines[i]}\n\n`;
  }
  return srt;
}

function fmtTime(sec) {
  const h = Math.floor(sec / 3600);
  const m = Math.floor((sec % 3600) / 60);
  const s = (sec % 60).toFixed(3);
  return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(6, '0')}`.replace('.', ',');
}

// ==================== 导出 ====================

module.exports = { edit, composeTimeline, mixAudioTrack, burnSubtitles, generateSRT, probeSec };
