/**
 * 视频生成模块 - FFmpeg 命令封装
 * 实现 Ken Burns 效果、图片+音频合成等功能
 */

import ffmpeg from 'fluent-ffmpeg';
import path from 'path';
import fs from 'fs';
import { VideoConfig } from './video-generator.types';

const OUTPUT_DIR = path.join(process.cwd(), 'public', 'videos');
const TEMP_DIR = path.join(process.cwd(), 'temp');

// 确保目录存在
function ensureDirectories() {
  if (!fs.existsSync(OUTPUT_DIR)) fs.mkdirSync(OUTPUT_DIR, { recursive: true });
  if (!fs.existsSync(TEMP_DIR)) fs.mkdirSync(TEMP_DIR, { recursive: true });
}

/**
 * 合成图片+音频生成最终视频
 */
export async function generateVideo(
  imagePath: string,
  audioPath: string,
  outputPath: string,
  config: VideoConfig
): Promise<{ duration: number; fileSize: number }> {
  ensureDirectories();

  return new Promise((resolve, reject) => {
    const command = ffmpeg();
    command
      .input(imagePath)
      .inputOptions(['-loop', '1'])
      .input(audioPath);

    // 构建视频滤镜
    const videoFilters: any[] = [];

    // Ken Burns 效果
    if (config.kenburns?.enabled) {
      const kb = config.kenburns;
      videoFilters.push({
        filter: 'zoompan',
        options: {
          z: `if(gte(zoom,${kb.minZoom}),min(zoom+0.001,${kb.maxZoom}),${kb.minZoom})`,
          x: 'iw/2-(iw/zoom/2)',
          y: 'ih/2-(ih/zoom/2)',
          d: 1,
          s: `${config.video.width}x${config.video.height}`,
          fps: config.video.fps,
        },
      });
    }

    // 缩放和填充
    videoFilters.push({
      filter: 'scale',
      options: `${config.video.width}:${config.video.height}:force_original_aspect_ratio=decrease`,
    });
    videoFilters.push({
      filter: 'pad',
      options: `${config.video.width}:${config.video.height}:(ow-iw)/2:(oh-ih)/2:color=black`,
    });

    // 字幕
    if (config.subtitle) {
      const sub = config.subtitle;
      const yPos = sub.position === 'top' ? 20 : sub.position === 'center' ? (config.video.height - 40) / 2 : config.video.height - 60;
      videoFilters.push({
        filter: 'drawtext',
        options: {
          text: sub.text,
          fontsize: sub.fontSize || 24,
          fontcolor: sub.fontColor || 'white',
          borderw: 2,
          bordercolor: 'black',
          x: '(w-text_w)/2',
          y: yPos,
        },
      });
    }

    command.videoFilters(videoFilters);

    command
      .outputOptions([`-af volume=${config.audio.volume}`])
      .outputOptions([
        '-c:v libx264',
        '-preset fast',
        '-crf 18',
        '-c:a aac',
        '-b:a 192k',
        '-pix_fmt yuv420p',
        '-shortest',
      ])
      .output(outputPath);

    command.on('progress', (progress) => {
      if (progress.percent) console.log(`Processing: ${progress.percent.toFixed(1)}% done`);
    });

    command.on('end', () => {
      ffmpeg.ffprobe(outputPath, (err, metadata) => {
        if (err) { reject(err); return; }
        const stats = fs.statSync(outputPath);
        resolve({
          duration: Math.round(metadata.format.duration || 0),
          fileSize: stats.size,
        });
      });
    });

    command.on('error', reject);
    command.run();
  });
}

/**
 * 合成带背景音乐的混音视频
 */
