#!/usr/bin/env bash
# Build Zyryn's pinned LGPL-only Linux libmpv/FFmpeg runtime.
#
# This script intentionally builds only libmpv's external OpenGL renderer plus
# PulseAudio/ALSA output. The app supplies the GL context, while the host keeps
# ownership of its graphics driver ABI. Keeping mpv's native display and GPU
# stacks disabled avoids binding a portable release to the build machine.
set -euo pipefail

readonly FFMPEG_VERSION="6.0"
readonly FFMPEG_URL="https://ffmpeg.org/releases/ffmpeg-6.0.tar.xz"
readonly FFMPEG_SHA256="57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082"
readonly MPV_VERSION="0.36.0"
readonly MPV_URL="https://github.com/mpv-player/mpv/archive/refs/tags/v0.36.0.tar.gz"
readonly MPV_SHA256="29abc44f8ebee013bb2f9fe14d80b30db19b534c679056e4851ceadf5a5e8bf6"
readonly MESON_VERSION="1.2.3"
readonly SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
readonly ROOT="$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)"

RUNTIME_PREFIX="${ZYRYN_LINUX_RUNTIME_PREFIX:-$ROOT/build/linux-media-runtime}"
BUILD_CACHE="${ZYRYN_LINUX_BUILD_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/zyryn-linux-media}"
BUILD_JOBS="${ZYRYN_LINUX_BUILD_JOBS:-4}"
VERIFY_ONLY=0
WORK_DIR=""

fail() {
  echo "ERROR: $*" >&2
  exit 1
}

log() {
  echo "[linux-media] $*"
}

cleanup() {
  if [ -n "$WORK_DIR" ] && [ -d "$WORK_DIR" ]; then
    rm -rf -- "$WORK_DIR"
  fi
}
trap cleanup EXIT INT TERM

usage() {
  printf '%s\n' \
    "Usage: scripts/build_linux_media_runtime.sh [--verify-only] [--prefix PATH]" \
    "" \
    "Environment:" \
    "  ZYRYN_LINUX_RUNTIME_PREFIX  Install prefix (default build/linux-media-runtime)" \
    "  ZYRYN_LINUX_BUILD_CACHE     Download cache" \
    "  ZYRYN_LINUX_BUILD_JOBS      Parallel compiler jobs (default 4)"
}

while [ "$#" -gt 0 ]; do
  case "$1" in
    --verify-only)
      VERIFY_ONLY=1
      shift
      ;;
    --prefix)
      [ "$#" -ge 2 ] || fail "--prefix requires a path."
      RUNTIME_PREFIX="$2"
      shift 2
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      fail "Unknown argument '$1'."
      ;;
  esac
done

case "$BUILD_JOBS" in
  ''|*[!0-9]*|0) fail "ZYRYN_LINUX_BUILD_JOBS must be a positive integer." ;;
esac

require_command() {
  command -v "$1" >/dev/null 2>&1 || fail "Required command '$1' was not found."
}

sha256_of() {
  sha256sum "$1" | awk '{print $1}'
}

download_source() {
  local url="$1" expected_sha="$2" destination="$3" temp_file

  if [ -f "$destination" ] && [ "$(sha256_of "$destination")" = "$expected_sha" ]; then
    log "using cached $(basename -- "$destination")"
    return 0
  fi

  mkdir -p -- "$(dirname -- "$destination")"
  temp_file="$destination.partial"
  rm -f -- "$temp_file"
  curl --fail --location --retry 3 --retry-delay 2 --output "$temp_file" "$url"
  [ "$(sha256_of "$temp_file")" = "$expected_sha" ] || {
    rm -f -- "$temp_file"
    fail "Source checksum mismatch for $url"
  }
  mv -f -- "$temp_file" "$destination"
}

runtime_library() {
  local prefix="$1" name="$2" path
  path="$(find "$prefix" -maxdepth 5 \( -type f -o -type l \) \
    -name "$name" -print -quit 2>/dev/null || true)"
  [ -n "$path" ] || return 1
  readlink -f -- "$path"
}

