#!/usr/bin/env bash
# Provisions a macOS host (Apple Silicon, macOS 14+) as a Gitea Actions runner
# for RustDesk desktop (.dmg) builds. Idempotent: safe to re-run.
#
# Versions are pinned to .gitea/workflows/build-macos.yml. Bump them there and
# here together.
#
# Usage:
#   sudo ./provision.sh \
#     --gitea-url https://gitea.example.com \
#     --runner-token <token>
#
# Toolchains land in /opt/* (chowned to the runner user). Service is installed
# as a LaunchDaemon running as that user.

set -euo pipefail

# ---- pinned versions (mirror .gitea/workflows/build-macos.yml env block) ----
RUST_VERSION="1.81.0"               # MAC_RUST_VERSION upstream (cidre crate needs >=1.81)
FLUTTER_VERSION="3.24.5"            # used for `flutter build macos`
FLUTTER_BRIDGE_VERSION="3.22.3"     # used for `flutter pub get` + flutter_rust_bridge_codegen
VCPKG_COMMIT="120deac3062162151622ca4860575a33844ba10b"
NASM_VERSION="2.16.03"              # 3.x has incompatible CLI; aom/dav1d need 2.x
RUNNER_VERSION="0.2.11"

# ---- defaults ----
RUNNER_NAME="$(hostname -s)-rustdesk"
RUNNER_LABELS=""
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,15p' "$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; }

# ---- arch + macOS version detection ----
ARCH="$(uname -m)"
case "$ARCH" in
    arm64)  HOMEBREW_PREFIX="/opt/homebrew";  ARCH_LABEL="ARM64" ;;
    x86_64) HOMEBREW_PREFIX="/usr/local";     ARCH_LABEL="X64" ;;
    *) echo "Unsupported arch: $ARCH" >&2; exit 1 ;;
esac

OS_MAJOR="$(sw_vers -productVersion | cut -d. -f1)"
[[ "$OS_MAJOR" -ge 14 ]] || {
    echo "WARNING: tested only on macOS 14+. You're on $(sw_vers -productVersion)."
    sleep 3
}
DISTRO_LABEL="macos-${OS_MAJOR}"

if [[ -z "$RUNNER_LABELS" ]]; then
    RUNNER_LABELS="${DISTRO_LABEL},self-hosted,${ARCH_LABEL},macOS"
fi

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

# ---- 1. Xcode Command Line Tools ----
log "Verifying Xcode Command Line Tools"
if ! /usr/bin/xcode-select -p >/dev/null 2>&1; then
    echo "Xcode Command Line Tools not installed. Run:" >&2
    echo "    xcode-select --install" >&2
    echo "Then re-run this script." >&2
    exit 1
fi
echo "    $(xcode-select -p)"

# ---- 2. Homebrew (machine-wide) ----
# Homebrew refuses to install under root (its installer aborts with
# "Don't run this as root!"). It must be installed manually by a regular
# user before this script runs.
log "Verifying Homebrew"
if [[ ! -x "$HOMEBREW_PREFIX/bin/brew" ]]; then
    echo "Homebrew not installed at $HOMEBREW_PREFIX." >&2
    echo "Install it as your regular user (NOT root), then re-run this script:" >&2
    echo "    /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"" >&2
    exit 1
fi
export PATH="$HOMEBREW_PREFIX/bin:$PATH"
echo "    $(brew --version | head -1)"

# brew install must also run as a non-root user. Determine which user invoked
# sudo so we can drop privileges for brew commands below.
BREW_USER="${SUDO_USER:-}"
if [[ -z "$BREW_USER" || "$BREW_USER" == "root" ]]; then
    echo "Could not determine the non-root user that ran sudo (SUDO_USER unset)." >&2
    echo "Re-run with: sudo ./provision.sh ..." >&2
    exit 1
fi
brew_as_user() { sudo -u "$BREW_USER" -H "$HOMEBREW_PREFIX/bin/brew" "$@"; }

# ---- 3. brew packages ----
log "Installing brew packages"
brew_pkgs=(node cocoapods llvm create-dmg pkg-config cmake ninja yasm autoconf automake libtool wget)
for p in "${brew_pkgs[@]}"; do
    if brew_as_user list --versions "$p" >/dev/null 2>&1; then
        echo "    $p (already installed)"
    else
        brew_as_user install "$p"
    fi
done

# ---- 4. NASM 2.16.x (NOT brew's nasm 3.x; aom/dav1d need 2.x) ----
if ! /usr/local/bin/nasm --version 2>/dev/null | grep -q "version $NASM_VERSION"; then
    log "Installing NASM $NASM_VERSION"
    tmp="$(mktemp -d)"
    curl -fsSL -o "$tmp/nasm.zip" \
        "https://www.nasm.us/pub/nasm/releasebuilds/${NASM_VERSION}/macosx/nasm-${NASM_VERSION}-macosx.zip"
    unzip -q "$tmp/nasm.zip" -d "$tmp"
    install -m 0755 "$tmp/nasm-${NASM_VERSION}/nasm" /usr/local/bin/nasm
    rm -rf "$tmp"
fi
/usr/local/bin/nasm --version | head -1

