#!/usr/bin/env bash
# Copyright 2026, Jiri Eischmann <jiri@eischmann.cz> and the meshy contributors
# SPDX-License-Identifier: GPL-3.0-or-later
set -euo pipefail

APP_ID="page.codeberg.sesivany.Meshy"
APP_NAME="Meshy"
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(CDPATH= cd -- "$SCRIPT_DIR/../.." && pwd)"
BUILD_DIR="$ROOT_DIR/builddir-appimage"
DIST_DIR="$ROOT_DIR/dist"
APPDIR="$DIST_DIR/AppDir"
TOOLS_DIR="$ROOT_DIR/.cache/appimage"
ARCH="${ARCH:-$(uname -m)}"
WITH_QR=1
DOWNLOAD_TOOLS=0
APPDIR_ONLY=0

log_step() {
    printf '\n==> %s\n' "$1"
}

usage() {
    cat <<'EOF'
Usage: ./build-aux/appimage/build-appimage.sh [options]

Options:
  --appdir-only      Build the staged AppDir but skip the final AppImage step
  --build-dir PATH   Meson build directory to use
  --dist-dir PATH    Output directory for AppDir and final AppImage
  --download-tools   Download linuxdeploy into .cache/appimage
  --no-qr            Build without QR scanner support
  -h, --help         Show this help text
EOF
}

while [ "$#" -gt 0 ]; do
    case "$1" in
        --appdir-only)
            APPDIR_ONLY=1
            ;;
        --build-dir)
            BUILD_DIR="$2"
            shift
            ;;
        --dist-dir)
            DIST_DIR="$2"
            APPDIR="$DIST_DIR/AppDir"
            shift
            ;;
        --download-tools)
            DOWNLOAD_TOOLS=1
            ;;
        --no-qr)
            WITH_QR=0
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        *)
            echo "Unknown option: $1" >&2
            usage >&2
            exit 1
            ;;
    esac
    shift
done

if [ "$ARCH" != "x86_64" ]; then
    echo "Only x86_64 AppImage builds are supported by this script right now." >&2
    exit 1
fi

mkdir -p "$DIST_DIR" "$TOOLS_DIR"

require_tool() {
    if ! command -v "$1" >/dev/null 2>&1; then
        echo "Missing required tool: $1" >&2
        exit 1
    fi
}

download_tool() {
    local target="$1"
    local url="$2"
    if [ ! -x "$target" ]; then
        if command -v curl >/dev/null 2>&1; then
            curl -L "$url" -o "$target"
        elif command -v wget >/dev/null 2>&1; then
            wget -O "$target" "$url"
        else
            echo "Missing curl or wget; cannot download $url" >&2
            exit 1
        fi
        chmod +x "$target"
    fi
}

resolve_appimage_tool() {
    local binary_name="$1"
    local appimage_name="$2"
    local url="$3"
    local target="$TOOLS_DIR/$appimage_name"
    if command -v "$binary_name" >/dev/null 2>&1; then
        command -v "$binary_name"
        return
    fi
    if command -v "$appimage_name" >/dev/null 2>&1; then
        command -v "$appimage_name"
        return
    fi
    if [ -x "$target" ]; then
        echo "$target"
        return
    fi
    if [ "$DOWNLOAD_TOOLS" -eq 1 ]; then
        download_tool "$target" "$url"
        echo "$target"
        return
    fi
    echo ""
}

run_appimage_tool() {
    local tool="$1"
    shift
    if [[ "$tool" == *.AppImage ]]; then
        APPIMAGE_EXTRACT_AND_RUN=1 "$tool" "$@"
    else
        "$tool" "$@"
    fi
}

LINUXDEPLOY_BIN="$(resolve_appimage_tool "linuxdeploy" "linuxdeploy-x86_64.AppImage" "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage")"

require_tool python3
require_tool meson
require_tool pkg-config
require_tool glib-compile-schemas

BUILD_PYTHON="$(command -v python3)"
SYSTEM_PYTHON="/usr/bin/python3"
if [ ! -x "$SYSTEM_PYTHON" ]; then
    echo "Missing required system interpreter: $SYSTEM_PYTHON" >&2
    exit 1
fi

TYPELIB_DIR="$(pkg-config --variable=typelibdir gobject-introspection-1.0)"

LDCONFIG_BIN="$(command -v ldconfig || true)"
if [ -z "$LDCONFIG_BIN" ]; then
    for candidate in /sbin/ldconfig /usr/sbin/ldconfig; do
        if [ -x "$candidate" ]; then
            LDCONFIG_BIN="$candidate"
            break
        fi
    done
fi
if [ -z "$LDCONFIG_BIN" ]; then
    echo "Missing required tool: ldconfig" >&2
    exit 1
fi

if [ -z "$LINUXDEPLOY_BIN" ]; then
    echo "Missing linuxdeploy. Install it or rerun with --download-tools." >&2
    exit 1
fi

QROPT="-Dqr_scanner=true"
if [ "$WITH_QR" -ne 1 ]; then
    QROPT="-Dqr_scanner=false"
fi

log_step "Preparing build directories"
rm -rf "$APPDIR" "$BUILD_DIR"