verify_runtime() {
  local prefix="$1" mpv_library avcodec_library mpv_pc mpv_pc_version config
  local provenance mpv_source_version ffmpeg_source_version
  local required forbidden
  local -a ffmpeg_libraries=(
    libavcodec.so.60
    libavformat.so.60
    libavutil.so.58
    libavfilter.so.9
    libswscale.so.7
    libswresample.so.4
  )
  local -a forbidden_mpv_dependencies=(
    libpipewire-0.3.so.0 libdrm.so.2 libgbm.so.1 libplacebo.so
    libwayland-client.so.0 libEGL.so.1 libvulkan.so.1 libva.so.2
    libvdpau.so.1 libavdevice.so
  )

  [ -d "$prefix" ] || fail "Linux media runtime prefix not found: $prefix"
  mpv_library="$(runtime_library "$prefix" libmpv.so.2 || true)"
  [ -n "$mpv_library" ] || fail "Runtime is missing libmpv.so.2."
  readelf -h "$mpv_library" | grep -Eq \
    'Machine:[[:space:]]*(Advanced Micro Devices X86-64|AMD x86-64)' || \
    fail "libmpv is not x86_64."
  readelf -d "$mpv_library" | grep -Fq 'Library soname: [libmpv.so.2]' || \
    fail "libmpv SONAME is not libmpv.so.2."
  mpv_pc="$(find "$prefix" -maxdepth 6 -type f -path '*/pkgconfig/mpv.pc' \
    -print -quit 2>/dev/null || true)"
  [ -n "$mpv_pc" ] || fail "Runtime is missing mpv.pc."
  mpv_pc_version="$(sed -n 's/^Version:[[:space:]]*//p' "$mpv_pc" | head -1)"
  [ "$mpv_pc_version" = "2.1.0" ] || \
    fail "Unexpected libmpv client API version: $mpv_pc_version"

  for required in "${ffmpeg_libraries[@]}"; do
    [ -n "$(runtime_library "$prefix" "$required" || true)" ] || \
      fail "Runtime is missing pinned FFmpeg library $required."
  done
  avcodec_library="$(runtime_library "$prefix" libavcodec.so.60)"
  config="$(strings "$avcodec_library" | grep -m1 -- '--disable-gpl' || true)"
  [ -n "$config" ] || fail "FFmpeg was not built with --disable-gpl."
  case "$config" in
    *--disable-nonfree*--disable-libdrm*--disable-vaapi*--disable-vdpau*--disable-avdevice*) ;;
    *) fail "FFmpeg configuration is missing a required LGPL/portable disable flag." ;;
  esac
  if printf '%s\n' "$config" | grep -Eq -- \
    '(^|[[:space:]])--enable-(gpl|nonfree)([[:space:]]|$)'; then
    fail "FFmpeg configuration enables GPL or nonfree components."
  fi

  for forbidden in "${forbidden_mpv_dependencies[@]}"; do
    if readelf -d "$mpv_library" | grep -Fq "Shared library: [$forbidden"; then
      fail "Lean libmpv unexpectedly depends on $forbidden."
    fi
  done
  for required in libasound.so.2 libpulse.so.0; do
    readelf -d "$mpv_library" | grep -Fq "Shared library: [$required]" || \
      fail "Lean libmpv is missing required audio backend $required."
  done

  provenance="$prefix/share/zyryn-linux-runtime/BUILD-PROVENANCE.txt"
  mpv_source_version="$prefix/share/zyryn-linux-runtime/mpv-source-VERSION"
  ffmpeg_source_version="$prefix/share/zyryn-linux-runtime/ffmpeg-source-VERSION"
  [ -f "$provenance" ] || \
    fail "Runtime build provenance is missing."
  [ "$(tr -d '\r\n' <"$mpv_source_version" 2>/dev/null || true)" = "$MPV_VERSION" ] || \
    fail "Runtime mpv source VERSION is not pinned $MPV_VERSION."
  [ "$(tr -d '\r\n' <"$ffmpeg_source_version" 2>/dev/null || true)" = "$FFMPEG_VERSION" ] || \
    fail "Runtime FFmpeg source VERSION is not pinned $FFMPEG_VERSION."
  grep -Fqx "mpv version: $MPV_VERSION" "$provenance" || \
    fail "Runtime provenance does not identify mpv $MPV_VERSION."
  grep -Fqx "mpv source SHA-256: $MPV_SHA256" "$provenance" || \
    fail "Runtime provenance has the wrong mpv source checksum."
  grep -Fqx 'mpv Meson option: gpl=false' "$provenance" || \
    fail "Runtime provenance does not attest mpv gpl=false."
  grep -Fqx "FFmpeg version: $FFMPEG_VERSION" "$provenance" || \
    fail "Runtime provenance does not identify FFmpeg $FFMPEG_VERSION."
  grep -Fqx "FFmpeg source SHA-256: $FFMPEG_SHA256" "$provenance" || \
    fail "Runtime provenance has the wrong FFmpeg source checksum."
  log "verified runtime at $prefix"
  log "  libmpv sha256: $(sha256_of "$mpv_library")"
  log "  libavcodec sha256: $(sha256_of "$avcodec_library")"
}

