#!/bin/sh
# Remote bootstrap: one command for Windows (Git Bash), Linux, and macOS.
#   curl -fsSL https://remote.ivjn.us/install.sh | bash -s -- agent
#   curl -fsSL https://remote.ivjn.us/install.sh | bash -s -- cli
#
# Agent credentials come from LUCI_DEVICE_ID + LUCI_TOKEN, LUCI_CONFIG, or an
# existing config.json. Values are never printed.
set -eu

BASE="${REMOTE_BASE:-https://remote.ivjn.us}"
# Release archives live on the public R2 host, not the Pages site.
DOWNLOAD_BASE="${REMOTE_DOWNLOAD_BASE:-${REMOTE_BASE:-https://r2.ivjn.us}}"
WANT_AGENT=0
WANT_CLI=0
SYSTEM_FLAG=""
NO_START_FLAG=""
DEVICE_ID="${LUCI_DEVICE_ID:-}"
TOKEN="${LUCI_TOKEN:-}"
SERVER="${LUCI_SERVER:-}"
CONFIG="${LUCI_CONFIG:-}"

die() {
    printf '%s\n' "$*" >&2
    exit 1
}

usage() {
    cat <<'EOF'
Usage:
  curl -fsSL https://remote.ivjn.us/install.sh | bash -s -- agent
  curl -fsSL https://remote.ivjn.us/install.sh | bash -s -- cli

Windows: Git Bash. Linux / macOS: bash or sh.

  agent, remote-agent   Download, install, and start Remote Agent (login autostart)
  cli, remote-cli       Put remote-cli on PATH (~/.local/bin)

Agent needs a device id and token (existing config is enough).
Put them AFTER the pipe (env vars before curl do not reach bash):
  curl -fsSL https://remote.ivjn.us/install.sh | bash -s -- agent --device-id my-pc --token YOUR_TOKEN

Optional flags: --system (Linux systemd, root)  --no-start
EOF
}

while [ "$#" -gt 0 ]; do
    case "$1" in
        -h|--help) usage; exit 0 ;;
        agent|remote-agent) WANT_AGENT=1 ;;
        cli|remote-cli) WANT_CLI=1 ;;
        --system) SYSTEM_FLAG=--system ;;
        --no-start) NO_START_FLAG=--no-start ;;
        --device-id) [ "$#" -ge 2 ] || die "--device-id needs a value"; DEVICE_ID=$2; shift ;;
        --token) [ "$#" -ge 2 ] || die "--token needs a value"; TOKEN=$2; shift ;;
        --server) [ "$#" -ge 2 ] || die "--server needs a value"; SERVER=$2; shift ;;
        --config) [ "$#" -ge 2 ] || die "--config needs a value"; CONFIG=$2; shift ;;
        --) shift; break ;;
        -*) die "unknown flag: $1" ;;
        *) die "unknown argument: $1 (use agent or cli)" ;;
    esac
    shift
done

if [ "$WANT_AGENT" -eq 0 ] && [ "$WANT_CLI" -eq 0 ]; then
    usage >&2
    exit 2
fi

uname_s=$(uname -s 2>/dev/null || printf '%s' unknown)
uname_m=$(uname -m 2>/dev/null || printf '%s' unknown)

case "$uname_s" in
    Linux*) OS=linux ;;
    Darwin*) OS=macos ;;
    MINGW*|MSYS*|CYGWIN*) OS=windows ;;
    *)
        if [ "${OS:-}" = Windows_NT ] || [ -n "${WINDIR:-}" ]; then
            OS=windows
        else
            die "unsupported kernel: $uname_s"
        fi
        ;;
esac

ARCH=x64
case "$uname_m" in
    x86_64|amd64|AMD64) ARCH=x64 ;;
    aarch64|arm64|ARM64) ARCH=arm64 ;;
    *) die "unsupported architecture: $uname_m" ;;
esac
if [ "$OS" = windows ]; then
    case "${PROCESSOR_ARCHITECTURE:-}" in
        ARM64|arm64) ARCH=arm64 ;;
        AMD64|amd64) ARCH=x64 ;;
    esac
fi

