#!/usr/bin/env bash
# ╔══════════════════════════════════════════════════════════════════╗
# ║  Gateway — One-Shot Bootstrap                                    ║
# ║                                                                  ║
# ║    curl -fsSL https://thegateway.pro/install.sh | bash           ║
# ║                                                                  ║
# ║  Detects arch, fetches and verifies the binary + the full        ║
# ║  installer, then                                                ║
# ║  hands off. Defaults to the `vps` profile because that's what    ║
# ║  curl-pipe-bash is overwhelmingly used for (a fresh Linux VPS    ║
# ║  with no peripherals). RPi operators should run the full         ║
# ║  installer directly so they can pick the rpi profile.            ║
# ╚══════════════════════════════════════════════════════════════════╝
#
# What this script does:
#   1. sudo-elevates if not root
#   2. Picks the right arch (amd64 / arm64)
#   3. Downloads the prebuilt gatewayd binary into /tmp
#   4. Verifies the binary against the release SHA256SUMS manifest
#   5. Downloads the full installer (install-gateway.sh) into /tmp
#   6. Runs install-gateway.sh --profile vps --binary /tmp/gatewayd ...
#
# Flags (passed through pipe by setting env vars before piping, or as
# args when run directly):
#   GATEWAY_PROFILE=vps|rpi|node|full|custom   default: vps
#   GATEWAY_VERSION=latest|vX.Y.Z              default: latest
#   GATEWAY_BASE_URL=https://thegateway.pro    where install-gateway.sh + binary live
#   GATEWAY_FROM_SOURCE=1                      clone repo and `go build` instead of binary download
#   GATEWAY_NONINTERACTIVE=1                   skip every prompt
#   GATEWAY_UPDATE=1                           update mode: replace the gatewayd binary
#                                              ONLY, keep config / db / certs / systemd unit
#                                              in place. Faster than re-running the full
#                                              installer and safe to re-run anytime.
#
# Example (build from source on a VPS):
#   curl -fsSL https://thegateway.pro/install.sh | \
#     GATEWAY_FROM_SOURCE=1 GATEWAY_PROFILE=vps GATEWAY_NONINTERACTIVE=1 bash
#
# Example (update an existing install to the latest binary):
#   curl -fsSL https://thegateway.pro/install.sh | GATEWAY_UPDATE=1 bash
#
# Example (update + pin to a specific release):
#   curl -fsSL https://thegateway.pro/install.sh | \
#     GATEWAY_UPDATE=1 GATEWAY_VERSION=v2.13.1 bash

set -euo pipefail

# ── Colours so curl-pipe-bash shows progress visibly ──
GREEN='\033[0;32m'; YELLOW='\033[0;33m'; RED='\033[0;31m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
log()  { echo -e "${GREEN}[+]${NC} $*"; }
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
err()  { echo -e "${RED}[✗]${NC} $*" >&2; exit 1; }
info() { echo -e "${CYAN}[i]${NC} $*"; }

# ── Sudo escalation when needed ──
# Curl-pipe-bash invocations run as the operator's user; the deeper
# installer needs root for apt, systemd, /etc edits. Re-exec under
# sudo so the operator doesn't have to remember `sudo` in the curl
# line.
if [ "$(id -u)" -ne 0 ]; then
    # `curl … | bash` has no script FILE: $0 is "bash", so `sudo … bash "$0"`
    # would become `sudo … bash bash` and fail. We cannot re-exec a consumed
    # stdin pipe, so give a clear instruction instead of the cryptic failure.
    # The file case (./install.sh) still auto-re-execs under sudo below.
    if [ ! -f "$0" ]; then
        err "This installer needs root. Re-run with sudo, e.g.: curl -fsSL https://thegateway.pro/install.sh | sudo bash"
    fi
    if command -v sudo >/dev/null 2>&1; then
        warn "Re-running under sudo..."
        # `bash -c` keeps env vars (GATEWAY_*) propagated; -E on sudo
        # preserves them through the privilege escalation.
        exec sudo -E env "BASH_ENV=${BASH_ENV:-}" \
            GATEWAY_PROFILE="${GATEWAY_PROFILE:-}" \
            GATEWAY_VERSION="${GATEWAY_VERSION:-}" \
            GATEWAY_BASE_URL="${GATEWAY_BASE_URL:-}" \
            GATEWAY_FROM_SOURCE="${GATEWAY_FROM_SOURCE:-}" \
            GATEWAY_NONINTERACTIVE="${GATEWAY_NONINTERACTIVE:-}" \
            GATEWAY_UPDATE="${GATEWAY_UPDATE:-}" \
            GATEWAY_REPO_URL="${GATEWAY_REPO_URL:-}" \
            bash "$0" "$@"
    else
        err "sudo not available and not running as root"
    fi
