import { execFile } from 'child_process';
import { promisify } from 'util';

const execFileAsync = promisify(execFile);

export const runFFmpeg = async (args, options = {}) => {
  const { stdio = 'pipe', ...otherOptions } = options;
  try {
    const { stdout, stderr } = await execFileAsync('ffmpeg', args, {
      stdio,
      ...otherOptions,
    });
    return { stdout, stderr };
  } catch (error) {
    throw new Error(`FFmpeg error: ${error.message}\nStderr: ${error.stderr}`);
  }
};

const runFFprobe = async (args) => {
  try {
    const { stdout } = await execFileAsync('ffprobe', args);
    return { stdout };
  } catch (error) {
    throw new Error(`FFprobe error: ${error.message}\nStderr: ${error.stderr}`);
  }
};

export const getMediaDuration = async (inputPath) => {
  const args = [
    '-v', 'error',
    '-show_entries', 'format=duration',
    '-of', 'default=noprint_wrappers=1:nokey=1',
    inputPath,
  ];
  const { stdout } = await runFFprobe(args);
  return parseFloat(stdout.trim());
};

/**
 * Return structured media metadata for an input file.
 * Result shape:
 *   { fps, codec, resolution, durationMs, fileSizeBytes }
 * Any field may be null if ffprobe cannot determine it.
 */
export const getMediaInfo = async (inputPath) => {
  const args = [
    '-v', 'quiet',
    '-print_format', 'json',
    '-show_streams',
    '-show_format',
    inputPath,
  ];
  const { stdout } = await runFFprobe(args);
  const info = JSON.parse(stdout);

  const videoStream = (info.streams || []).find(s => s.codec_type === 'video');
  const fmt         = info.format || {};

  let fps = null;
  if (videoStream?.r_frame_rate) {
    const [num, den] = videoStream.r_frame_rate.split('/').map(Number);
    if (den > 0) fps = Math.round((num / den) * 1000) / 1000;
  }

  return {
    fps,
    codec:          videoStream?.codec_name  || null,
    resolution:     videoStream ? `${videoStream.width}x${videoStream.height}` : null,
    durationMs:     fmt.duration ? Math.round(parseFloat(fmt.duration) * 1000) : null,
    fileSizeBytes:  fmt.size     ? parseInt(fmt.size, 10)                       : null,
  };
};

export const extractFrameAtTime = async (inputPath, outputPath, timeCode) => {
  const args = [
    '-ss', timeCode,
    '-i', inputPath,
    '-vframes', '1',
    '-q:v', '2',
    '-y',
    outputPath,
  ];
  await runFFmpeg(args);
};

export const transcodeVideo = async (inputPath, outputPath, options = {}) => {
  const {
    videoCodec = 'libx264',
    videoPreset = 'fast',
    videoBitrate = '10M',
    audioCodec = 'aac',
    audioBitrate = '192k',
  } = options;

  const args = [
    '-i', inputPath,
    '-c:v', videoCodec,
    '-preset', videoPreset,
    '-b:v', videoBitrate,
    '-c:a', audioCodec,
    '-b:a', audioBitrate,
    '-movflags', '+faststart',
    '-y',
    outputPath,
  ];

  await runFFmpeg(args);
};

export const trimSegment = async (inputPath, outputPath, inPoint, outPoint) => {
  const args = [
    '-i', inputPath,
    '-ss', inPoint,
    '-to', outPoint,
    '-c', 'copy',
    '-y',
    outputPath,
  ];
  await runFFmpeg(args);
};

export const concatSegments = async (segmentListFile, outputPath) => {
  const args = [
    '-f', 'concat',
    '-safe', '0',
    '-i', segmentListFile,
    '-c', 'copy',
    '-y',
    outputPath,
  ];
  await runFFmpeg(args);
};
