// fallow-ignore-file code-duplication
import { execSync } from "node:child_process";
import { existsSync } from "node:fs";
import { resolve } from "node:path";
import { detectLinuxDistro, ffmpegInstallCommand } from "./linuxDeps.js";

export const FFMPEG_PATH_ENV = "HYPERFRAMES_FFMPEG_PATH";
export const FFPROBE_PATH_ENV = "HYPERFRAMES_FFPROBE_PATH";

function chooseBestPathCandidate(
  name: "ffmpeg" | "ffprobe",
  candidates: string[],
): string | undefined {
  const normalized = candidates.map((s) => s.trim()).filter(Boolean);
  if (normalized.length === 0) return undefined;
  const lowerName = name.toLowerCase();
  const preferredExe = normalized.find((candidate) =>
    candidate.toLowerCase().endsWith(`${lowerName}.exe`),
  );
  if (preferredExe) return preferredExe;
  const exact = normalized.find((candidate) => candidate.toLowerCase().endsWith(lowerName));
  if (exact) return exact;
  const nonShellShim = normalized.find((candidate) => {
    const lower = candidate.toLowerCase();
    return !lower.endsWith(".cmd") && !lower.endsWith(".bat");
  });
  return nonShellShim ?? normalized[0];
}

function findOnPath(name: "ffmpeg" | "ffprobe"): string | undefined {
  try {
    const cmd = process.platform === "win32" ? `where ${name}` : `which ${name}`;
    const output = execSync(cmd, {
      encoding: "utf-8",
      stdio: ["pipe", "pipe", "pipe"],
      timeout: 5000,
    });
    const candidate = chooseBestPathCandidate(name, output.split(/\r?\n/));
    return candidate ? resolve(candidate) : undefined;
  } catch {
    return undefined;
  }
}

// GUI/Dock/launchd-spawned processes on macOS don't inherit the shell PATH, so
// `which ffmpeg` fails even when ffmpeg is installed via Homebrew. Probe the
// well-known install dirs as a fallback. (No-op on Windows, where `where` and
// installer-added PATH entries cover it.)
const COMMON_BIN_DIRS =
  process.platform === "win32"
    ? []
    : ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin", "/snap/bin"];

function findInCommonDirs(name: "ffmpeg" | "ffprobe"): string | undefined {
  for (const dir of COMMON_BIN_DIRS) {
    const candidate = `${dir}/${name}`;
    if (existsSync(candidate)) return candidate;
  }
  return undefined;
}

function findConfiguredBinary(
  envName: string,
  binaryName: "ffmpeg" | "ffprobe",
): string | undefined {
  const configured = process.env[envName]?.trim();
  if (configured) return existsSync(configured) ? resolve(configured) : undefined;
  return findOnPath(binaryName) ?? findInCommonDirs(binaryName);
}

export function findFFmpeg(): string | undefined {
  return findConfiguredBinary(FFMPEG_PATH_ENV, "ffmpeg");
}

export function findFFprobe(): string | undefined {
  return findConfiguredBinary(FFPROBE_PATH_ENV, "ffprobe");
}

export function getFFmpegInstallHint(): string {
  switch (process.platform) {
    case "darwin":
      return "brew install ffmpeg";
    case "linux": {
      // Distro-aware so WSL/Fedora/Arch/Alpine users get a command that
      // actually works instead of a Debian-only `apt` line.
      const distro = detectLinuxDistro();
      return ffmpegInstallCommand(distro.family);
    }
    case "win32":
      return "Download the 64-bit Windows build from https://ffmpeg.org/download.html#build-windows and add its bin/ directory to PATH.";
    default:
      return "https://ffmpeg.org/download.html";
  }
}