fi

# ── Defaults ──
PROFILE="${GATEWAY_PROFILE:-vps}"
VERSION="${GATEWAY_VERSION:-latest}"
BASE_URL="${GATEWAY_BASE_URL:-https://thegateway.pro}"
FROM_SOURCE="${GATEWAY_FROM_SOURCE:-0}"
NONINTERACTIVE="${GATEWAY_NONINTERACTIVE:-0}"
UPDATE_MODE="${GATEWAY_UPDATE:-0}"

# Whitelist of supported profiles so a typo doesn't silently fall back
# to "vps" and surprise the operator. Empty profile means interactive;
# we map that to vps for non-interactive mode below.
case "$PROFILE" in
    ""|vps|rpi|node|full|custom) ;;
    *) err "GATEWAY_PROFILE must be one of: vps rpi node full custom (got: $PROFILE)" ;;
esac

# ── Architecture detection ──
HOST_ARCH=$(uname -m)
case "$HOST_ARCH" in
    x86_64|amd64)   ARCH=amd64 ;;
    aarch64|arm64)  ARCH=arm64 ;;
    *) err "Unsupported architecture: $HOST_ARCH (supported: x86_64, aarch64)" ;;
esac

# ── Sanity: we're on a Linux that the installer supports ──
# Source os-release inside a subshell so its VERSION=<distro-version>
# export doesn't stomp on OUR VERSION (the gateway release ref). The
# subshell prints back only the fields we care about for the warning
# heuristic. Without the subshell, the parent VERSION ended up as
# "24.04.3 LTS (Noble Numbat)" and the binary fetch URL became a
# malformed-input curl rejection.
if [ ! -f /etc/os-release ]; then
    err "Unsupported OS — /etc/os-release missing. The installer targets Debian / Ubuntu."
fi
read OS_ID OS_ID_LIKE OS_PRETTY < <(. /etc/os-release; printf '%s %s %q\n' "${ID:-}" "${ID_LIKE:-}" "${PRETTY_NAME:-unknown}")
case "${OS_ID}${OS_ID_LIKE}" in
    *debian*|*ubuntu*) ;;
    *) warn "Detected OS: ${OS_PRETTY}. Installer is tested on Debian 12+ / Ubuntu 22.04+; YMMV." ;;
esac

log "Gateway bootstrap"
info "  profile:        $PROFILE"
info "  arch:           $ARCH"
info "  from_source:    $([ "$FROM_SOURCE" = "1" ] && echo "yes — will clone repo + go build" || echo "no — download prebuilt binary")"
info "  version:        $VERSION"
info "  base_url:       $BASE_URL"

# ── Workspace ──
WORK=$(mktemp -d -t gateway-bootstrap.XXXXXX)
trap 'rm -rf "$WORK"' EXIT
cd "$WORK"

# ── Required local tooling for the bootstrap itself ──
need_pkg=""
command -v curl >/dev/null 2>&1 || need_pkg="$need_pkg curl"
command -v tar  >/dev/null 2>&1 || need_pkg="$need_pkg tar"
if ! command -v sha256sum >/dev/null 2>&1 && ! command -v shasum >/dev/null 2>&1; then
    need_pkg="$need_pkg coreutils"
