/**
 * 光湖视频AI系统 · 音频分轨引擎
 * D140 · 铸渊 ICE-GL-ZY001 · 2026-06-23
 *
 * 将混合音频分离为三轨: 对白(voice) / 背景音乐(bgm) / 音效(sfx)
 * 基于 FFmpeg 音频滤波链 + 频率/动态范围分析
 *
 * 工作原理:
 *   1. 对白轨: 人声频率范围(80Hz-8kHz)带通 + 动态范围压缩
 *   2. BGM轨: 低频段(20-250Hz) + 高频段(8kHz+)的中低能量部分
 *   3. SFX轨: 高频瞬态(打击类) + 非人声中频
 *
 * 输出三轨独立WAV，供 video-editor.js 混音使用
 *
 * 使用方式:
 *   const { splitAudioStems, analyzeAudioLevels } = require('./audio-stem-splitter');
 *   const stems = await splitAudioStems('input.mp4', './stems/');
 *   // stems.voice, stems.bgm, stems.sfx → 独立WAV文件路径
 *
 * 铸渊 ICE-GL-ZY001 · D140
 */

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

// ==================== 核心分轨 ====================

/**
 * 将音视频文件分离为三轨: 对白 / BGM / 音效
 *
 * @param {string} inputPath - 输入文件（视频或音频）
 * @param {string} outputDir - 输出目录
 * @param {object} [opts]
 * @param {number} [opts.voiceLow=80]   - 人声低频截止 Hz
 * @param {number} [opts.voiceHigh=8000] - 人声高频截止 Hz
 * @param {number} [opts.bgmLow=20]     - BGM低频 Hz
 * @param {number} [opts.bgmHigh=250]   - BGM低频截止 Hz
 * @param {number} [opts.sfxThreshold]  - SFX瞬态检测阈值(dB)
 * @returns {Promise<{voice: string, bgm: string, sfx: string, raw: string, meta: object}>}
 */
async function splitAudioStems(inputPath, outputDir, opts = {}) {
  if (!fs.existsSync(inputPath)) {
    throw new Error(`输入文件不存在: ${inputPath}`);
  }

  // 确保输出目录
  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
  }

  const baseName = path.basename(inputPath, path.extname(inputPath));
  const rawWav = path.join(outputDir, `${baseName}_raw.wav`);
  const voiceWav = path.join(outputDir, `${baseName}_voice.wav`);
  const bgmWav = path.join(outputDir, `${baseName}_bgm.wav`);
  const sfxWav = path.join(outputDir, `${baseName}_sfx.wav`);
  const metaJson = path.join(outputDir, `${baseName}_stems_meta.json`);

  console.log(`[StemSplitter] 输入: ${path.basename(inputPath)}`);
  console.log(`[StemSplitter] 输出: ${outputDir}`);

  // Step 1: 提取原始音频
  console.log('[StemSplitter] 1/4 提取原始音频...');
  execSync(
    `ffmpeg -y -i "${inputPath}" -vn -acodec pcm_s16le -ar 48000 -ac 2 "${rawWav}" 2>/dev/null`,
    { timeout: 60000 }
  );

  if (!fs.existsSync(rawWav)) {
    throw new Error('原始音频提取失败');
  }

  const duration = probeDuration(rawWav);
  console.log(`  原始音频: ${duration.toFixed(1)}s`);

  // Step 2: 分离对白轨（人声频段带通 + 动态增强）
  console.log('[StemSplitter] 2/4 分离对白轨...');
  extractVoiceTrack(rawWav, voiceWav, opts);

  // Step 3: 分离BGM轨（低频 + 宽频带中低能量）
  console.log('[StemSplitter] 3/4 分离BGM轨...');
  extractBgmTrack(rawWav, bgmWav, opts);

  // Step 4: 分离SFX轨（高频瞬态 + 残余中频）
  console.log('[StemSplitter] 4/4 分离音效轨...');
  extractSfxTrack(rawWav, sfxWav, voiceWav, bgmWav, opts);

  // 生成元数据
  const meta = {
    input: inputPath,
    duration: duration,
    tracks: {
      voice: { file: voiceWav, duration: probeDuration(voiceWav) },
      bgm: { file: bgmWav, duration: probeDuration(bgmWav) },
      sfx: { file: sfxWav, duration: probeDuration(sfxWav) },
    },
    createdAt: new Date().toISOString(),
  };

  fs.writeFileSync(metaJson, JSON.stringify(meta, null, 2));

  console.log(`[StemSplitter] ✅ 分轨完成`);
  console.log(`  对白: ${path.basename(voiceWav)} (${meta.tracks.voice.duration.toFixed(1)}s)`);
  console.log(`  BGM:  ${path.basename(bgmWav)} (${meta.tracks.bgm.duration.toFixed(1)}s)`);
  console.log(`  音效: ${path.basename(sfxWav)} (${meta.tracks.sfx.duration.toFixed(1)}s)`);

  return {
    voice: voiceWav,
    bgm: bgmWav,
    sfx: sfxWav,
    raw: rawWav,
    meta,
  };
}

