#!/usr/bin/env bash
# Build the FFmpeg 9.0 commercial/default Neocodec ffmpeg + ffprobe helper pair.
#
# This recipe intentionally builds from the already-pinned corresponding-source archives
# and does NOT compile or link x265. The resulting FFmpeg retains software H.264 (x264),
# software AV1 (SVT-AV1), VP9 (libvpx), Opus, and Apple's H.264 VideoToolbox encoder.
# HEVC encoding and decoding are explicitly disabled. Autodetection,
# network protocols, and capture devices are disabled so an ambient Homebrew library cannot
# silently change the shipped binary.
#
# This file is intentionally inside the published corresponding-source set. The repository
# wrapper at scripts/build-commercial-ffmpeg.sh invokes this canonical recipe and adds the
# separately built/pinned gifsicle helper to the requested output directory.
#
# Usage:
#   ./build-commercial-ffmpeg.sh /absolute/output/directory
#
# The build tree uses a fixed, owner-claimed /private/tmp path because FFmpeg/SVT diagnostic
# strings embed source paths;
# a random path would make otherwise identical binaries hash differently. The script refuses
# a pre-existing path instead of deleting unknown content. It removes its own tree on exit;
# set NEOCODEC_COMMERCIAL_KEEP_BUILD=1 to retain it for diagnosis.
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
SOURCE_SET="$SCRIPT_DIR"
OUTPUT_DIR="${1:-}"
KEEP_BUILD="${NEOCODEC_COMMERCIAL_KEEP_BUILD:-0}"
JOBS="${NEOCODEC_COMMERCIAL_BUILD_JOBS:-8}"
export MACOSX_DEPLOYMENT_TARGET=14.0
export SOURCE_DATE_EPOCH=1785797913

case "$KEEP_BUILD" in
  0|1) ;;
  *) echo "ERROR: NEOCODEC_COMMERCIAL_KEEP_BUILD must be 0 or 1." >&2; exit 1 ;;
esac
case "$JOBS" in
  ''|*[!0-9]*|0) echo "ERROR: NEOCODEC_COMMERCIAL_BUILD_JOBS must be a positive integer." >&2; exit 1 ;;
