import { spawn } from 'child_process';
import { config } from '../config';

export interface FFprobeStream {
  codec_name: string;
  codec_type: string;
  width?: number;
  height?: number;
  r_frame_rate?: string;
  avg_frame_rate?: string;
  duration?: string;
}

export interface FFprobeFormat {
  duration: string;
  size: string;
  filename: string;
}

export interface FFprobeResult {
  streams: FFprobeStream[];
  format: FFprobeFormat;
}

export interface FFmpegProgress {
  percent: number;
  currentTime: number;
}

type ProgressCallback = (progress: FFmpegProgress) => void;

export function runFFprobe(args: string[]): Promise<FFprobeResult> {
  return new Promise((resolve, reject) => {
    const proc = spawn(config.ffprobePath, args, { stdio: ['ignore', 'pipe', 'pipe'] });
    let stdout = '';
    let stderr = '';
    proc.stdout.on('data', (chunk: Buffer) => { stdout += chunk.toString(); });
    proc.stderr.on('data', (chunk: Buffer) => { stderr += chunk.toString(); });
    proc.on('close', (code) => {
      if (code !== 0) return reject(new Error(`FFprobe exit ${code}: ${stderr.slice(-300)}`));
      try {
        resolve(JSON.parse(stdout) as FFprobeResult);
      } catch {
        reject(new Error(`FFprobe JSON parse error: ${stdout.slice(-200)}`));
      }
    });
  });
}

export function runFFmpeg(args: string[], totalDuration?: number, onProgress?: ProgressCallback): Promise<void> {
  return new Promise((resolve, reject) => {
    const proc = spawn(config.ffmpegPath, ['-y', ...args], { stdio: ['ignore', 'pipe', 'pipe'] });
    let stderr = '';

    proc.stderr.on('data', (chunk: Buffer) => {
      const text = chunk.toString();
      stderr += text;

      if (onProgress && totalDuration) {
        const match = text.match(/time=(\d{2}):(\d{2}):(\d{2})\.(\d{2})/);
        if (match) {
          const currentTime =
            parseInt(match[1]) * 3600 +
            parseInt(match[2]) * 60 +
            parseInt(match[3]) +
            parseInt(match[4]) / 100;
          onProgress({
            percent: Math.min(99, (currentTime / totalDuration) * 100),
            currentTime,
          });
        }
      }
    });

    proc.on('close', (code) => {
      if (code === 0) {
        onProgress?.({ percent: 100, currentTime: totalDuration || 0 });
        resolve();
      } else {
        reject(new Error(`FFmpeg exit ${code}: ${stderr.slice(-500)}`));
      }
    });
  });
}

export async function probeVideo(filePath: string): Promise<FFprobeResult> {
  return runFFprobe([
    '-v', 'quiet',
    '-print_format', 'json',
    '-show_format',
    '-show_streams',
    filePath,
  ]);
}

export function getVideoCodec(probe: FFprobeResult): string {
  const videoStream = probe.streams.find(s => s.codec_type === 'video');
  return videoStream?.codec_name || 'unknown';
}

export function getVideoDuration(probe: FFprobeResult): number {
  return parseFloat(probe.format.duration || '0');
}

export function getVideoFps(probe: FFprobeResult): number {
  const videoStream = probe.streams.find(s => s.codec_type === 'video');
  if (!videoStream?.r_frame_rate) return 30;
  const [num, den] = videoStream.r_frame_rate.split('/').map(Number);
  return den ? num / den : 30;
}

export function checkFFmpegInstalled(): Promise<boolean> {
  // `ffprobe -version` 은 JSON 이 아닌 일반 텍스트를 출력하므로 runFFprobe(JSON 파싱) 로
  // 검사하면 항상 실패한다. 여기서는 실행 가능 여부(exit code 0)만 직접 확인한다.
  return new Promise((resolve) => {
    const proc = spawn(config.ffprobePath, ['-version'], { stdio: 'ignore' });
    proc.on('close', (code) => resolve(code === 0));
    proc.on('error', () => resolve(false));
  });
}
