#!/usr/bin/env bash
# Build sethLabels AppImages (GUI + batch).
#
# Pipeline (spec §5.3):
#   1. sanity / guardrail / version-compute (same as build-deb.sh)
#   2. out-of-tree cmake build with CMAKE_INSTALL_PREFIX=/usr
#   3. cmake --install to staging AppDir
#   4. linuxdeploy bundle GUI AppImage
#   5. re-stage AppDir for batch-only, linuxdeploy bundle batch AppImage
#   6. inline smoke tests T3, T4
#   7. print artifact paths
#
# Spec: sethlabels-docs/specs/2026-04-29-packaging-design.md §5.3
set -euo pipefail

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$REPO_ROOT"

echo "==> [1/6] Sanity check build host"
"$REPO_ROOT/scripts/lib/deps-debian.sh"
echo "==> [1/6] Strict-zero guardrail"
"$REPO_ROOT/scripts/check-no-upstream-edits.sh"
echo "==> [1/6] Compute version"
VERSION="$("$REPO_ROOT/scripts/compute-version.sh")"
echo "    VERSION = $VERSION"

# Bootstrap linuxdeploy + plugin-qt; defines $LINUXDEPLOY_BIN and $LINUXDEPLOY_PLUGIN_QT_BIN.
# shellcheck disable=SC1091
source "$REPO_ROOT/scripts/lib/linuxdeploy.sh"
# linuxdeploy looks for the plugin in PATH; symlink into the cache dir suffices.
PLUGIN_DIR="$(dirname "$LINUXDEPLOY_PLUGIN_QT_BIN")"
PATH="$PLUGIN_DIR:$PATH"
# Plugin file must be named exactly `linuxdeploy-plugin-qt` (no version suffix).
PLUGIN_LINK="$PLUGIN_DIR/linuxdeploy-plugin-qt"
ln -sf "$LINUXDEPLOY_PLUGIN_QT_BIN" "$PLUGIN_LINK"
chmod +x "$PLUGIN_LINK"
# Debian 13 has Qt6 only; linuxdeploy-plugin-qt's default qmake lookup looks
# for `qmake` (Qt5 era) or `/usr/lib/qt5/bin/qmake` (neither exists). Force it
# to the Qt6 binary explicitly so plugin staging works.
export QMAKE=/usr/bin/qmake6

echo "==> [2/6] Out-of-tree cmake build (install prefix /usr)"
BUILD_DIR="$REPO_ROOT/build/appimage"
APPDIR_GUI="$BUILD_DIR/AppDir-gui"
APPDIR_BATCH="$BUILD_DIR/AppDir-batch"
rm -rf "${BUILD_DIR:?BUILD_DIR must not be empty}"
mkdir -p "$BUILD_DIR"
cmake -S "$REPO_ROOT" -B "$BUILD_DIR" -G Ninja \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_INSTALL_PREFIX=/usr
cmake --build "$BUILD_DIR" --parallel

echo "==> [3/6] Stage install tree to AppDirs"
DESTDIR="$APPDIR_GUI" cmake --install "$BUILD_DIR"
# Batch AppDir gets its own copy so we can prune Qt plugins that GUI needs but batch doesn't.
DESTDIR="$APPDIR_BATCH" cmake --install "$BUILD_DIR"

# Sanity: both AppDirs must contain both binaries (we strip later, not here).
test -x "$APPDIR_GUI/usr/bin/glabels-qt" || { echo "ERROR: GUI binary missing in AppDir-gui" >&2; exit 1; }
test -x "$APPDIR_BATCH/usr/bin/glabels-batch-qt" || { echo "ERROR: batch binary missing in AppDir-batch" >&2; exit 1; }

# Prune the unwanted binary from each AppDir so linuxdeploy bundles only the
# libs needed by the remaining binary (otherwise both AppImages carry both
# binaries; spec §5.3 step 3 implies "AppDir-batch" should be batch-only).
rm "$APPDIR_GUI/usr/bin/glabels-batch-qt"
rm "$APPDIR_BATCH/usr/bin/glabels-qt"
# Also prune the GUI desktop files from the batch AppDir: linuxdeploy's
# --create-desktop-file doesn't suppress pre-existing desktop files, and
# the upstream desktop files reference Exec=glabels-qt (now pruned).
find "$APPDIR_BATCH/usr/share/applications" -name '*.desktop' -delete

echo "==> [4/6] Bundle GUI AppImage"
DESKTOP_FILE="$APPDIR_GUI/usr/share/applications/glabels-qt.desktop"
ICON_FILE="$APPDIR_GUI/usr/share/icons/hicolor/scalable/apps/glabels.svg"
# Upstream's actual desktop filename may vary — list what's there if missing.
if [ ! -f "$DESKTOP_FILE" ]; then
  ALT_DESKTOP=$(find "$APPDIR_GUI/usr/share/applications" -name '*.desktop' | head -1)
  if [ -n "$ALT_DESKTOP" ]; then
    DESKTOP_FILE="$ALT_DESKTOP"
  else
    echo "ERROR: no .desktop file found in $APPDIR_GUI/usr/share/applications" >&2
    exit 1
  fi