# ---- 5. dedicated runner user ----
if ! /usr/bin/id -u "$SERVICE_USER" >/dev/null 2>&1; then
    log "Creating user $SERVICE_USER"
    # Find an unused UID >= 600
    uid=600
    while dscl . -list /Users UniqueID | awk '{print $2}' | grep -qx "$uid"; do
        uid=$((uid + 1))
    done
    dscl . -create "/Users/$SERVICE_USER"
    dscl . -create "/Users/$SERVICE_USER" UserShell /bin/bash
    dscl . -create "/Users/$SERVICE_USER" RealName "Gitea Runner"
    dscl . -create "/Users/$SERVICE_USER" UniqueID "$uid"
    dscl . -create "/Users/$SERVICE_USER" PrimaryGroupID 20
    dscl . -create "/Users/$SERVICE_USER" NFSHomeDirectory "/Users/$SERVICE_USER"
    mkdir -p "/Users/$SERVICE_USER"
    chown "$SERVICE_USER:staff" "/Users/$SERVICE_USER"
fi
RUNNER_HOME="/Users/$SERVICE_USER"

# ---- 6. 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" aarch64-apple-darwin x86_64-apple-darwin
"$CARGO_HOME/bin/rustup" default "$RUST_VERSION"

# ---- 7. Flutter (two SDKs: 3.24.5 for build, 3.22.3 for bridge gen) ----
# Same rationale as Linux: bridge codegen 1.80.1 + freezed produces broken Dart
# under newer Flutter. Run codegen under 3.22.3, build under 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")"
        # Flutter URL pattern differs between archs: Apple Silicon has an
        # `_arm64_` infix, Intel has no arch infix at all.
        local flutter_url_base="https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos"
        local flutter_url
        case "$ARCH" in
            arm64)  flutter_url="${flutter_url_base}_arm64_${ver}-stable.zip" ;;
            x86_64) flutter_url="${flutter_url_base}_${ver}-stable.zip" ;;
        esac
        curl -fsSL -o "$tmp/flutter.zip" "$flutter_url"
        mkdir -p "$parent"
        unzip -q "$tmp/flutter.zip" -d "$tmp"
        mv "$tmp/flutter" "$dir"
        rm -rf "$tmp"
    fi
    "$dir/bin/flutter" config --no-analytics >/dev/null
    "$dir/bin/flutter" precache --macos >/dev/null
}
install_flutter "$FLUTTER_VERSION"        /opt/flutter
install_flutter "$FLUTTER_BRIDGE_VERSION" /opt/flutter-bridge

# ---- 8. 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

mkdir -p /var/cache/vcpkg
chown -R "$SERVICE_USER:staff" /var/cache/vcpkg

# ---- 9. Permissions ----
log "Setting up permissions for $SERVICE_USER"
chown -R "$SERVICE_USER:staff" "$CARGO_HOME" "$RUSTUP_HOME" \
    /opt/flutter /opt/flutter-bridge "$VCPKG_DIR"
mkdir -p /opt/cargo-tools
chown -R "$SERVICE_USER:staff" /opt/cargo-tools

git config --system --add safe.directory '*' || true

# ---- 10. act_runner ----
RUNNER_DIR="/usr/local/var/gitea-runner"
mkdir -p "$RUNNER_DIR"
chown -R "$SERVICE_USER:staff" "$RUNNER_DIR"

if [[ ! -x "$RUNNER_DIR/act_runner" ]]; then
    log "Downloading act_runner $RUNNER_VERSION"
    case "$ARCH" in
        arm64)  rarch="arm64" ;;
        x86_64) rarch="amd64" ;;
    esac
    curl -fsSL -o "$RUNNER_DIR/act_runner" \
        "https://gitea.com/gitea/act_runner/releases/download/v${RUNNER_VERSION}/act_runner-${RUNNER_VERSION}-darwin-${rarch}"
    chmod +x "$RUNNER_DIR/act_runner"
    chown "$SERVICE_USER:staff" "$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

# ---- 11. launchd service ----
log "Installing LaunchDaemon"
PLIST=/Library/LaunchDaemons/com.rustdesk.gitea-runner.plist
cat > "$PLIST" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.rustdesk.gitea-runner</string>
    <key>UserName</key>
    <string>${SERVICE_USER}</string>
    <key>WorkingDirectory</key>
    <string>${RUNNER_DIR}</string>
    <key>ProgramArguments</key>
    <array>
        <string>${RUNNER_DIR}/act_runner</string>
        <string>daemon</string>
    </array>
    <key>EnvironmentVariables</key>
    <dict>
        <key>RUSTUP_HOME</key>      <string>${RUSTUP_HOME}</string>
        <key>CARGO_HOME</key>       <string>${CARGO_HOME}</string>
        <key>VCPKG_ROOT</key>       <string>${VCPKG_DIR}</string>
        <key>HOMEBREW_PREFIX</key>  <string>${HOMEBREW_PREFIX}</string>
        <key>PATH</key>
        <string>${CARGO_HOME}/bin:/opt/flutter/bin:/opt/cargo-tools/bin:${HOMEBREW_PREFIX}/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
    </dict>
    <key>RunAtLoad</key>      <true/>
    <key>KeepAlive</key>      <true/>
    <key>StandardOutPath</key><string>${RUNNER_DIR}/stdout.log</string>
    <key>StandardErrorPath</key><string>${RUNNER_DIR}/stderr.log</string>
    <key>SoftResourceLimits</key>
    <dict>
        <key>NumberOfFiles</key>  <integer>65535</integer>
    </dict>
</dict>
</plist>
EOF
chmod 0644 "$PLIST"

launchctl bootout system "$PLIST" 2>/dev/null || true
launchctl bootstrap system "$PLIST"
launchctl enable "system/com.rustdesk.gitea-runner"

log "Done."
echo "    Verify with:    sudo launchctl print system/com.rustdesk.gitea-runner | head"
echo "    Tail logs with: tail -F $RUNNER_DIR/stderr.log"
echo "    Runner should appear (online) at $GITEA_URL > Site Admin > Actions > Runners"