esac
case "$OUTPUT_DIR" in
  /*) ;;
  '') echo "ERROR: an absolute output directory argument is required." >&2; exit 1 ;;
  *) echo "ERROR: output directory must be absolute: $OUTPUT_DIR" >&2; exit 1 ;;
esac

[ "$(uname -s)" = "Darwin" ] || { echo "ERROR: this helper build requires macOS." >&2; exit 1; }
[ "$(uname -m)" = "arm64" ] || { echo "ERROR: this helper build requires an arm64 Mac." >&2; exit 1; }
for command_name in clang make cmake ninja pkg-config shasum tar lipo otool codesign; do
  command -v "$command_name" >/dev/null 2>&1 || {
    echo "ERROR: required build command '$command_name' is missing." >&2
    exit 1
  }
done

FFMPEG_ARCHIVE="$SOURCE_SET/ffmpeg-9.0.tar.xz"
X264_ARCHIVE="$SOURCE_SET/libs/x264-0480cb05fa188d37ae87e8f4fd8f1aea3711f7ee.tar.gz"
SVT_ARCHIVE="$SOURCE_SET/libs/SVT-AV1-3.1.2.tar.gz"
VPX_ARCHIVE="$SOURCE_SET/libs/libvpx-1.16.0.tar.gz"
OPUS_ARCHIVE="$SOURCE_SET/libs/opus-1.6.1.tar.gz"
for archive in "$FFMPEG_ARCHIVE" "$X264_ARCHIVE" "$SVT_ARCHIVE" \
               "$VPX_ARCHIVE" "$OPUS_ARCHIVE"; do
  [ -f "$archive" ] || {
    echo "ERROR: pinned source archive missing: $archive" >&2
    echo "       Re-populate the source set with its fetch-sources.sh --fetch runbook." >&2
    exit 1
  }
done

verify_archive() {
  local archive="$1"
  local expected="$2"
  local actual
  actual=$(shasum -a 256 "$archive" | awk '{print $1}')
  [ "$actual" = "$expected" ] || {
    echo "ERROR: source checksum mismatch for $archive" >&2
    echo "       expected $expected" >&2
    echo "       actual   $actual" >&2
    exit 1
  }
}
verify_archive "$FFMPEG_ARCHIVE" "7f607a00dd0d28a729d5a4811205812eef01cf6ef6155025febb6f36a9062d52"
verify_archive "$X264_ARCHIVE" "d0967a1348c85dfde363bb52610403be898171493100561efa0dd05d5fd1ae50"
verify_archive "$SVT_ARCHIVE" "d0d73bfea42fdcc1222272bf2b0e2319e9df5574721298090c3d28315586ecb1"
verify_archive "$VPX_ARCHIVE" "7a479a3c66b9f5d5542a4c6a1b7d3768a983b1e5c14c60a9396edc9b649e015c"
verify_archive "$OPUS_ARCHIVE" "6ffcb593207be92584df15b32466ed64bbec99109f007c82205f0194572411a1"

BUILD_ROOT=/private/tmp/neocodec-commercial-ffmpeg-build-1785797913
if ! mkdir -m 700 "$BUILD_ROOT"; then
  echo "ERROR: deterministic build path already exists: $BUILD_ROOT" >&2
  echo "       Refusing to delete or reuse it. Inspect and remove it manually if it is stale." >&2
  exit 1
fi
cleanup() {
  if [ "$KEEP_BUILD" = "1" ]; then
    echo "Retained build tree: $BUILD_ROOT"
  else
    rm -rf "$BUILD_ROOT"
  fi
}
trap cleanup EXIT

mkdir -p "$BUILD_ROOT/src" "$BUILD_ROOT/build" "$BUILD_ROOT/prefix"
tar -xf "$FFMPEG_ARCHIVE" -C "$BUILD_ROOT/src"
tar -xf "$X264_ARCHIVE" -C "$BUILD_ROOT/src"
tar -xf "$SVT_ARCHIVE" -C "$BUILD_ROOT/src"
tar -xf "$VPX_ARCHIVE" -C "$BUILD_ROOT/src"
tar -xf "$OPUS_ARCHIVE" -C "$BUILD_ROOT/src"

FFMPEG_SOURCE="$BUILD_ROOT/src/ffmpeg-9.0"
X264_SOURCE="$BUILD_ROOT/src/x264-0480cb05fa188d37ae87e8f4fd8f1aea3711f7ee"
SVT_SOURCE="$BUILD_ROOT/src/SVT-AV1-v3.1.2"
VPX_SOURCE="$BUILD_ROOT/src/libvpx-1.16.0"
OPUS_SOURCE="$BUILD_ROOT/src/opus-1.6.1"
[ -d "$FFMPEG_SOURCE" ] && [ -d "$X264_SOURCE" ] && [ -d "$SVT_SOURCE" ] \
  && [ -d "$VPX_SOURCE" ] && [ -d "$OPUS_SOURCE" ] || {
  echo "ERROR: a pinned source archive did not extract to the expected directory." >&2
  exit 1
}

echo "Building x264 (pinned 0480cb05)…"
(
  cd "$X264_SOURCE"
  MACOSX_DEPLOYMENT_TARGET=14.0 CC=clang ./configure \
    --prefix="$BUILD_ROOT/prefix" \
    --host=aarch64-apple-darwin \
    --enable-static \
    --disable-cli \
    --disable-opencl \
    --enable-pic
  make -j"$JOBS"
  make install
)

echo "Building SVT-AV1 3.1.2…"
cmake -S "$SVT_SOURCE" -B "$BUILD_ROOT/build/svt-av1" -G Ninja \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_INSTALL_PREFIX="$BUILD_ROOT/prefix" \
  -DCMAKE_OSX_ARCHITECTURES=arm64 \
  -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 \
  -DBUILD_SHARED_LIBS=OFF \
  -DBUILD_APPS=OFF \
  -DBUILD_TESTING=OFF \
  -DEXCLUDE_HASH=ON \
  -DREPRODUCIBLE_BUILDS=ON \
  -DSVT_AV1_LTO=OFF \
  -DENABLE_SVE=OFF \
  -DENABLE_SVE2=OFF
cmake --build "$BUILD_ROOT/build/svt-av1" --parallel "$JOBS"
cmake --install "$BUILD_ROOT/build/svt-av1"

echo "Building libvpx 1.16.0 (VP9 only)…"
mkdir -p "$BUILD_ROOT/build/libvpx"
(
  cd "$BUILD_ROOT/build/libvpx"
  CC=clang MACOSX_DEPLOYMENT_TARGET=14.0 \
    "$VPX_SOURCE/configure" \
    --prefix="$BUILD_ROOT/prefix" \
    --target=arm64-darwin24-gcc \
    --disable-shared \
    --enable-static \
    --enable-pic \
    --disable-examples \
    --disable-tools \
    --disable-docs \
    --disable-vp8 \
    --enable-vp9
  make -j"$JOBS"
  make install
)

echo "Building Opus 1.6.1…"
cmake -S "$OPUS_SOURCE" -B "$BUILD_ROOT/build/opus" -G Ninja \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_INSTALL_PREFIX="$BUILD_ROOT/prefix" \
  -DCMAKE_OSX_ARCHITECTURES=arm64 \
  -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 \
  -DBUILD_SHARED_LIBS=OFF \
  -DBUILD_TESTING=OFF \
  -DOPUS_BUILD_SHARED_LIBRARY=OFF \
  -DOPUS_BUILD_PROGRAMS=OFF \
  -DOPUS_BUILD_TESTING=OFF
cmake --build "$BUILD_ROOT/build/opus" --parallel "$JOBS"
cmake --install "$BUILD_ROOT/build/opus"

echo "Building FFmpeg 9.0 commercial helper pair…"
mkdir -p "$BUILD_ROOT/build/ffmpeg"
(
  cd "$BUILD_ROOT/build/ffmpeg"
  PKG_CONFIG_PATH="../../prefix/lib/pkgconfig" \
  MACOSX_DEPLOYMENT_TARGET=14.0 \
  SOURCE_DATE_EPOCH=1785797913 \
    ../../src/ffmpeg-9.0/configure \
    --prefix=/opt/neocodec-commercial-ffmpeg \
    --arch=arm64 \
    --target-os=darwin \
    --cc=clang \
    --extra-version=neocodec-commercial \
    --pkg-config-flags=--static \
    --extra-cflags=-I../../prefix/include \
    --extra-ldflags=-L../../prefix/lib \
    --disable-shared \
    --enable-static \
    --enable-pic \
    --disable-autodetect \
    --disable-debug \
    --disable-doc \
    --disable-ffplay \
    --disable-network \
    --enable-avdevice \
    --disable-indevs \
    --enable-indev=lavfi \
    --disable-outdevs \
    --enable-gpl \
    --enable-version3 \
    --enable-zlib \
    --enable-libx264 \
    --enable-libsvtav1 \
    --enable-libvpx \
    --enable-libopus \
    --enable-videotoolbox \
    --enable-audiotoolbox \
    --disable-encoder=hevc_videotoolbox \
    --disable-decoder=hevc
  make -j"$JOBS" ffmpeg ffprobe
)

FFMPEG_BIN="$BUILD_ROOT/build/ffmpeg/ffmpeg"
FFPROBE_BIN="$BUILD_ROOT/build/ffmpeg/ffprobe"
FFMPEG_VERSION=$("$FFMPEG_BIN" -hide_banner -version | awk 'NR == 1 { print $3 }')
FFPROBE_VERSION=$("$FFPROBE_BIN" -hide_banner -version | awk 'NR == 1 { print $3 }')
[ "$FFMPEG_VERSION" = "9.0-neocodec-commercial" ] || {
  echo "ERROR: ffmpeg reports '$FFMPEG_VERSION', expected 9.0-neocodec-commercial." >&2
  exit 1
}
[ "$FFPROBE_VERSION" = "$FFMPEG_VERSION" ] || {
  echo "ERROR: ffprobe reports '$FFPROBE_VERSION', expected $FFMPEG_VERSION." >&2
  exit 1
}
for binary in "$FFMPEG_BIN" "$FFPROBE_BIN"; do
  [ -x "$binary" ] || { echo "ERROR: expected output missing: $binary" >&2; exit 1; }
  [ "$(lipo -archs "$binary")" = "arm64" ] || {
    echo "ERROR: helper is not arm64-only: $binary" >&2
    exit 1
  }
  minos=$(otool -l "$binary" | awk '$1 == "minos" { print $2; exit }')
  [ "$minos" = "14.0" ] || {
    echo "ERROR: helper deployment target is '$minos', expected 14.0: $binary" >&2
    exit 1
  }
  linkage=$(otool -L "$binary")
  non_system_linkage=$(sed -n '2,$p' <<< "$linkage" | grep -Ev \
      '^[[:space:]]+(/usr/lib/|/System/Library/Frameworks/)' || true)
  if [ -n "$non_system_linkage" ]; then
    echo "ERROR: helper links a non-system dynamic library: $binary" >&2
    echo "$linkage" >&2
    exit 1
  fi
done

BUILD_CONF=$("$FFMPEG_BIN" -hide_banner -buildconf 2>&1)
ENCODERS=$("$FFMPEG_BIN" -hide_banner -encoders 2>&1)
FILTERS=$("$FFMPEG_BIN" -hide_banner -filters 2>&1)
MUXERS=$("$FFMPEG_BIN" -hide_banner -muxers 2>&1)
DECODERS=$("$FFMPEG_BIN" -hide_banner -decoders 2>&1)

if grep -Fq -- "libx265" <<< "$BUILD_CONF$ENCODERS"; then
  echo "ERROR: commercial helper unexpectedly contains x265 capability." >&2
  exit 1
fi
for flag in --disable-autodetect --disable-network --disable-indevs --enable-indev=lavfi --disable-outdevs \
            --enable-zlib --enable-libx264 --enable-libsvtav1 --enable-libvpx --enable-libopus \
            --enable-videotoolbox --disable-encoder=hevc_videotoolbox --disable-decoder=hevc; do
  grep -Fq -- "$flag" <<< "$BUILD_CONF" || {
    echo "ERROR: expected configure flag absent from built helper: $flag" >&2
    exit 1
  }
done
for encoder in libx264 libsvtav1 libvpx-vp9 libopus aac h264_videotoolbox png; do
  grep -Eq "^[[:space:]]*[VAS][[:graph:]]*[[:space:]]+$encoder([[:space:]]|$)" <<< "$ENCODERS" || {
    echo "ERROR: required encoder absent from built helper: $encoder" >&2
    exit 1
  }
done
if grep -Eq "^[[:space:]]*[VAS][[:graph:]]*[[:space:]]+hevc_videotoolbox([[:space:]]|$)" <<< "$ENCODERS"; then
  echo "ERROR: commercial helper unexpectedly exposes HEVC VideoToolbox encoding." >&2
  exit 1
fi
if grep -Eq "^[[:space:]]*[VAS][[:graph:]]*[[:space:]]+hevc([[:space:]]|$)" <<< "$DECODERS"; then
  echo "ERROR: commercial helper unexpectedly exposes HEVC decoding." >&2
  exit 1
fi
for filter_name in fps palettegen paletteuse scale; do
  grep -Eq "^[[:space:]]*\\.\\.[[:space:]]+$filter_name([[:space:]]|$)" <<< "$FILTERS" || {
    echo "ERROR: required filter absent from built helper: $filter_name" >&2
    exit 1
  }
done
for muxer in gif image2 ipod mp4 webm; do
  grep -Eq "^[[:space:]]*E[[:space:]]+$muxer([[:space:]]|$)" <<< "$MUXERS" || {
    echo "ERROR: required muxer absent from built helper: $muxer" >&2
    exit 1
  }
done

# At-rest signature only. build-app.sh replaces this with the selected app-signing identity.
codesign --force --sign - "$FFMPEG_BIN"
codesign --force --sign - "$FFPROBE_BIN"

mkdir -p "$OUTPUT_DIR"
cp "$FFMPEG_BIN" "$OUTPUT_DIR/ffmpeg"
cp "$FFPROBE_BIN" "$OUTPUT_DIR/ffprobe"
chmod 755 "$OUTPUT_DIR/ffmpeg" "$OUTPUT_DIR/ffprobe"

echo "PASS: commercial FFmpeg/FFprobe report the exact version 9.0-neocodec-commercial."
echo "PASS: commercial FFmpeg helper has no x265 and no HEVC encoder/decoder."
echo "PASS: required H.264/AV1/VP9/Opus/AAC encoders, GIF/MP4/iPod-M4A/WebM muxers, filters, architecture, and linkage verified."
shasum -a 256 "$OUTPUT_DIR/ffmpeg" "$OUTPUT_DIR/ffprobe"