fi

cd "$BUILD_DIR"
APPIMAGE_EXTRACT_AND_RUN=1 \
"$LINUXDEPLOY_BIN" \
  --appdir "$APPDIR_GUI" \
  --plugin qt \
  --executable "$APPDIR_GUI/usr/bin/glabels-qt" \
  --desktop-file "$DESKTOP_FILE" \
  --icon-file "$ICON_FILE" \
  --output appimage

# linuxdeploy names the AppImage from the desktop file's Name= field (spaces→underscores).
# For this upstream desktop file (Name=gLabels Label Designer 4) that produces
# gLabels_Label_Designer_4-x86_64.AppImage.  Use a broad glob and exclude *batch*.
GUI_RAW=$(ls "$BUILD_DIR"/*.AppImage 2>/dev/null | grep -v batch | head -1)
GUI_OUT="$REPO_ROOT/sethlabels-gui-${VERSION}-x86_64.AppImage"
mv "$GUI_RAW" "$GUI_OUT"
chmod +x "$GUI_OUT"
cd "$REPO_ROOT"

echo "==> [5/6] Bundle batch AppImage"
# Batch is CLI-only; reuse the upstream SVG icon so --create-desktop-file has
# an icon to reference (linuxdeploy errors if the Icon= entry has no match).
BATCH_ICON_FILE="$APPDIR_BATCH/usr/share/icons/hicolor/scalable/apps/glabels.svg"

[ -f "$BATCH_ICON_FILE" ] || { echo "ERROR: batch icon not found: $BATCH_ICON_FILE" >&2; exit 1; }

cd "$BUILD_DIR"
APPIMAGE_EXTRACT_AND_RUN=1 \
"$LINUXDEPLOY_BIN" \
  --appdir "$APPDIR_BATCH" \
  --plugin qt \
  --executable "$APPDIR_BATCH/usr/bin/glabels-batch-qt" \
  --icon-file "$BATCH_ICON_FILE" \
  --icon-filename glabels-batch-qt \
  --create-desktop-file \
  --output appimage

# linuxdeploy names the batch AppImage using the first desktop file it finds (the upstream
# GUI desktop), producing the same name as the GUI build.  Since we already moved the GUI
# AppImage out, only one .AppImage remains in BUILD_DIR at this point — pick it directly.
BATCH_RAW=$(ls "$BUILD_DIR"/*.AppImage 2>/dev/null | head -1)
BATCH_OUT="$REPO_ROOT/sethlabels-batch-${VERSION}-x86_64.AppImage"
mv "$BATCH_RAW" "$BATCH_OUT"
chmod +x "$BATCH_OUT"
cd "$REPO_ROOT"

echo "==> [6/6] Smoke tests"

# Both AppImages bundle only the xcb Qt platform plugin (linuxdeploy-plugin-qt does not
# include offscreen/minimal).  We need a real X display.  Use Xvfb if available; if not,
# require DISPLAY to be set by the caller.
SMOKE_XVFB_PID=""
if ! xdpyinfo -display "${DISPLAY:-}" >/dev/null 2>&1; then
  if command -v Xvfb >/dev/null 2>&1; then
    echo "    (starting Xvfb :99 for headless smoke tests)"
    Xvfb :99 -screen 0 800x600x24 &
    SMOKE_XVFB_PID=$!
    export DISPLAY=:99
    sleep 1
  else
    echo "WARNING: no DISPLAY and Xvfb not found — smoke tests may fail on xcb platform" >&2
  fi
fi
cleanup_xvfb() { [ -n "$SMOKE_XVFB_PID" ] && kill "$SMOKE_XVFB_PID" 2>/dev/null || true; }
trap cleanup_xvfb EXIT

# T3: batch AppImage --version exits 0 with non-empty output.
echo "    T3: batch --version"
T3_OUT=$(APPIMAGE_EXTRACT_AND_RUN=1 "$BATCH_OUT" --version 2>&1) || {
  echo "ERROR: T3 failed — batch AppImage --version exited non-zero" >&2
  echo "$T3_OUT" >&2
  exit 1
}
# Strip EGL/DRM warnings (libEGL warning: failed to open /dev/dri/...) which are advisory.
T3_VERSION=$(echo "$T3_OUT" | grep -v 'libEGL warning' | head -1)
if [ -z "$T3_VERSION" ]; then
  echo "ERROR: T3 failed — batch AppImage --version produced no version line" >&2
  exit 1
fi
echo "    T3: PASS  ($T3_VERSION)"

# T4: GUI AppImage --help exits 0 under headless Xvfb display.
echo "    T4: gui --help (DISPLAY=$DISPLAY)"
APPIMAGE_EXTRACT_AND_RUN=1 "$GUI_OUT" --help >"$BUILD_DIR/sethlabels-gui-help.txt" 2>&1 || {
  echo "ERROR: T4 failed — GUI AppImage --help exited non-zero" >&2
  cat "$BUILD_DIR/sethlabels-gui-help.txt" >&2
  exit 1
}
echo "    T4: PASS"

echo ""
echo "Artifacts:"
echo "  $GUI_OUT"
echo "  $BATCH_OUT"