// ==================== 对白轨提取 ====================

function extractVoiceTrack(inputWav, outputWav, opts = {}) {
  const voiceLow = opts.voiceLow || 80;
  const voiceHigh = opts.voiceHigh || 8000;

  // 滤波链:
  // 1. highpass=f=voiceLow  — 去除低频噪声
  // 2. lowpass=f=voiceHigh  — 限制人声高频范围
  // 3. compand              — 动态范围压缩，增强人声
  // 4. afftdn=nr=10         — 降噪
  const filter = [
    `highpass=f=${voiceLow}`,
    `lowpass=f=${voiceHigh}`,
    `compand=attacks=0:points=-80/-90|-45/-15|-27/-9|0/-7|20/-7`,
    `afftdn=nr=10:nf=-25`,
  ].join(',');

  execSync(
    `ffmpeg -y -i "${inputWav}" -af "${filter}" -acodec pcm_s16le -ar 48000 -ac 2 "${outputWav}" 2>/dev/null`,
    { timeout: 120000 }
  );

  if (!fs.existsSync(outputWav)) {
    throw new Error('对白轨提取失败');
  }
}

// ==================== BGM轨提取 ====================

function extractBgmTrack(inputWav, outputWav, opts = {}) {
  const bgmLow = opts.bgmLow || 20;
  const bgmHigh = opts.bgmHigh || 250;

  // BGM特征: 低频持续 + 宽频带中低能量
  // 滤波链:
  // 1. lowpass=f=bgmHigh     — 截取低频段（鼓/贝斯/低频氛围）
  // 2. bass=g=3              — 增强低频
  // 3. compand               — 压缩动态范围，使BGM更平稳
  // 4. volume=0.7            — 稍微降低（混音时通常BGM低于人声）
  const filter = [
    `lowpass=f=${bgmHigh}`,
    `bass=g=3:f=${bgmLow}:w=80`,
    `compand=attacks=1:decays=1:points=-80/-90|-45/-20|0/-12|20/-12`,
    `volume=0.7`,
  ].join(',');

  execSync(
    `ffmpeg -y -i "${inputWav}" -af "${filter}" -acodec pcm_s16le -ar 48000 -ac 2 "${outputWav}" 2>/dev/null`,
    { timeout: 120000 }
  );

  if (!fs.existsSync(outputWav)) {
    throw new Error('BGM轨提取失败');
  }
}

// ==================== SFX轨提取 ====================

function extractSfxTrack(inputWav, outputWav, voiceWav, bgmWav, opts = {}) {
  // SFX = 原始音频 - 对白 - BGM
  // 方法: 用 sidechaincompress 或直接用频段提取
  //
  // 滤波链:
  // 1. highpass=f=2000      — 取高频段（打击/碰撞/环境音效多在高频）
  // 2. 用原始音频减去voice和bgm的频段
  //    实际实现: 取高频 + 中频瞬态部分
  
  const filter = [
    `highpass=f=1500`,                                         // 高频段
    `treble=g=2:f=4000`,                                       // 高频增强
    `compand=attacks=0:decays=0.3:points=-80/-90|-30/-30|0/-5|20/-5`,  // 快速响应瞬态
    `volume=0.8`,
    `silenceremove=start_periods=0:start_threshold=-50dB:start_silence=0.1:stop_threshold=-50dB:stop_silence=0.05`,
  ].join(',');

  execSync(
    `ffmpeg -y -i "${inputWav}" -af "${filter}" -acodec pcm_s16le -ar 48000 -ac 2 "${outputWav}" 2>/dev/null`,
    { timeout: 120000 }
  );

  if (!fs.existsSync(outputWav)) {
    throw new Error('音效轨提取失败');
  }
}

// ==================== 音频电平分析 ====================

/**
 * 分析音频文件的电平信息（RMS、峰值、LUFS近似值）
 * 用于自动混音决策
 *
 * @param {string} audioPath
 * @returns {{rms: number, peak: number, duration: number, channels: number}}
 */
