#!/usr/bin/env bash
# install.sh — one-shot Linux/macOS installer for the tunnel client.
#
# Usage:
#   curl -fsSL https://tnl.voxent.io/install.sh | bash
#
# Pre-set env vars (optional) to skip prompts:
#   REMOTETUNNEL_INVITE=<code>          # signup invite code
#   REMOTETUNNEL_CLIENT_NAME=<name>     # default: hostname
#   REMOTETUNNEL_SSH_PORT=<port>        # if set, auto-installs SSH systemd unit
#
# What it does:
#   1. Detects OS + arch.
#   2. Downloads the matching prebuilt binary from https://tnl.voxent.io/dl/
#   3. Installs to /usr/local/bin (sudo) or ~/.local/bin.
#   4. Asks for the invite code, POSTs to /signup, gets a token back.
#   5. Calls `tunnel login` with the issued token.
#   6. (Linux) offers to install a systemd service for persistent SSH.

set -euo pipefail

SERVER="tnl.voxent.io:7443"
APEX="tnl.voxent.io"
BASE_URL="https://${APEX}"
DOWNLOAD_BASE="${BASE_URL}/dl"
SIGNUP_URL="${BASE_URL}/signup"

cyan()   { printf '\033[36m%s\033[0m\n' "$*"; }
green()  { printf '\033[32m%s\033[0m\n' "$*"; }
red()    { printf '\033[31m%s\033[0m\n' "$*"; }
yellow() { printf '\033[33m%s\033[0m\n' "$*"; }
step()   { echo; cyan "=> $*"; }
ok()     { green   "   $*"; }
bad()    { red     "   $*"; }
warn()   { yellow  "   $*"; }

# Ensure interactive prompts work even when piped from curl.
TTY=""
if [ -r /dev/tty ]; then
    TTY=/dev/tty
fi

prompt() {
    # prompt "question" varname [-s for silent]
    local q="$1"; local var="$2"; local silent="${3:-}"
    if [ -n "$TTY" ]; then
        if [ "$silent" = "-s" ]; then
            printf "%s" "$q" > "$TTY"
            stty -echo < "$TTY"
            IFS= read -r "$var" < "$TTY"
            stty echo < "$TTY"
            echo > "$TTY"
        else
            printf "%s" "$q" > "$TTY"
            IFS= read -r "$var" < "$TTY"
        fi
    else
        printf "%s" "$q"
        if [ "$silent" = "-s" ]; then
            stty -echo
            IFS= read -r "$var"
            stty echo
            echo
        else
            IFS= read -r "$var"
        fi
    fi
}

echo
echo "remotetunnel — Linux/macOS installer"
echo "------------------------------------"

# ---- 0. platform detection ----------------------------------------------
OS_RAW="$(uname -s)"
case "$OS_RAW" in
    Linux)  GOOS=linux ;;
    Darwin) GOOS=darwin ;;
    *) bad "unsupported OS: $OS_RAW"; exit 1 ;;
esac
ARCH_RAW="$(uname -m)"
case "$ARCH_RAW" in
    x86_64|amd64)  GOARCH=amd64 ;;
    aarch64|arm64) GOARCH=arm64 ;;
    armv7l|armv6l) GOARCH=arm ;;
    i?86)          GOARCH=386 ;;
    *) bad "unsupported arch: $ARCH_RAW"; exit 1 ;;
esac
step "Platform: $GOOS/$GOARCH"; ok "detected"

ASSET="tunnel-${GOOS}-${GOARCH}"

# ---- 1. install location -------------------------------------------------
step "Choosing install location"
if [ "$(id -u)" -eq 0 ] || sudo -n true 2>/dev/null; then
    INSTALL_DIR="/usr/local/bin"; NEEDS_SUDO=1
    ok "/usr/local/bin (sudo)"
else
    INSTALL_DIR="$HOME/.local/bin"; NEEDS_SUDO=0
    ok "$HOME/.local/bin (no sudo)"
    mkdir -p "$INSTALL_DIR"
fi
DEST="$INSTALL_DIR/tunnel"

# ---- 2. binary -----------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" 2>/dev/null && pwd || echo .)"
SRC=""
if [ -x "$SCRIPT_DIR/bin/tunnel" ]; then SRC="$SCRIPT_DIR/bin/tunnel"; fi
if [ -x "$SCRIPT_DIR/tunnel" ];      then SRC="$SCRIPT_DIR/tunnel"; fi

if [ -n "$SRC" ]; then
    step "Using local binary $SRC"
