/**
 * 时间线构建 + 视频调整策略
 *
 * 核心算法模块。纯函数 + ffmpeg，自包含可测试。
 *
 * 铁律（固化，不可绕过）：
 *   音频：生成后不可调速（TTS=1.15x，CapCut无speed字段）
 *   视频：始终配合音频时长（只允许加速/截断，不允许慢放/冻结）
 *
 * 时间线规则：
 *   图片模式: TTS 音频时长 = 画面时长，无音频 = 跳过
 *   视频模式: TTS 为主轴，视频通过策略适配
 *     视频比音频长 → 加速(≤2x) / 裁剪(>2x)
 *     视频比音频短 → 禁止！应在分镜阶段拆分 shot，不允许慢放/冻结补齐
 */

const fs = require('fs')
const path = require('path')
const { execFile } = require('child_process')
const { US } = require('./capcut-api')

// ============================================================================
// 时间线构建
// ============================================================================

/**
 * 构建时间线条目
 *
 * @param {Array} items - manifest items
 * @returns {Array} timeline entries
 *
 * 策略选择（固化，按 ratio = videoDur / audioDur）：
 *   ≥ 1.1, ≤ 2   → speed_up   （视频加速追上音频，最优）
 *   > 2          → trim       （视频截断至音频时长）
 *   0.9 ~ 1.1    → none       （接近匹配，无需调整）
 *   < 0.9        → 禁止！音频时长超过视频，分镜阶段未正确拆分 shot
 *
 * 铁律：不允许 slow_down / freeze，不允许音频调速
 */
function buildTimeline(items) {
  let offset = 0
  return items.map(item => {
    const audioDur = Math.round((item.audioDuration != null) ? item.audioDuration * US : 0)
    const videoDur = Math.round((item.videoDuration != null) ? item.videoDuration * US : 0)
    const hasVideo = !!(item.video || item.videoUrl || item.url)

    // 无 TTS 音频
    if (audioDur <= 0) {
      if (hasVideo && videoDur > 0) {
        const entry = { start: offset, end: offset + videoDur, duration: videoDur, speed: 1, strategy: 'none' }
        offset += videoDur
        return entry
      }
      const entry = { start: offset, end: offset, duration: 0, speed: 1, strategy: 'none', skip: true }
      return entry
    }

    const dur = audioDur

    if (!hasVideo || videoDur <= 0) {
      const entry = { start: offset, end: offset + dur, duration: dur, speed: 1, strategy: 'none' }
      offset += dur
      return entry
    }

    // 视频模式：策略选择（铁律：不允许音频>视频）
    const ratio = videoDur / audioDur

    if (ratio > 1.1) {
      if (ratio <= 2) {
        const entry = { start: offset, end: offset + dur, duration: dur, speed: ratio, strategy: 'speed_up' }
        offset += dur
        return entry
      } else {
        const entry = { start: offset, end: offset + dur, duration: dur, speed: 1, strategy: 'trim' }
        offset += dur
        return entry
      }
    } else if (ratio >= 0.9) {
      // 0.9 ~ 1.1：无需调整
      const entry = { start: offset, end: offset + dur, duration: dur, speed: 1, strategy: 'none' }
      offset += dur
      return entry
    } else {
      // ratio < 0.9：音频时长超过视频！
      // 铁律禁止：不允许慢放/冻结/拼接补齐。此情况应在分镜阶段拆分 shot。
      // 强制截断并打印错误标记，由主 Agent 上报给用户/打回分镜重做。
      const entry = {
        start: offset, end: offset + dur, duration: dur, speed: 1,
        strategy: 'FORBIDDEN_audio_gt_video',
        ratio: parseFloat(ratio.toFixed(3)),
        videoDur: parseFloat((videoDur / US).toFixed(2)),
        audioDur: parseFloat((audioDur / US).toFixed(2)),
        error: '音频时长(' + (audioDur / US).toFixed(2) + 's) > 视频时长(' + (videoDur / US).toFixed(2) + 's)，分镜阶段 shot 未正确拆分，请打回重新切割',
      }
      offset += dur
      return entry
    }
  })
}

// ============================================================================
// 视频调整（ffmpeg 策略）
// ============================================================================

/**
 * ffmpeg 视频调整：根据策略适配音频时长
 *
 * 允许策略（按 ratio = videoDur / audioDur 选择）:
 *   speed_up  (ratio > 1.1, ≤2x)  → setpts 压缩时间（加速），最优
 *   trim      (ratio > 2x)        → 截断到目标时长，次选
 *   none      (0.9~1.1)           → 无需调整
 *
 * 禁止策略（已删除）:
 *   slow_down (ratio < 0.9)       → ❌ 音频不可调速！
 *   freeze    (ratio < 0.5)        → ❌ 不允许冻结帧补齐！
 *
 * 所有策略失败后兜底：截断到目标时长
 */
async function adjustVideoSpeed(videoPath, targetDurationSec, strategy = 'none', speed = 1) {
  if (!fs.existsSync(videoPath)) return videoPath
  if (strategy === 'none') return videoPath

  function fallbackTrim(cb) {
    execFile('ffmpeg', [
      '-y', '-i', videoPath,
      '-t', String(targetDurationSec),
      '-c', 'copy',
      videoPath.replace(/(\.\w+)$/, '_adj$1')
    ], { timeout: 30000 }, (err) => {
      if (err) { cb(videoPath); return }
      cb(videoPath.replace(/(\.\w+)$/, '_adj$1'))
    })
  }

  return new Promise((resolve) => {
    execFile('ffprobe', [
      '-v', 'quiet', '-show_entries', 'format=duration',
      '-of', 'csv=p=0', videoPath
    ], (err, stdout) => {
      if (err) { fallbackTrim(resolve); return }
      const videoDur = parseFloat(stdout.trim())
      if (!videoDur || videoDur <= 0) { fallbackTrim(resolve); return }

      const outPath = videoPath.replace(/(\.\w+)$/, '_adj$1')

      if (strategy === 'trim') {
        execFile('ffmpeg', [
          '-y', '-i', videoPath,
          '-t', String(targetDurationSec),
          '-c', 'copy',
          outPath
        ], { timeout: 30000 }, (err) => {
          if (err) { console.log(`     截断失败: ${err.message}`); resolve(videoPath); return }
          console.log(`     截断: ${videoDur.toFixed(1)}s → ${targetDurationSec.toFixed(1)}s`)
          resolve(outPath)
        })
      } else if (strategy === 'speed_up') {
        const speedVal = speed.toFixed(3)
        execFile('ffmpeg', [
          '-y', '-i', videoPath,
          '-filter_complex', `setpts=PTS/${speedVal}`,
          '-an',
          outPath
        ], { timeout: 30000 }, (err) => {
          if (err) {
            console.log(`     加速失败，兜底截断: ${err.message}`)
            fallbackTrim(resolve)
            return
          }
          console.log(`     加速: ${videoDur.toFixed(1)}s → ${targetDurationSec.toFixed(1)}s (${speedVal}x)`)
          resolve(outPath)
        })
      } else {
        // 未知策略，兜底截断
        fallbackTrim(resolve)
      }
    })
  })
}

module.exports = { buildTimeline, adjustVideoSpeed }