log_step "Configuring Meson"
meson setup "$BUILD_DIR" --prefix=/usr --buildtype=release "$QROPT"
log_step "Compiling project"
meson compile -C "$BUILD_DIR"
log_step "Installing into AppDir"
DESTDIR="$APPDIR" meson install -C "$BUILD_DIR"

log_step "Installing AppImage launcher and metadata"
cp "$SCRIPT_DIR/AppRun" "$APPDIR/AppRun"
chmod +x "$APPDIR/AppRun"

ln -srf "$APPDIR/usr/share/applications/$APP_ID.desktop" "$APPDIR/$APP_ID.desktop"
ln -srf "$APPDIR/usr/share/icons/hicolor/256x256/apps/$APP_ID.png" "$APPDIR/$APP_ID.png"
ln -srf "$APPDIR/usr/share/icons/hicolor/256x256/apps/$APP_ID.png" "$APPDIR/.DirIcon"

SCHEMA_DIR="$APPDIR/usr/share/glib-2.0/schemas"
log_step "Compiling GSettings schemas"
glib-compile-schemas "$SCHEMA_DIR"

PYTHON_VERSION="$("$SYSTEM_PYTHON" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')"
PYTHON_STDLIB="$("$SYSTEM_PYTHON" -c 'import sysconfig; print(sysconfig.get_path("stdlib"))')"
TARGET_PYTHON_DIR="$APPDIR/usr/lib/python$PYTHON_VERSION"
TARGET_SITE_PACKAGES="$TARGET_PYTHON_DIR/site-packages"

mkdir -p "$APPDIR/usr/bin" "$TARGET_SITE_PACKAGES" "$APPDIR/usr/lib/girepository-1.0"
log_step "Copying Python runtime"
cp -aL "$SYSTEM_PYTHON" "$APPDIR/usr/bin/python3"
cp -a "$PYTHON_STDLIB" "$APPDIR/usr/lib/"

copy_python_module() {
    local interpreter="$1"
    local module="$2"
    "$interpreter" - "$module" "$TARGET_SITE_PACKAGES" <<'PY'
import importlib.util
import pathlib
import shutil
import sys

module = sys.argv[1]
target_site = pathlib.Path(sys.argv[2])
spec = importlib.util.find_spec(module)
if spec is None or spec.origin is None:
    print(f"Missing Python module {module}", file=sys.stderr)
    sys.exit(1)
origin = pathlib.Path(spec.origin)
if origin.name == "__init__.py":
    dest = target_site / module
    shutil.copytree(origin.parent, dest, dirs_exist_ok=True)
else:
    dest = target_site / origin.name
    dest.parent.mkdir(parents=True, exist_ok=True)
    shutil.copy2(origin, dest)
PY
}

copy_python_module "$BUILD_PYTHON" "Crypto"
copy_python_module "$BUILD_PYTHON" "serial"
copy_python_module "$BUILD_PYTHON" "segno"
copy_python_module "$SYSTEM_PYTHON" "gi"
copy_python_module "$SYSTEM_PYTHON" "cairo"
if [ "$WITH_QR" -eq 1 ]; then
    copy_python_module "$BUILD_PYTHON" "pyzbar"
fi

log_step "Collecting GObject introspection data"
copy_typelib() {
    local typelib="$1"
    local src="$TYPELIB_DIR/$typelib.typelib"
    if [ -f "$src" ]; then
        cp -a "$src" "$APPDIR/usr/lib/girepository-1.0/"
    fi
}

copy_typelib "Gtk-4.0"
copy_typelib "Adw-1"
copy_typelib "Gio-2.0"
copy_typelib "GLib-2.0"
copy_typelib "GdkPixbuf-2.0"
copy_typelib "Shumate-1.0"
copy_typelib "Geoclue-2.0"
copy_typelib "Gdk-4.0"
copy_typelib "GObject-2.0"

log_step "Bundling shared libraries with linuxdeploy"
ARCH="$ARCH" run_appimage_tool "$LINUXDEPLOY_BIN" \
    --appdir "$APPDIR" \
    --executable "$APPDIR/usr/bin/python3" \
    --desktop-file "$APPDIR/usr/share/applications/$APP_ID.desktop" \
    --icon-file "$APPDIR/usr/share/icons/hicolor/256x256/apps/$APP_ID.png"

VERSION="$(date +%Y%m%d)"

APPIMAGE_PATH="$DIST_DIR/${APP_NAME}-${VERSION}-${ARCH}.AppImage"
if [ "$APPDIR_ONLY" -eq 1 ]; then
    exit 0
fi

log_step "Building final AppImage"
(cd "$DIST_DIR" && \
    ARCH="$ARCH" \
    LDAI_OUTPUT="$(basename "$APPIMAGE_PATH")" \
    run_appimage_tool "$LINUXDEPLOY_BIN" \
        --appdir "$APPDIR" \
        --executable "$APPDIR/usr/bin/python3" \
        --desktop-file "$APPDIR/usr/share/applications/$APP_ID.desktop" \
        --icon-file "$APPDIR/usr/share/icons/hicolor/256x256/apps/$APP_ID.png" \
        --output appimage)

log_step "Done"
echo "AppImage written to $APPIMAGE_PATH"