else
    step "Downloading $ASSET"
    TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
    SRC="$TMP/tunnel"
    if command -v curl >/dev/null 2>&1; then
        curl -fSL --retry 3 -o "$SRC" "${DOWNLOAD_BASE}/${ASSET}" || { bad "download failed"; exit 1; }
    elif command -v wget >/dev/null 2>&1; then
        wget -q -O "$SRC" "${DOWNLOAD_BASE}/${ASSET}" || { bad "download failed"; exit 1; }
    else
        bad "neither curl nor wget available"; exit 1
    fi
    chmod 0755 "$SRC"
fi

step "Installing $DEST"
if [ "$NEEDS_SUDO" = "1" ]; then sudo install -m 0755 "$SRC" "$DEST"
else                              install -m 0755 "$SRC" "$DEST"
fi
ok "installed"

# If there are existing tunnel-* systemd services from a previous install,
# restart them so they pick up the new binary. Idempotent: no-op if no
# such services exist.
if [ "$GOOS" = "linux" ] && [ "$NEEDS_SUDO" = "1" ] && command -v systemctl >/dev/null 2>&1; then
    UNITS="$(systemctl list-unit-files 'tunnel-*' --no-legend 2>/dev/null | awk '{print $1}' || true)"
    if [ -n "$UNITS" ]; then
        for u in $UNITS; do
            if systemctl is-active "$u" >/dev/null 2>&1; then
                ok "restarting existing service: $u"
                sudo systemctl restart "$u" || warn "restart of $u failed (check journal)"
            fi
        done
    fi
fi

# Confirm the binary works at its full path before relying on PATH.
if ! "$DEST" whoami >/dev/null 2>&1; then
    # whoami exits non-zero if creds aren't set yet — that's expected.
    # Just confirm the file exists and is executable.
    if [ ! -x "$DEST" ]; then
        bad "$DEST is not executable after install"
        exit 1
    fi
fi
ok "binary verified at $DEST"

# Now check whether 'tunnel' (bare name, no path) actually resolves to
# the binary we just installed. Multiple things can break this:
#   1. $INSTALL_DIR is not in PATH at all.
#   2. PATH has /snap/bin or similar BEFORE our install dir.
#   3. The current shell's hash table cached "not found" before we installed.
PATH_OK=0
RESOLVED=""
if command -v tunnel >/dev/null 2>&1; then
    RESOLVED="$(command -v tunnel 2>/dev/null || true)"
    if [ "$RESOLVED" = "$DEST" ]; then
        PATH_OK=1
    fi
fi

if [ "$PATH_OK" != "1" ]; then
    # Best-effort: install a /usr/bin/tunnel symlink so the bare name
    # resolves on Ubuntu/Debian where /usr/bin is on every user's PATH.
    # /usr/bin is BEFORE /snap/bin on the standard PATH, so this also
    # wins against any conflicting snap.
    if [ "$NEEDS_SUDO" = "1" ] && [ "$DEST" != "/usr/bin/tunnel" ]; then
        sudo ln -sf "$DEST" /usr/bin/tunnel 2>/dev/null || true
        if [ -x /usr/bin/tunnel ]; then
            ok "linked /usr/bin/tunnel → $DEST  (overrides any snap with the same name)"
            PATH_OK=1
        fi
    fi
fi

if [ "$PATH_OK" != "1" ]; then
    warn "$INSTALL_DIR is not in your PATH (or 'tunnel' resolves to something else)."
    warn "Add this to your shell rc and re-open the terminal:"
    warn "   export PATH=\"$INSTALL_DIR:\$PATH\""
fi

# ---- 3. signup (or skip if already configured) ---------------------------
NAME="${REMOTETUNNEL_CLIENT_NAME:-$(hostname)}"
INVITE="${REMOTETUNNEL_INVITE:-}"

# Detect existing credentials — skip signup entirely on re-runs.
CRED_FILE=""
if [ -n "${XDG_CONFIG_HOME:-}" ]; then
    CRED_FILE="$XDG_CONFIG_HOME/tunnel/credentials.yaml"
else
    CRED_FILE="$HOME/.config/tunnel/credentials.yaml"
fi
SKIP_SIGNUP=0
if [ -f "$CRED_FILE" ] && grep -q '^token:' "$CRED_FILE" 2>/dev/null; then
    step "Detected existing credentials at $CRED_FILE"
    if "$DEST" whoami >/dev/null 2>&1; then
        ok "credentials look valid — skipping signup"
        SKIP_SIGNUP=1
        # Skip the systemd-restart-on-binary-swap block below by reusing
        # the value of NAME the user originally registered with. We don't
        # have it in the credentials file's token, but `tunnel whoami`
        # surfaces server/apex/client_id/last-seen — credentials.yaml
        # also persists client_id which is enough.
    else
        warn "credentials file exists but is unusable — will re-run signup"
    fi
