import { spawn } from "child_process";
import fs from "fs/promises";
import path from "path";
import ffmpegStatic from "ffmpeg-static";
import { getVideoAttributionText } from "../branding";
import { getResolution } from "../constants";
import { getWatermarkPath } from "../storage";

function getFfmpegPath(): string {
  if (process.env.FFMPEG_PATH) return process.env.FFMPEG_PATH;
  if (ffmpegStatic) return ffmpegStatic;
  return "ffmpeg";
}

export { getFfmpegPath };

export async function encodeVideo(options: {
  imagePath: string;
  audioPath: string;
  outputPath: string;
  resolution: string;
  includeWatermark: boolean;
}): Promise<void> {
  const res = getResolution(options.resolution);
  if (!res) throw new Error(`Invalid resolution: ${options.resolution}`);

  await fs.mkdir(path.dirname(options.outputPath), { recursive: true });

  const scaleFilter = `scale=${res.width}:${res.height}:force_original_aspect_ratio=decrease,pad=${res.width}:${res.height}:(ow-iw)/2:(oh-ih)/2:black`;

  const watermarkPath = getWatermarkPath();
  let watermarkExists = false;
  try {
    await fs.access(watermarkPath);
    watermarkExists = true;
  } catch {
    watermarkExists = false;
  }

  const useWatermark = options.includeWatermark && watermarkExists;
  const attributionText = getVideoAttributionText().replace(/:/g, "\\:").replace(/'/g, "\\'");
  const watermarkWidth = Math.max(1, Math.round(res.width * 0.32));

  // Base template: ffmpeg -loop 1 -r 1 -i image -i audio -c:v libx264 -tune stillimage -c:a copy -shortest -pix_fmt yuv420p output.mp4
  const args = ["-y", "-loop", "1", "-r", "1", "-i", options.imagePath, "-i", options.audioPath];

  if (useWatermark) {
    args.push("-i", watermarkPath);
    args.push(
      "-filter_complex",
      `[0:v]${scaleFilter}[scaled];[2:v]scale=${watermarkWidth}:-1[wm];[scaled][wm]overlay=W-w-20:H-h-20[vout]`,
      "-map",
      "[vout]",
      "-map",
      "1:a",
    );
  } else if (options.includeWatermark && !watermarkExists) {
    args.push(
      "-filter_complex",
      `[0:v]${scaleFilter},drawtext=text='${attributionText}':fontsize=22:fontcolor=white@0.85:x=w-text_w-20:y=h-th-20[vout]`,
      "-map",
      "[vout]",
      "-map",
      "1:a",
    );
  } else {
    args.push("-vf", scaleFilter);
  }

  args.push(
    "-c:v",
    "libx264",
    "-tune",
    "stillimage",
    "-c:a",
    "copy",
    "-shortest",
    "-pix_fmt",
    "yuv420p",
    options.outputPath,
  );

  await runFfmpeg(args);
}

function runFfmpeg(args: string[]): Promise<void> {
  return new Promise((resolve, reject) => {
    const proc = spawn(getFfmpegPath(), args, { stdio: ["ignore", "pipe", "pipe"] });
    let stderr = "";
    proc.stderr.on("data", (chunk) => {
      stderr += chunk.toString();
    });
    proc.on("close", (code) => {
      if (code === 0) resolve();
      else reject(new Error(`FFmpeg failed (code ${code}): ${stderr.slice(-500)}`));
    });
    proc.on("error", (err) => {
      reject(new Error(`FFmpeg not found or failed to start: ${err.message}`));
    });
  });
}

export async function cleanupFiles(paths: string[]) {
  for (const p of paths) {
    try {
      await fs.unlink(p);
    } catch {
      // ignore missing files
    }
  }
}
