#!/usr/bin/env bash
# Provisions an Ubuntu 22.04 LTS or Debian 13 (Trixie) host as a Gitea Actions
# runner for RustDesk desktop (.deb) builds. Idempotent: safe to re-run.
#
# Versions are pinned to .gitea/workflows/build-linux.yml. Bump them there and
# here together.
#
# Build host vs. user host: the resulting .deb links against the host's glibc.
# Build on the OLDEST distro your users have, otherwise the .deb won't install
# on older systems.
#   - Ubuntu 22.04 build  -> runs on Ubuntu 22.04+, Debian 12+, derivatives
#   - Debian 13   build  -> runs on Debian 13+, Ubuntu 24.04+ only
#
# Usage:
#   sudo ./provision.sh \
#     --gitea-url https://gitea.example.com \
#     --runner-token <token>
#
# All toolchains land in /opt and are readable by the gitea-runner user.
# Service is installed as a systemd unit running as that user.

set -euo pipefail

# ---- pinned versions (mirror .gitea/workflows/build-linux.yml env block) ----
RUST_VERSION="1.75.0"
FLUTTER_VERSION="3.24.5"        # used for `flutter build linux`
FLUTTER_BRIDGE_VERSION="3.22.3" # used for `flutter pub get` + flutter_rust_bridge_codegen
LLVM_VERSION="15.0.6"
VCPKG_COMMIT="120deac3062162151622ca4860575a33844ba10b"
RUNNER_VERSION="0.2.11"

# ---- defaults ----
RUNNER_NAME="$(hostname)-rustdesk"
RUNNER_LABELS=""        # auto-derived from /etc/os-release if empty
SERVICE_USER="gitea-runner"
GITEA_URL=""
RUNNER_TOKEN=""

# ---- arg parse ----
while [[ $# -gt 0 ]]; do
    case "$1" in
        --gitea-url)     GITEA_URL="$2";     shift 2 ;;
        --runner-token)  RUNNER_TOKEN="$2";  shift 2 ;;
        --runner-name)   RUNNER_NAME="$2";   shift 2 ;;
        --runner-labels) RUNNER_LABELS="$2"; shift 2 ;;
        --service-user)  SERVICE_USER="$2";  shift 2 ;;
        -h|--help)
            sed -n '2,18p' "$0"
            exit 0 ;;
        *) echo "Unknown arg: $1" >&2; exit 2 ;;
    esac
done

[[ "$EUID" -eq 0 ]] || { echo "Run as root (use sudo)." >&2; exit 1; }
[[ -n "$GITEA_URL" && -n "$RUNNER_TOKEN" ]] \
    || { echo "Missing --gitea-url or --runner-token" >&2; exit 2; }

. /etc/os-release
case "${ID}-${VERSION_ID:-}" in
    ubuntu-22.04)  DISTRO_LABEL="ubuntu-22.04" ;;
    debian-13|debian-trixie) DISTRO_LABEL="debian-13" ;;
    *)
        echo "WARNING: tested only on Ubuntu 22.04 and Debian 13. You're on $PRETTY_NAME."
        echo "Package names may differ; build outputs may not run on user systems."
        DISTRO_LABEL="${ID}-${VERSION_ID:-unknown}"
        sleep 3 ;;
esac

# If --runner-labels wasn't passed, derive a sensible default that includes the
# detected distro so workflows can target a specific build host when needed.
if [[ -z "$RUNNER_LABELS" ]]; then
    RUNNER_LABELS="${DISTRO_LABEL},self-hosted,X64,Linux"
fi

log() { printf '\n==> %s\n' "$*"; }

# ---- 1. apt packages ----
log "Installing apt packages"
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y --no-install-recommends \
    build-essential clang gcc g++ cmake ninja-build pkg-config nasm yasm \
    autoconf automake libtool libtool-bin \
    libclang-dev llvm-dev \
    libgtk-3-dev libayatana-appindicator3-dev \
    libasound2-dev libpulse-dev libpam0g-dev libssl-dev \
    libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
    libva-dev libxdo-dev libxfixes-dev \
    libxcb-randr0-dev libxcb-shape0-dev libxcb-xfixes0-dev \
    git curl wget zip unzip tar xz-utils ca-certificates \
    python3 python3-pip \
    rpm tree dpkg-dev sudo

