import "server-only";

import fs from "node:fs";
import os from "node:os";
import path from "node:path";

type MediaBinaryName = "ffmpeg" | "ffprobe";

export type BinaryResolutionSource =
  | "env"
  | "bundle-local"
  | "bundled"
  | "path"
  | "windows-common"
  | "fallback";

export interface BinaryResolution {
  binaryName: MediaBinaryName;
  path: string;
  source: BinaryResolutionSource;
}

function fileExists(filePath: string) {
  try {
    return fs.statSync(filePath).isFile();
  } catch {
    return false;
  }
}

function getRuntimePlatform() {
  return os.platform();
}

function getRuntimeArch() {
  return os.arch();
}

function commandNameForPlatform(binaryName: MediaBinaryName, runtimePlatform = getRuntimePlatform()) {
  return runtimePlatform === "win32" ? `${binaryName}.exe` : binaryName;
}

function splitPathEntries(rawPath: string | undefined) {
  if (!rawPath) {
    return [];
  }

  return rawPath
    .split(path.delimiter)
    .map((entry) => entry.trim())
    .filter(Boolean);
}

function resolveFromEnv(binaryName: MediaBinaryName): BinaryResolution | null {
  const envVariable = binaryName === "ffmpeg" ? "FFMPEG_PATH" : "FFPROBE_PATH";
  const configuredValue = process.env[envVariable]?.trim();
  if (!configuredValue) {
    return null;
  }

  const candidatePath = path.isAbsolute(configuredValue)
    ? configuredValue
    : path.resolve(configuredValue);

  if (fileExists(candidatePath)) {
    return {
      binaryName,
      path: candidatePath,
      source: "env"
    };
  }

  // Allow command names in env values so PATH lookup can still work.
  if (!configuredValue.includes(path.sep) && !configuredValue.includes("/")) {
    return {
      binaryName,
      path: configuredValue,
      source: "env"
    };
  }

  return null;
}

function resolveBundledBinary(binaryName: MediaBinaryName): BinaryResolution | null {
  const runtimePlatform = getRuntimePlatform();
  const runtimeExecutableName = commandNameForPlatform(binaryName, runtimePlatform);
  const runtimeArch = getRuntimeArch();

  // Used by cPanel/standalone drop-in bundles where binaries are shipped outside node_modules.
  const localBundleCandidates = [
    path.join(process.cwd(), ".next", "standalone", "bin", runtimePlatform, runtimeExecutableName),
    path.join(process.cwd(), "bin", runtimePlatform, runtimeExecutableName),
    path.join(process.cwd(), "bin", runtimeExecutableName)
  ];

  for (const localBundleCandidate of localBundleCandidates) {
    if (fileExists(localBundleCandidate)) {
      return {
        binaryName,
        path: localBundleCandidate,
        source: "bundle-local"
      };
    }
  }

  const nodeModulesRoot = path.join(process.cwd(), "node_modules");

  if (binaryName === "ffmpeg") {
    const ffmpegCandidate = path.join(nodeModulesRoot, "ffmpeg-static", runtimeExecutableName);
    if (fileExists(ffmpegCandidate)) {
      return {
        binaryName,
        path: ffmpegCandidate,
        source: "bundled"
      };
    }
  }

  if (binaryName === "ffprobe") {
    const ffprobeExecutableName = commandNameForPlatform("ffprobe", runtimePlatform);
    const archCandidates = [runtimeArch, process.arch, "x64", "ia32", "arm64", "arm"];
    const dedupedArchCandidates = [...new Set(archCandidates)];

    for (const arch of dedupedArchCandidates) {
      const ffprobeCandidate = path.join(
        nodeModulesRoot,
        "ffprobe-static",
        "bin",
        runtimePlatform,
        arch,
        ffprobeExecutableName
      );
      if (fileExists(ffprobeCandidate)) {
        return {
          binaryName,
          path: ffprobeCandidate,
          source: "bundled"
        };
      }
    }
  }

  return null;
}

function resolveFromPath(binaryName: MediaBinaryName): BinaryResolution | null {
  const executableName = commandNameForPlatform(binaryName, getRuntimePlatform());
  const pathEntries = splitPathEntries(process.env.PATH);

  for (const directory of pathEntries) {
    const candidate = path.join(directory, executableName);
    if (fileExists(candidate)) {
      return {
        binaryName,
        path: candidate,
        source: "path"
      };
    }
  }

  return null;
}

function resolveFromWindowsCommonLocations(binaryName: MediaBinaryName): BinaryResolution | null {
  if (getRuntimePlatform() !== "win32") {
    return null;
  }

  const executableName = `${binaryName}.exe`;
  const userHome = process.env.USERPROFILE || "";
  const programFiles = process.env.ProgramFiles || "C:\\Program Files";
  const programFilesX86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
  const chocolateyRoot = process.env.ChocolateyInstall || "C:\\ProgramData\\chocolatey";

  const candidates = [
    path.join(chocolateyRoot, "bin", executableName),
    path.join(programFiles, "ffmpeg", "bin", executableName),
    path.join(programFiles, "FFmpeg", "bin", executableName),
    path.join(programFilesX86, "ffmpeg", "bin", executableName),
    path.join(userHome, "scoop", "shims", executableName),
    path.join(userHome, "scoop", "apps", "ffmpeg", "current", "bin", executableName),
    path.join("C:\\ffmpeg", "bin", executableName)
  ];

  for (const candidate of candidates) {
    if (fileExists(candidate)) {
      return {
        binaryName,
        path: candidate,
        source: "windows-common"
      };
    }
  }

  return null;
}

export function resolveMediaBinaryPath(binaryName: MediaBinaryName): BinaryResolution {
  const fromEnv = resolveFromEnv(binaryName);
  if (fromEnv) {
    return fromEnv;
  }

  const fromPath = resolveFromPath(binaryName);
  if (fromPath) {
    return fromPath;
  }

  const fromBundled = resolveBundledBinary(binaryName);
  if (fromBundled) {
    return fromBundled;
  }

  const fromWindowsCommon = resolveFromWindowsCommonLocations(binaryName);
  if (fromWindowsCommon) {
    return fromWindowsCommon;
  }

  return {
    binaryName,
    path: binaryName,
    source: "fallback"
  };
}