function analyzeAudioLevels(audioPath) {
  if (!fs.existsSync(audioPath)) {
    throw new Error(`音频文件不存在: ${audioPath}`);
  }

  // 使用 ffprobe 获取音频信息
  const probeOut = execSync(
    `ffprobe -v quiet -show_entries format=duration:stream=channels,sample_rate -of json "${audioPath}"`,
    { encoding: 'utf8', timeout: 5000 }
  );
  const probeData = JSON.parse(probeOut);
  const duration = parseFloat(probeData.format?.duration || 0);
  const channels = probeData.streams?.[0]?.channels || 2;

  // 使用 ffmpeg volumedetect 获取电平
  const volOut = execSync(
    `ffmpeg -i "${audioPath}" -af volumedetect -f null /dev/null 2>&1 | grep -E "mean_volume|max_volume"`,
    { encoding: 'utf8', timeout: 30000 }
  );

  const meanMatch = volOut.match(/mean_volume:\s*(-?\d+\.?\d*)\s*dB/);
  const maxMatch = volOut.match(/max_volume:\s*(-?\d+\.?\d*)\s*dB/);

  const rms = meanMatch ? parseFloat(meanMatch[1]) : -20;
  const peak = maxMatch ? parseFloat(maxMatch[1]) : -3;

  return {
    rms,           // RMS电平 (dB)
    peak,          // 峰值电平 (dB)
    duration,      // 时长 (秒)
    channels,      // 声道数
    sampleRate: probeData.streams?.[0]?.sample_rate || 48000,
  };
}

// ==================== 自动混音建议 ====================

/**
 * 根据三轨电平分析，生成混音参数建议
 *
 * @param {string} voiceWav
 * @param {string} bgmWav
 * @param {string} sfxWav
 * @returns {{voiceVolume: number, bgmVolume: number, sfxVolume: number, reasoning: string[]}}
 */
function suggestMixLevels(voiceWav, bgmWav, sfxWav) {
  const reasoning = [];

  // 分析各轨电平
  const voiceLevel = analyzeAudioLevels(voiceWav);
  const bgmLevel = bgmWav && fs.existsSync(bgmWav) ? analyzeAudioLevels(bgmWav) : null;
  const sfxLevel = sfxWav && fs.existsSync(sfxWav) ? analyzeAudioLevels(sfxWav) : null;

  // 目标: 对白 > BGM > SFX（一般情况）
  // 对白 RMS 目标: -16 ~ -12 dB
  // BGM RMS 目标: -24 ~ -20 dB (比对白低8dB左右)
  // SFX RMS 目标: -20 ~ -15 dB

  let voiceVolume = 1.0;
  let bgmVolume = 0.4;
  let sfxVolume = 0.6;

  // 根据对白RMS调整
  const voiceTarget = -14;
  if (voiceLevel.rms < voiceTarget - 3) {
    voiceVolume = Math.min(1.5, (voiceTarget - voiceLevel.rms) / 6 + 1.0);
    reasoning.push(`对白RMS偏低(${voiceLevel.rms}dB)，增益×${voiceVolume.toFixed(2)}`);
  } else if (voiceLevel.rms > voiceTarget + 3) {
    voiceVolume = Math.max(0.5, 1.0 - (voiceLevel.rms - voiceTarget) / 10);
    reasoning.push(`对白RMS偏高(${voiceLevel.rms}dB)，衰减×${voiceVolume.toFixed(2)}`);
  }

  // 根据BGM电平调整
  if (bgmLevel) {
    const bgmTarget = -22;
    const bgmDiff = bgmLevel.rms - bgmTarget;
    if (Math.abs(bgmDiff) > 3) {
      bgmVolume = Math.max(0.15, Math.min(0.8, 0.4 * Math.pow(10, -bgmDiff / 20)));
      reasoning.push(`BGM电平调整: RMS=${bgmLevel.rms}dB → 音量×${bgmVolume.toFixed(2)}`);
    }
  }

  // 根据SFX电平调整
  if (sfxLevel) {
    const sfxTarget = -18;
    const sfxDiff = sfxLevel.rms - sfxTarget;
    if (Math.abs(sfxDiff) > 3) {
      sfxVolume = Math.max(0.2, Math.min(1.0, 0.6 * Math.pow(10, -sfxDiff / 20)));
      reasoning.push(`SFX电平调整: RMS=${sfxLevel.rms}dB → 音量×${sfxVolume.toFixed(2)}`);
    }
  }

  // 确保对白比BGM高至少6dB
  if (bgmLevel && voiceLevel.rms + 20 * Math.log10(voiceVolume) < 
      bgmLevel.rms + 20 * Math.log10(bgmVolume) + 6) {
    bgmVolume *= 0.7;
    reasoning.push(`对白-BGM间距不足6dB，BGM再降×0.7`);
  }

  return {
    voiceVolume: Math.round(voiceVolume * 100) / 100,
    bgmVolume: Math.round(bgmVolume * 100) / 100,
    sfxVolume: Math.round(sfxVolume * 100) / 100,
    voiceLevel,
    bgmLevel,
    sfxLevel,
    reasoning,
  };
}

// ==================== 工具函数 ====================

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

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

module.exports = {
  splitAudioStems,
  extractVoiceTrack,
  extractBgmTrack,
  extractSfxTrack,
  analyzeAudioLevels,
  suggestMixLevels,
  probeDuration,
};