# Node.js (act_runner spawns node for JS actions like actions/checkout)
if ! command -v node >/dev/null; then
    log "Installing Node.js LTS"
    curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
    apt-get install -y --no-install-recommends nodejs
fi

# ---- 2. LLVM (binary tarball; libclang-15-dev was dropped from Debian 13) ----
LLVM_DIR="/opt/llvm-${LLVM_VERSION}"
if [[ ! -x "$LLVM_DIR/bin/clang" ]]; then
    log "Installing LLVM $LLVM_VERSION (binary tarball)"
    arch="$(uname -m)"
    case "$arch" in
        x86_64)  llvm_arch="x86_64-linux-gnu-ubuntu-18.04" ;;
        aarch64) llvm_arch="aarch64-linux-gnu" ;;
        *) echo "Unsupported arch for LLVM tarball: $arch" >&2; exit 1 ;;
    esac
    tmp="$(mktemp -d)"
    curl -fsSL -o "$tmp/llvm.tar.xz" \
        "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/clang+llvm-${LLVM_VERSION}-${llvm_arch}.tar.xz"
    mkdir -p "$LLVM_DIR"
    tar --strip-components=1 -xJf "$tmp/llvm.tar.xz" -C "$LLVM_DIR"
    rm -rf "$tmp"
fi

# ---- 3. dedicated runner user ----
if ! id -u "$SERVICE_USER" >/dev/null 2>&1; then
    log "Creating user $SERVICE_USER"
    useradd --system --create-home --shell /bin/bash "$SERVICE_USER"
fi
RUNNER_HOME="$(getent passwd "$SERVICE_USER" | cut -d: -f6)"

# ---- 4. Rust (machine-wide) ----
export RUSTUP_HOME=/opt/rustup
export CARGO_HOME=/opt/cargo
mkdir -p "$RUSTUP_HOME" "$CARGO_HOME"

if [[ ! -x "$CARGO_HOME/bin/rustup" ]]; then
    log "Installing rustup at $RUSTUP_HOME / $CARGO_HOME"
    curl -fsSL https://sh.rustup.rs | RUSTUP_HOME="$RUSTUP_HOME" CARGO_HOME="$CARGO_HOME" \
        sh -s -- -y --default-toolchain none --profile minimal --no-modify-path
fi
"$CARGO_HOME/bin/rustup" toolchain install "$RUST_VERSION" --profile minimal --component rustfmt
"$CARGO_HOME/bin/rustup" target add --toolchain "$RUST_VERSION" x86_64-unknown-linux-gnu
"$CARGO_HOME/bin/rustup" default "$RUST_VERSION"

# ---- 5. Flutter (two SDKs: 3.24.5 for build, 3.22.3 for bridge gen) ----
# Why two: the bridge codegen (flutter_rust_bridge_codegen 1.80.1 + freezed)
# produces broken Dart output when run under newer Flutter SDKs on Linux.
# Upstream's bridge.yml uses 3.22.3 specifically; we mirror that. The .deb
# build itself uses 3.24.5.
install_flutter() {
    local ver="$1" dir="$2"
    if [[ ! -x "$dir/bin/flutter" ]]; then
        log "Installing Flutter $ver -> $dir"
        local tmp; tmp="$(mktemp -d)"
        local parent; parent="$(dirname "$dir")"
        curl -fsSL -o "$tmp/flutter.tar.xz" \
            "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${ver}-stable.tar.xz"
        # Tarball extracts into a top-level `flutter/` dir; rename to target.
        tar -xJf "$tmp/flutter.tar.xz" -C "$tmp"
        mkdir -p "$parent"
        mv "$tmp/flutter" "$dir"
        rm -rf "$tmp"
    fi
    "$dir/bin/flutter" config --no-analytics >/dev/null
    "$dir/bin/flutter" precache --linux >/dev/null
}
install_flutter "$FLUTTER_VERSION"        /opt/flutter
install_flutter "$FLUTTER_BRIDGE_VERSION" /opt/flutter-bridge
FLUTTER_DIR=/opt/flutter

# ---- 6. vcpkg ----
VCPKG_DIR=/opt/vcpkg
if [[ ! -d "$VCPKG_DIR/.git" ]]; then
    log "Cloning vcpkg"
    git clone https://github.com/microsoft/vcpkg.git "$VCPKG_DIR"