ARCHIVE=""
case "$OS-$ARCH" in
    linux-x64) ARCHIVE=remote-linux-x64.tar.gz ;;
    windows-x64) ARCHIVE=remote-windows-x64.zip ;;
    windows-arm64) ARCHIVE=remote-windows-arm64.zip ;;
    macos-arm64) ARCHIVE=remote-macos-arm64.tar.gz ;;
    linux-arm64) die "Linux arm64 packages are not on the site yet. Use an x64 host or build from source." ;;
    macos-x64) die "macOS Intel packages are not on the site yet. Use Apple Silicon or build from source." ;;
    *) die "no published package for $OS/$ARCH" ;;
esac

need_cmd() {
    command -v "$1" >/dev/null 2>&1 || die "need '$1' on PATH"
}

fetch() {
    url=$1
    dest=$2
    if command -v curl >/dev/null 2>&1; then
        curl -fsSL --retry 3 --retry-delay 1 -o "$dest" "$url"
    elif command -v wget >/dev/null 2>&1; then
        wget -q -O "$dest" "$url"
    else
        die "need curl or wget"
    fi
}

file_sha256() {
    f=$1
    if command -v sha256sum >/dev/null 2>&1; then
        sha256sum "$f" | awk '{print $1}'
    elif command -v shasum >/dev/null 2>&1; then
        shasum -a 256 "$f" | awk '{print $1}'
    elif command -v openssl >/dev/null 2>&1; then
        openssl dgst -sha256 "$f" | awk '{print $NF}'
    else
        die "need sha256sum, shasum, or openssl"
    fi
}

WORKDIR=$(mktemp -d "${TMPDIR:-/tmp}/remote-install.XXXXXX")
cleanup() {
    rm -rf "$WORKDIR"
}
trap cleanup EXIT INT HUP TERM

printf 'Remote install: %s/%s -> %s\n' "$OS" "$ARCH" "$ARCHIVE"

fetch "$DOWNLOAD_BASE/downloads/SHA256SUMS.txt" "$WORKDIR/SHA256SUMS.txt"
fetch "$DOWNLOAD_BASE/downloads/$ARCHIVE" "$WORKDIR/$ARCHIVE"

EXPECTED=$(awk -v n="$ARCHIVE" '{
    f=$2
    sub(/^\*/, "", f)
    if (f == n) { print $1; exit }
}' "$WORKDIR/SHA256SUMS.txt")
[ -n "$EXPECTED" ] || die "no sha256 listed for $ARCHIVE"
GOT=$(file_sha256 "$WORKDIR/$ARCHIVE")
GOT_LC=$(printf '%s' "$GOT" | tr 'A-F' 'a-f')
EXP_LC=$(printf '%s' "$EXPECTED" | tr 'A-F' 'a-f')
[ "$GOT_LC" = "$EXP_LC" ] || die "sha256 mismatch for $ARCHIVE"

EXTRACT="$WORKDIR/out"
mkdir -p "$EXTRACT"
case "$ARCHIVE" in
    *.tar.gz)
        need_cmd tar
        tar -xzf "$WORKDIR/$ARCHIVE" -C "$EXTRACT"
        ;;
    *.zip)
        if command -v unzip >/dev/null 2>&1; then
            unzip -qo "$WORKDIR/$ARCHIVE" -d "$EXTRACT"
        elif command -v tar >/dev/null 2>&1; then
            tar -xf "$WORKDIR/$ARCHIVE" -C "$EXTRACT"
        elif [ "$OS" = windows ]; then
            need_cmd powershell.exe
            WIN_ZIP=$(cygpath -w "$WORKDIR/$ARCHIVE" 2>/dev/null || printf '%s' "$WORKDIR/$ARCHIVE")
            WIN_OUT=$(cygpath -w "$EXTRACT" 2>/dev/null || printf '%s' "$EXTRACT")
            powershell.exe -NoProfile -NonInteractive -Command "Expand-Archive -LiteralPath '$WIN_ZIP' -DestinationPath '$WIN_OUT' -Force"
        else
            die "need unzip to extract $ARCHIVE"
        fi
        ;;
    *) die "unknown archive type: $ARCHIVE" ;;
esac

find_bin() {
    name=$1
    if [ -f "$EXTRACT/$name" ]; then
        printf '%s\n' "$EXTRACT/$name"
        return 0
    fi
    if [ -f "$EXTRACT/${name}.exe" ]; then
        printf '%s\n' "$EXTRACT/${name}.exe"
        return 0
    fi
    found=$(find "$EXTRACT" -type f \( -name "$name" -o -name "${name}.exe" \) 2>/dev/null | head -n 1)
    [ -n "$found" ] || return 1
    printf '%s\n' "$found"
}