fi

if [ "$SKIP_SIGNUP" != "1" ]; then
    if [ -z "$INVITE" ]; then
        step "Signup: enter your invite code"
        echo
        prompt "  Invite code: " INVITE
        INVITE="$(echo "$INVITE" | tr -d ' \t\n\r')"
    fi
    if [ -z "$INVITE" ]; then
        bad "no invite code entered"; exit 1
    fi
fi

if [ "$SKIP_SIGNUP" != "1" ]; then
    # do_signup writes "200" or "code" to stdout based on HTTP status, and
    # always saves the JSON response body to $RESP_FILE.
    RESP_FILE="$(mktemp)"
    trap 'rm -f "$RESP_FILE"' EXIT
    do_signup() {
        local n="$1"
        local body
        body="{\"name\":\"${n}\",\"invite_code\":\"${INVITE}\"}"
        curl -sS -o "$RESP_FILE" -w '%{http_code}' \
            -X POST "$SIGNUP_URL" \
            -H 'Content-Type: application/json' \
            --data "$body" 2>/dev/null || true
    }

    step "Creating client \"$NAME\" via $SIGNUP_URL"
    STATUS="$(do_signup "$NAME")"

    # 409 = name already taken. Add a short random suffix and try once more.
    if [ "$STATUS" = "409" ]; then
        SUFFIX="$(printf '%04x' "$$" | tail -c 4)"
        NAME2="${NAME}-${SUFFIX}"
        warn "name \"$NAME\" is taken — retrying as \"$NAME2\""
        NAME="$NAME2"
        STATUS="$(do_signup "$NAME")"
    fi

    # Parse JSON body in $RESP_FILE.
    RESP="$(cat "$RESP_FILE" 2>/dev/null || true)"
    TOKEN="$(printf '%s' "$RESP" | sed -n 's/.*"token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')"
    ERR="$(printf '%s' "$RESP" | sed -n 's/.*"error"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')"

    if [ -z "$TOKEN" ]; then
        case "$STATUS" in
            401) bad "signup failed: invalid invite code" ;;
            409) bad "signup failed: this hostname is already registered (try REMOTETUNNEL_CLIENT_NAME=<unique-name>)" ;;
            404) bad "signup failed: signup is disabled on this server" ;;
            000|"") bad "signup failed: could not reach $SIGNUP_URL" ;;
            *) bad "signup failed (HTTP $STATUS): ${ERR:-$RESP}" ;;
        esac
        exit 1
    fi
    ok "client created as \"$NAME\""

    # ---- 4. login ----------------------------------------------------------
    step "Saving credentials"
    "$DEST" login \
        --token "$TOKEN" \
        --server "$SERVER" \
        --apex "$APEX" \
        --client-id "$NAME"
fi

# ---- 5. (Linux) optional systemd SSH tunnel ------------------------------
# When the user says yes, we:
#   1. ask the server for an unused public port via /api/allocate-tcp-port
#   2. write the systemd unit pointing at that port
#   3. enable + start the service
#   4. emit a copy-ready ~/.ssh/config block for their LAPTOP
SYSTEMD_OFFERED=0
SSH_PORT=""
SSH_USER="$(id -un)"
SSH_HOST_LABEL="$(hostname | tr -cd 'a-z0-9-')"
[ -z "$SSH_HOST_LABEL" ] && SSH_HOST_LABEL="$(echo "$NAME" | tr -cd 'a-z0-9-')"