fi
git -C "$VCPKG_DIR" fetch --tags origin
git -C "$VCPKG_DIR" -c advice.detachedHead=false checkout "$VCPKG_COMMIT"
[[ -x "$VCPKG_DIR/vcpkg" ]] || "$VCPKG_DIR/bootstrap-vcpkg.sh" -disableMetrics

# vcpkg binary cache (file-backed -- same scheme as build-windows.yml)
mkdir -p /var/cache/vcpkg
chown -R "$SERVICE_USER:$SERVICE_USER" /var/cache/vcpkg

# ---- 7. Permissions ----
log "Setting up permissions for $SERVICE_USER"
chown -R "$SERVICE_USER:$SERVICE_USER" "$CARGO_HOME"
# rustup state needs to be writable too -- toolchain installs touch it.
chown -R "$SERVICE_USER:$SERVICE_USER" "$RUSTUP_HOME"
# Flutter SDK: r/x is enough for builds, but `flutter pub get` writes to its
# own cache subdir so we make it writable as well.
chown -R "$SERVICE_USER:$SERVICE_USER" "$FLUTTER_DIR"
chown -R "$SERVICE_USER:$SERVICE_USER" /opt/flutter-bridge
# vcpkg: builds write under installed/, buildtrees/, etc.
chown -R "$SERVICE_USER:$SERVICE_USER" "$VCPKG_DIR"
# LLVM: read+execute is enough; we never write here at build time.
chown -R "$SERVICE_USER:$SERVICE_USER" "$LLVM_DIR"
# /opt/cargo-tools: workflow installs cargo-expand and flutter_rust_bridge_codegen
# here via `cargo install --root`. Pre-create with the right owner so the first
# job doesn't try to mkdir under root-owned /opt.
mkdir -p /opt/cargo-tools
chown -R "$SERVICE_USER:$SERVICE_USER" /opt/cargo-tools

# git "dubious ownership": same fix as Windows. Trust system-wide.
git config --system --add safe.directory '*' || true

# ---- 8. act_runner ----
RUNNER_DIR=/var/lib/gitea-runner
mkdir -p "$RUNNER_DIR"
chown -R "$SERVICE_USER:$SERVICE_USER" "$RUNNER_DIR"

if [[ ! -x "$RUNNER_DIR/act_runner" ]]; then
    log "Downloading act_runner $RUNNER_VERSION"
    curl -fsSL -o "$RUNNER_DIR/act_runner" \
        "https://gitea.com/gitea/act_runner/releases/download/v${RUNNER_VERSION}/act_runner-${RUNNER_VERSION}-linux-amd64"
    chmod +x "$RUNNER_DIR/act_runner"
    chown "$SERVICE_USER:$SERVICE_USER" "$RUNNER_DIR/act_runner"
fi

if [[ ! -f "$RUNNER_DIR/.runner" ]]; then
    log "Registering runner with $GITEA_URL"
    sudo -u "$SERVICE_USER" -H bash -c "
        cd '$RUNNER_DIR' && \
        ./act_runner register --no-interactive \
            --instance '$GITEA_URL' \
            --token    '$RUNNER_TOKEN' \
            --name     '$RUNNER_NAME' \
            --labels   '$RUNNER_LABELS'
    "
fi

# ---- 9. systemd service ----
log "Installing systemd unit"
cat > /etc/systemd/system/gitea-act-runner.service <<EOF
[Unit]
Description=Gitea Actions runner (RustDesk)
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=${SERVICE_USER}
WorkingDirectory=${RUNNER_DIR}
ExecStart=${RUNNER_DIR}/act_runner daemon
Restart=on-failure
RestartSec=5

# Toolchain locations -- needed because services don't inherit a login shell's PATH.
Environment=RUSTUP_HOME=${RUSTUP_HOME}
Environment=CARGO_HOME=${CARGO_HOME}
Environment=VCPKG_ROOT=${VCPKG_DIR}
Environment=LIBCLANG_PATH=${LLVM_DIR}/lib
Environment=PATH=${CARGO_HOME}/bin:${FLUTTER_DIR}/bin:${LLVM_DIR}/bin:${VCPKG_DIR}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# Resource limits for builds
LimitNOFILE=65535
TasksMax=infinity

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable gitea-act-runner.service
systemctl restart gitea-act-runner.service

log "Done."
echo "    Verify with:    systemctl status gitea-act-runner"
echo "    Tail logs with: journalctl -u gitea-act-runner -f"
echo "    Runner should appear (online) at $GITEA_URL > Site Admin > Actions > Runners"