for command_name in awk curl find grep install make mv ninja pkg-config python3 \
  readelf readlink sed sha256sum strings tar tr; do
  require_command "$command_name"
done

if [ "$VERIFY_ONLY" -eq 1 ]; then
  verify_runtime "$RUNTIME_PREFIX"
  exit 0
fi

if [ -d "$RUNTIME_PREFIX" ]; then
  if verify_runtime "$RUNTIME_PREFIX" >/dev/null 2>&1; then
    log "reusing verified runtime at $RUNTIME_PREFIX"
    verify_runtime "$RUNTIME_PREFIX"
    exit 0
  fi
  fail "Refusing to overwrite non-empty or unverified runtime prefix: $RUNTIME_PREFIX"
fi

for module in libass alsa libpulse; do
  pkg-config --exists "$module" || \
    fail "Build dependency '$module' is missing. Install the Ubuntu 22.04 Linux build prerequisites from RELEASING.txt."
done

mkdir -p -- "$BUILD_CACHE"
download_source "$FFMPEG_URL" "$FFMPEG_SHA256" \
  "$BUILD_CACHE/ffmpeg-$FFMPEG_VERSION.tar.xz"
download_source "$MPV_URL" "$MPV_SHA256" \
  "$BUILD_CACHE/mpv-$MPV_VERSION.tar.gz"

WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/zyryn-linux-media.XXXXXX")"
readonly STAGE_PREFIX="$WORK_DIR/install"
readonly FFMPEG_SOURCE="$WORK_DIR/ffmpeg-source"
readonly FFMPEG_BUILD="$WORK_DIR/ffmpeg-build"
readonly MPV_SOURCE="$WORK_DIR/mpv-source"
readonly MPV_BUILD="$WORK_DIR/mpv-build"
readonly MESON_VENV="$WORK_DIR/meson-venv"

mkdir -p "$STAGE_PREFIX" "$FFMPEG_SOURCE" "$FFMPEG_BUILD" "$MPV_SOURCE"
tar -xf "$BUILD_CACHE/ffmpeg-$FFMPEG_VERSION.tar.xz" \
  --strip-components=1 -C "$FFMPEG_SOURCE"
tar -xf "$BUILD_CACHE/mpv-$MPV_VERSION.tar.gz" \
  --strip-components=1 -C "$MPV_SOURCE"

python3 -m venv "$MESON_VENV"
"$MESON_VENV/bin/python" -m pip install --disable-pip-version-check \
  --no-deps "meson==$MESON_VERSION"

log "configuring FFmpeg $FFMPEG_VERSION (LGPL only)"
(
  cd "$FFMPEG_BUILD"
  "$FFMPEG_SOURCE/configure" \
    --prefix="$STAGE_PREFIX" \
    --enable-shared \
    --disable-static \
    --disable-programs \
    --disable-doc \
    --disable-debug \
    --disable-gpl \
    --disable-nonfree \
    --disable-autodetect \
    --enable-openssl \
    --enable-zlib \
    --enable-bzlib \
    --enable-lzma \
    --enable-iconv \
    --disable-libdrm \
    --disable-vaapi \
    --disable-vdpau \
    --disable-avdevice \
    --disable-postproc \
    --enable-pic
  make -j"$BUILD_JOBS"
  make install
)