if [ "$GOOS" = "linux" ] && [ -d /etc/systemd/system ]; then
    DO_SSH=""
    if [ -n "${REMOTETUNNEL_SSH_PORT:-}" ]; then
        DO_SSH="y"
        SSH_PORT="$REMOTETUNNEL_SSH_PORT"
    else
        echo
        cyan "=> Want a persistent SSH tunnel? (survives reboot + crash)"
        echo "   You'll get an 'ssh $SSH_HOST_LABEL' shortcut for your laptop."
        prompt "   Set up persistent SSH? [Y/n] " ANS
        case "$ANS" in n|N|no|NO) DO_SSH="" ;; *) DO_SSH="y" ;; esac
    fi

    if [ "$DO_SSH" = "y" ]; then
        SYSTEMD_OFFERED=1

        # 1. Auto-allocate a port unless the user pre-set one via env.
        if [ -z "$SSH_PORT" ]; then
            step "Asking the server for a free public port"
            TOKEN_VAL="$("$DEST" whoami 2>/dev/null | sed -n 's/^token:.*=\([0-9a-f]\{60,\}\).*/\1/p')"
            # Simpler: re-extract from credentials file directly.
            TOKEN_VAL="$(grep '^token:' "$CRED_FILE" 2>/dev/null | sed -E 's/^token:[[:space:]]*"?([^"[:space:]]+)"?.*/\1/')"
            if [ -z "$TOKEN_VAL" ]; then
                bad "couldn't read token from $CRED_FILE — skipping port allocation"
                SSH_PORT="2222"
            else
                ALLOC_RESP="$(curl -sS -H "Authorization: Bearer $TOKEN_VAL" \
                    "https://${APEX}/api/allocate-tcp-port" 2>/dev/null || true)"
                SSH_PORT="$(printf '%s' "$ALLOC_RESP" | sed -n 's/.*"port"[[:space:]]*:[[:space:]]*\([0-9]\+\).*/\1/p')"
                if [ -z "$SSH_PORT" ]; then
                    bad "could not allocate a port; server said: $ALLOC_RESP"
                    SSH_PORT="2222"
                    warn "falling back to :2222 — may conflict with other clients"
                else
                    ok "server allocated port $SSH_PORT"
                fi
            fi
        fi

        # 2. Install + start the systemd service.
        if [ "$NEEDS_SUDO" = "1" ]; then
            sudo "$DEST" install ssh ssh --port "$SSH_PORT" >/dev/null
            sudo systemctl daemon-reload
            sudo systemctl enable --now tunnel-ssh >/dev/null
            ok "systemd service tunnel-ssh enabled and running"
        else
            warn "Need sudo to install a systemd unit. Run by hand:"
            warn "   sudo $DEST install ssh ssh --port $SSH_PORT"
            warn "   sudo systemctl daemon-reload"
            warn "   sudo systemctl enable --now tunnel-ssh"
        fi
    fi
fi

echo
green "All set."
echo

# If 'tunnel' isn't on PATH in this shell, give the user the full path
# instead — they shouldn't have to know about hash -r or new shells.
if command -v tunnel >/dev/null 2>&1 && [ "$(command -v tunnel)" = "$DEST" ]; then
    BIN="tunnel"
elif [ -x /usr/bin/tunnel ]; then
    BIN="tunnel"
else
    BIN="$DEST"
    warn "Use the full path until you open a new shell:"
    echo
fi

echo "  $BIN http 3000               # expose a local web app"
echo "  $BIN ssh --port 2222         # one-shot SSH tunnel"
echo "  $BIN whoami                  # check your saved credentials"
echo

# If persistent SSH was installed, print the ssh-config block the user
# can paste into ~/.ssh/config on their LAPTOP.
if [ "$SYSTEMD_OFFERED" = "1" ] && [ -n "$SSH_PORT" ]; then
    cyan "=> Persistent SSH is live."
    echo "   From any other machine you SSH from, run:"
    echo
    echo "     ssh -p $SSH_PORT $SSH_USER@$APEX"
    echo
    echo "   Or paste this into ~/.ssh/config on your laptop for"
    echo "   'ssh $SSH_HOST_LABEL' shortcut:"
    echo
    echo "     Host $SSH_HOST_LABEL"
    echo "       HostName $APEX"
    echo "       Port $SSH_PORT"
    echo "       User $SSH_USER"
    echo
    echo "   The systemd service reconnects automatically and survives reboots."
    echo "   Logs:     sudo journalctl -u tunnel-ssh -f"
    echo "   Remove:   sudo $BIN uninstall ssh && sudo systemctl daemon-reload"
    echo
elif [ "$GOOS" = "linux" ]; then
    echo "  # later, to make SSH persistent (auto-allocated port):"
    echo "  curl -fsSL https://$APEX/install.sh | REMOTETUNNEL_SSH_PORT=auto bash"
    echo
fi

# If the user's current shell has 'tunnel' hashed to a previous (missing)
# location, suggest the cheap fix.
if [ "$BIN" = "tunnel" ] && [ -n "${BASH_VERSION:-}" ]; then
    if hash -t tunnel 2>/dev/null | grep -qv "^$DEST$"; then
        echo "  # if 'tunnel: command not found' in your current shell, run:"
        echo "  hash -r"
        echo
    fi
fi