export async function generateVideoWithBgm(
  imagePath: string,
  audioPath: string,
  bgmPath: string,
  outputPath: string,
  config: VideoConfig
): Promise<{ duration: number; fileSize: number }> {
  ensureDirectories();

  return new Promise((resolve, reject) => {
    const command = ffmpeg();
    command
      .input(imagePath)
      .inputOptions(['-loop 1'])
      .input(audioPath)
      .input(bgmPath)
      .inputOptions(['-stream_loop -1']);

    const videoFilters: any[] = [];

    if (config.kenburns?.enabled) {
      const kb = config.kenburns;
      videoFilters.push({
        filter: 'zoompan',
        options: {
          z: `if(gte(zoom,${kb.minZoom}),min(zoom+0.001,${kb.maxZoom}),${kb.minZoom})`,
          x: 'iw/2-(iw/zoom/2)',
          y: 'ih/2-(ih/zoom/2)',
          d: 1,
          s: `${config.video.width}x${config.video.height}`,
          fps: config.video.fps,
        },
      });
    }

    videoFilters.push({
      filter: 'scale',
      options: `${config.video.width}:${config.video.height}:force_original_aspect_ratio=decrease`,
    });
    videoFilters.push({
      filter: 'pad',
      options: `${config.video.width}:${config.video.height}:(ow-iw)/2:(oh-ih)/2:color=black`,
    });

    command.videoFilters(videoFilters);

    // 音频混音
    const bgm = config.bgm!;
    command.audioFilters(`[1:a]volume=${config.audio.volume}[main];[2:a]volume=${bgm.volume}[bgm];[main][bgm]amix=inputs=2:duration=longest[aout]`);

    command
      .outputOptions([
        '-tune stillimage',
        '-c:v libx264',
        '-preset fast',
        '-crf 18',
        '-c:a aac',
        '-b:a 192k',
        '-pix_fmt yuv420p',
        '-shortest',
        '-map 0:v',
        '-map [aout]',
      ])
      .output(outputPath);

    command.on('end', () => {
      ffmpeg.ffprobe(outputPath, (err, metadata) => {
        if (err) { reject(err); return; }
        const stats = fs.statSync(outputPath);
        resolve({ duration: Math.round(metadata.format.duration || 0), fileSize: stats.size });
      });
    });

    command.on('error', reject);
    command.run();
  });
}

/**
 * 生成多图轮播视频
 */
export async function generateSlideshow(
  images: { path: string; duration: number }[],
  audioPath: string,
  outputPath: string,
  config: VideoConfig
): Promise<{ duration: number; fileSize: number }> {
  ensureDirectories();

  const concatListPath = path.join(TEMP_DIR, `concat_${Date.now()}.txt`);
  const concatList = images.map((img) => `file '${img.path}'`).join('\n');
  fs.writeFileSync(concatListPath, concatList);

  return new Promise((resolve, reject) => {
    const command = ffmpeg();
    command
      .input(concatListPath)
      .inputOptions(['-f concat', '-safe 0'])
      .input(audioPath);

    const videoFilters: any[] = [];
    videoFilters.push({
      filter: 'scale',
      options: `${config.video.width}:${config.video.height}:force_original_aspect_ratio=decrease`,
    });
    videoFilters.push({
      filter: 'pad',
      options: `${config.video.width}:${config.video.height}:(ow-iw)/2:(oh-ih)/2:color=black`,
    });

    if (config.subtitle) {
      const sub = config.subtitle;
      const yPos = sub.position === 'top' ? 20 : sub.position === 'center' ? (config.video.height - 40) / 2 : config.video.height - 60;
      videoFilters.push({
        filter: 'drawtext',
        options: {
          text: sub.text,
          fontsize: sub.fontSize || 24,
          fontcolor: sub.fontColor || 'white',
          borderw: 2,
          bordercolor: 'black',
          x: '(w-text_w)/2',
          y: yPos,
        },
      });
    }

    command.videoFilters(videoFilters);

    command
      .outputOptions([
        '-c:v libx264',
        '-preset fast',
        '-crf 18',
        '-c:a aac',
        '-b:a 192k',
        '-pix_fmt yuv420p',
        '-shortest',
      ])
      .output(outputPath);

    command.on('end', () => {
      fs.unlinkSync(concatListPath);
      ffmpeg.ffprobe(outputPath, (err, metadata) => {
        if (err) { reject(err); return; }
        const stats = fs.statSync(outputPath);
        resolve({ duration: Math.round(metadata.format.duration || 0), fileSize: stats.size });
      });
    });

    command.on('error', (err) => {
      fs.unlinkSync(concatListPath);
      reject(err);
    });

    command.run();
  });
}

/**
 * 获取视频信息
 */
export async function getVideoInfo(filePath: string): Promise<{ duration: number; width: number; height: number; size: number }> {
  return new Promise((resolve, reject) => {
    ffmpeg.ffprobe(filePath, (err, metadata) => {
      if (err) { reject(err); return; }
      const videoStream = metadata.streams.find((s) => s.codec_type === 'video');
      const stats = fs.statSync(filePath);
      resolve({
        duration: Math.round(metadata.format.duration || 0),
        width: videoStream?.width || 0,
        height: videoStream?.height || 0,
        size: stats.size,
      });
    });
  });
}