log "configuring mpv $MPV_VERSION (gpl=false, external OpenGL renderer)"
PKG_CONFIG_PATH="$STAGE_PREFIX/lib/pkgconfig" \
LD_LIBRARY_PATH="$STAGE_PREFIX/lib" \
  "$MESON_VENV/bin/meson" setup "$MPV_BUILD" "$MPV_SOURCE" \
    --prefix="$STAGE_PREFIX" \
    --libdir=lib \
    --buildtype=release \
    --wrap-mode=nodownload \
    --auto-features=disabled \
    -Dgpl=false \
    -Dcplayer=false \
    -Dlibmpv=true \
    -Dbuild-date=false \
    -Dtests=false \
    -Dlua=disabled \
    -Dgl=enabled \
    -Dplain-gl=enabled \
    -Dalsa=enabled \
    -Dpulse=enabled \
    -Diconv=enabled \
    -Dstdatomic=enabled \
    -Dvector=enabled \
    -Dzlib=enabled
PKG_CONFIG_PATH="$STAGE_PREFIX/lib/pkgconfig" \
LD_LIBRARY_PATH="$STAGE_PREFIX/lib" \
  ninja -C "$MPV_BUILD" -j "$BUILD_JOBS"
PKG_CONFIG_PATH="$STAGE_PREFIX/lib/pkgconfig" \
LD_LIBRARY_PATH="$STAGE_PREFIX/lib" \
  ninja -C "$MPV_BUILD" install

mkdir -p "$STAGE_PREFIX/share/zyryn-linux-runtime/licenses"
install -m 0644 "$FFMPEG_SOURCE/COPYING.LGPLv2.1" \
  "$STAGE_PREFIX/share/zyryn-linux-runtime/licenses/FFmpeg-COPYING.LGPLv2.1"
install -m 0644 "$MPV_SOURCE/LICENSE.LGPL" \
  "$STAGE_PREFIX/share/zyryn-linux-runtime/licenses/mpv-LICENSE.LGPL"
install -m 0644 "$MPV_SOURCE/Copyright" \
  "$STAGE_PREFIX/share/zyryn-linux-runtime/licenses/mpv-Copyright"
install -m 0644 "$MPV_SOURCE/VERSION" \
  "$STAGE_PREFIX/share/zyryn-linux-runtime/mpv-source-VERSION"
install -m 0644 "$FFMPEG_SOURCE/VERSION" \
  "$STAGE_PREFIX/share/zyryn-linux-runtime/ffmpeg-source-VERSION"
install -m 0644 "$FFMPEG_BUILD/ffbuild/config.mak" \
  "$STAGE_PREFIX/share/zyryn-linux-runtime/ffmpeg-config.mak"
install -m 0644 "$MPV_BUILD/meson-info/intro-buildoptions.json" \
  "$STAGE_PREFIX/share/zyryn-linux-runtime/mpv-buildoptions.json"

printf '%s\n' \
  'Zyryn portable Linux media runtime' \
  "Build baseline: Ubuntu 22.04 x86_64" \
  "FFmpeg version: $FFMPEG_VERSION" \
  "FFmpeg source: $FFMPEG_URL" \
  "FFmpeg source SHA-256: $FFMPEG_SHA256" \
  'FFmpeg license flags: --disable-gpl --disable-nonfree' \
  "mpv version: $MPV_VERSION" \
  "mpv source: $MPV_URL" \
  "mpv source SHA-256: $MPV_SHA256" \
  'mpv Meson option: gpl=false' \
  'mpv renderer: plain-gl external OpenGL callback' \
  'mpv audio: PulseAudio and ALSA' \
  >"$STAGE_PREFIX/share/zyryn-linux-runtime/BUILD-PROVENANCE.txt"

# pkg-config metadata records the temporary stage path. The shared libraries
# themselves do not embed it, so rewrite only the text metadata before moving.
while IFS= read -r -d '' pc_file; do
  sed -i "s#${STAGE_PREFIX}#${RUNTIME_PREFIX}#g" "$pc_file"
done < <(find "$STAGE_PREFIX" -type f -name '*.pc' -print0)

verify_runtime "$STAGE_PREFIX"
mkdir -p -- "$(dirname -- "$RUNTIME_PREFIX")"
mv -- "$STAGE_PREFIX" "$RUNTIME_PREFIX"
verify_runtime "$RUNTIME_PREFIX"