fi
if [ -n "$need_pkg" ]; then
    log "Installing bootstrap tools:$need_pkg"
    apt-get update -qq
    apt-get install -y -qq $need_pkg
fi

# ── Fetch the deeper installer ──
INSTALLER="$WORK/install-gateway.sh"
log "Fetching installer from $BASE_URL/install-gateway.sh"
if ! curl -fsSL "$BASE_URL/install-gateway.sh" -o "$INSTALLER"; then
    err "Failed to download install-gateway.sh from $BASE_URL/install-gateway.sh — check connectivity and GATEWAY_BASE_URL"
fi
chmod +x "$INSTALLER"

# ── Acquire the gatewayd binary ──
BINARY_PATH=""
if [ "$FROM_SOURCE" = "1" ]; then
    # Source-build path. Go is installed by install-gateway.sh's
    # install_go() — we just shell that out here directly so the
    # operator's first feedback isn't "what is Go and why am I
    # waiting 6 minutes for it to install".
    log "Source-build mode: installing Go + cloning repo"
    if ! command -v go >/dev/null 2>&1; then
        GO_VER=1.24.2
        curl -fsSL "https://go.dev/dl/go${GO_VER}.linux-${ARCH}.tar.gz" -o /tmp/go.tgz
        rm -rf /usr/local/go
        tar -C /usr/local -xzf /tmp/go.tgz
        rm /tmp/go.tgz
        export PATH=$PATH:/usr/local/go/bin
    fi
    if ! command -v git >/dev/null 2>&1; then
        apt-get install -y -qq git
    fi
    log "Cloning gateway source"
    git clone --depth 1 "${GATEWAY_REPO_URL:-https://github.com/thegateway-pro/gateway.git}" "$WORK/src" \
        || err "git clone failed — set GATEWAY_REPO_URL to a reachable clone URL if the default is wrong"
    cd "$WORK/src"
    log "Building gatewayd (this takes ~2 min on a modest VPS)"
    GIT_COMMIT=$(git rev-parse --short HEAD)
    GIT_VERSION=$(git describe --tags --always 2>/dev/null || echo dev)
    BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
    LDFLAGS="-X main.version=$GIT_VERSION -X main.commit=$GIT_COMMIT -X main.buildDate=$BUILD_DATE"
    GOOS=linux GOARCH="$ARCH" CGO_ENABLED=0 \
        go build -ldflags "$LDFLAGS" -o "$WORK/gatewayd" ./cmd/gatewayd
    BINARY_PATH="$WORK/gatewayd"
    cd "$WORK"
else
    # Prebuilt-binary path. The release artefact lives at
    # $BASE_URL/releases/$VERSION/gatewayd-linux-$ARCH so the operator
    # can self-host releases by mirroring that one URL pattern.
    if [ "$VERSION" = "latest" ]; then
        REL_URL="$BASE_URL/releases/latest/gatewayd-linux-$ARCH"
        MANIFEST_URL="$BASE_URL/releases/latest/SHA256SUMS"
    else
        REL_URL="$BASE_URL/releases/$VERSION/gatewayd-linux-$ARCH"
        MANIFEST_URL="$BASE_URL/releases/$VERSION/SHA256SUMS"
    fi
    log "Downloading binary from $REL_URL"
    if ! curl -fSL "$REL_URL" -o "$WORK/gatewayd"; then
        err "Failed to download $REL_URL — set GATEWAY_FROM_SOURCE=1 to build from source instead"
    fi
    log "Verifying binary checksum from $MANIFEST_URL"
    if ! curl -fsSL "$MANIFEST_URL" -o "$WORK/SHA256SUMS"; then
        err "Failed to download $MANIFEST_URL — refusing to run an unverified binary"
    fi
    expected_hash=$(awk -v file="gatewayd-linux-$ARCH" '$2 == file {print $1}' "$WORK/SHA256SUMS")
    if [ -z "$expected_hash" ]; then
        err "SHA256SUMS does not include gatewayd-linux-$ARCH"
    fi
    if command -v sha256sum >/dev/null 2>&1; then
        actual_hash=$(sha256sum "$WORK/gatewayd" | awk '{print $1}')
    else
        actual_hash=$(shasum -a 256 "$WORK/gatewayd" | awk '{print $1}')
    fi
    if [ "$actual_hash" != "$expected_hash" ]; then
        err "Checksum mismatch for gatewayd-linux-$ARCH — expected $expected_hash, got $actual_hash"
    fi
    info "  sha256:        $actual_hash"
    chmod +x "$WORK/gatewayd"
    BINARY_PATH="$WORK/gatewayd"