DEST_BIN="${HOME}/.local/bin"
mkdir -p "$DEST_BIN"

copy_bin() {
    src=$1
    base=$(basename "$src")
    dest="$DEST_BIN/$base"
    cp -f "$src" "$dest"
    chmod 755 "$dest" 2>/dev/null || true
    printf 'installed %s\n' "$dest"
}

ensure_path() {
    case ":$PATH:" in
        *":$DEST_BIN:"*) return 0 ;;
    esac
    marker="# remote.ivjn.us local bin"
    line="export PATH=\"$DEST_BIN:\$PATH\""
    for rc in "$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.profile"; do
        if [ -f "$rc" ] && grep -F "$marker" "$rc" >/dev/null 2>&1; then
            continue
        fi
        if [ -f "$rc" ] || [ "$rc" = "$HOME/.profile" ]; then
            printf '\n%s\n%s\n' "$marker" "$line" >> "$rc"
            printf 'added %s to PATH in %s\n' "$DEST_BIN" "$rc"
        fi
    done
    PATH="$DEST_BIN:$PATH"
    export PATH
}

AGENT_SRC=""
CLI_SRC=""
if AGENT_SRC=$(find_bin remote-agent); then
    :
else
    die "archive is missing remote-agent"
fi
if CLI_SRC=$(find_bin remote-cli); then
    :
else
    die "archive is missing remote-cli"
fi

if [ "$WANT_CLI" -eq 1 ]; then
    copy_bin "$CLI_SRC"
    ensure_path
    printf 'remote-cli is ready. Next: remote-cli login\n'
    if command -v remote-cli >/dev/null 2>&1; then
        remote-cli --version 2>/dev/null || "$DEST_BIN/$(basename "$CLI_SRC")" --version
    else
        "$DEST_BIN/$(basename "$CLI_SRC")" --version
    fi
fi

default_config() {
    if [ "$OS" = windows ]; then
        localapp="${LOCALAPPDATA:-}"
        if [ -z "$localapp" ] && [ -n "${USERPROFILE:-}" ]; then
            localapp="${USERPROFILE}/AppData/Local"
        fi
        if [ -n "$localapp" ]; then
            printf '%s\n' "$localapp/remote-agent/config.json"
            return
        fi
    fi
    printf '%s\n' "${HOME}/.config/remote-agent/config.json"
}

if [ "$WANT_AGENT" -eq 1 ]; then
    copy_bin "$AGENT_SRC"
    ensure_path
    CFG="$CONFIG"
    if [ -z "$CFG" ]; then
        CFG=$(default_config)
    fi
    has_cfg=0
    if [ -n "$CFG" ] && [ -f "$CFG" ]; then
        has_cfg=1
    fi
    if [ -z "$DEVICE_ID" ] && [ -z "$TOKEN" ] && [ "$has_cfg" -eq 0 ]; then
        printf '%s\n' "Agent binary installed, but no device credentials reached this process."
        printf '%s\n' "Env vars before curl do not cross the pipe. Re-run:"
        printf '%s\n' "  curl -fsSL https://remote.ivjn.us/install.sh | bash -s -- agent --device-id NAME --token TOKEN"
        printf '%s\n' "Or, with the binary already installed:"
        printf '%s\n' "  remote-agent install --device-id NAME --token TOKEN"
        printf '%s\n' "Token is never printed back."
        exit 0
    fi
    set -- install --force
    [ -n "$SYSTEM_FLAG" ] && set -- "$@" --system
    [ -n "$NO_START_FLAG" ] && set -- "$@" --no-start
    if [ "$has_cfg" -eq 1 ]; then
        set -- "$@" --config "$CFG"
    fi
    if [ -n "$DEVICE_ID" ]; then
        LUCI_DEVICE_ID=$DEVICE_ID
        export LUCI_DEVICE_ID
    fi
    if [ -n "$TOKEN" ]; then
        LUCI_TOKEN=$TOKEN
        export LUCI_TOKEN
    fi
    if [ -n "$SERVER" ]; then
        LUCI_SERVER=$SERVER
        export LUCI_SERVER
    fi
    printf 'running remote-agent install\n'
    "$AGENT_SRC" "$@"
fi