fi

# ── Update mode: swap the binary, leave everything else alone ──
#
# The full installer is overkill (and slightly risky — apt updates,
# systemd-unit regeneration, TLS-cert paths) for a routine binary
# bump. Update mode skips all of that and just stops the daemon,
# replaces /usr/local/bin/gatewayd, and starts it again. Bails
# early if there's nothing to update.
if [ "$UPDATE_MODE" = "1" ]; then
    if [ ! -x /usr/local/bin/gatewayd ]; then
        err "GATEWAY_UPDATE=1 set, but /usr/local/bin/gatewayd doesn't exist — run a fresh install first"
    fi
    log "Update mode: swapping gatewayd binary in place"
    old_ver=$(/usr/local/bin/gatewayd --version 2>&1 | head -1 || echo "unknown")
    new_ver=$("$BINARY_PATH" --version 2>&1 | head -1 || echo "unknown")
    info "  current: $old_ver"
    info "  new:     $new_ver"
    if [ "$old_ver" = "$new_ver" ] && [ "$old_ver" != "unknown" ]; then
        info "Versions match — nothing to do (re-run with GATEWAY_VERSION=<ref> to force)"
        exit 0
    fi
    # Snapshot the running binary in case the new one wedges and
    # the operator needs to roll back fast. Same shape as the
    # deploy-pi.sh / deploy-vps.sh inline backup that we've been
    # using by hand all day.
    backup="/usr/local/bin/gatewayd.bak.$(date -u +%Y%m%dT%H%M%SZ)"
    cp /usr/local/bin/gatewayd "$backup"
    info "  backup:  $backup"

    # NEVER SIGKILL — corrupts the SQLite WAL on a write-in-progress
    # and we've burned an hour of recovery time on that before
    # (per project memory feedback_deploy_no_sigkill.md). Graceful
    # stop only.
    systemctl stop gatewayd
    cp "$BINARY_PATH" /usr/local/bin/gatewayd
    chmod +x /usr/local/bin/gatewayd
    systemctl start gatewayd
    sleep 4
    if systemctl is-active gatewayd >/dev/null 2>&1; then
        log "gatewayd updated: $(/usr/local/bin/gatewayd --version 2>&1 | head -1)"
        info "Rollback: sudo systemctl stop gatewayd && sudo cp $backup /usr/local/bin/gatewayd && sudo systemctl start gatewayd"
    else
        warn "gatewayd failed to start with new binary — rolling back to $backup"
        systemctl stop gatewayd
        cp "$backup" /usr/local/bin/gatewayd
        systemctl start gatewayd
        err "Rolled back. Check: journalctl -u gatewayd -n 50"
    fi
    exit 0
fi

# ── Hand off to the full installer ──
INSTALLER_ARGS=( --profile "$PROFILE" --binary "$BINARY_PATH" )
if [ "$NONINTERACTIVE" = "1" ]; then
    INSTALLER_ARGS+=( --non-interactive )
fi
log "Handing off to install-gateway.sh ${INSTALLER_ARGS[*]}"
exec bash "$INSTALLER" "${INSTALLER_ARGS[@]}"
