#!/bin/bash
# Benchday Daemon One-Liner Install Script
#
# Usage:
#   curl -fsSL https://benchday.zztech.io/install.sh | bash -s -- [--enrollment-code <code>]
#
# One idempotent command, safe to re-run. It auto-resolves what to do:
#   • code given           → enroll a new machine, or additively grant another
#                            account on an existing one (idempotent on the hub).
#   • no code, already set  → reuse the existing machine identity (silent re-install).
#   • no code, brand-new    → QR pairing to bootstrap trust (no code needed).
#
# This script:
#   1. Detects OS/arch
#   2. Downloads the latest benchday-daemon binary
#   3. Reuses or generates the machine's ed25519 keypair (the real authenticator)
#   4. Configures outbound mode to connect to the hub
#   5. Bootstraps the access grant (code or QR) only when the machine has no identity yet
#   6. Installs and starts a systemd/launchd service
#
# Get an enrollment code from the mobile app (Account → Add Machine), or omit it
# to QR-pair / re-install.

set -euo pipefail

# ── Configuration ───────────────────────────────────────────────────────

HUB_URL=""
ENROLLMENT_CODE=""
DAEMON_ID=""
CAPABILITY_PUBLIC_KEY=""
TMUX_PATH="${BENCHDAY_TMUX_PATH:-}"
INSTALL_DIR="/usr/local/bin"
CONFIG_DIR="${HOME}/.config/benchday-daemon"
SERVICE_NAME="benchday-daemon"
NO_SERVICE=0
VERIFY_TIMEOUT_SECONDS="${BENCHDAY_INSTALL_VERIFY_TIMEOUT_SECONDS:-120}"
DIRECT_WS_PORT="${BENCHDAY_DAEMON_DIRECT_WS_PORT:-7778}"
PAIR_MODE=0
FORCE_USER_SCOPE=0
DAEMON_ID_EXPLICIT=0

# ── Args ────────────────────────────────────────────────────────────────

while [ $# -gt 0 ]; do
  case "$1" in
    --hub-url)         HUB_URL="$2"; shift 2;;
    --enrollment-code) ENROLLMENT_CODE="$2"; shift 2;;
    --daemon-id)       DAEMON_ID="$2"; DAEMON_ID_EXPLICIT=1; shift 2;;
    --capability-public-key) CAPABILITY_PUBLIC_KEY="$2"; shift 2;;
    --tmux-path)       TMUX_PATH="$2"; shift 2;;
    --install-dir)     INSTALL_DIR="$2"; shift 2;;
    --no-service)      NO_SERVICE=1; shift;;
    --user-service)    FORCE_USER_SCOPE=1; shift;;
    --pair)            PAIR_MODE=1; shift;;
    -h|--help)
      cat <<'EOF'
Usage: install.sh [options]

The same command is safe to re-run. With no enrollment options it auto-detects:
re-install (reuse the existing machine identity) if a daemon is already set up,
or QR pairing to bootstrap a brand-new machine.

Enrollment (optional — auto-detected if omitted):
  --enrollment-code <code> One-time code from the app (Account → Add Machine).
                           Enrolls a new machine, or additively grants another
                           account access to an existing one. Idempotent: safe to
                           pass on a machine that is already enrolled.
  --pair                   Force host-initiated QR pairing instead of a code.

Optional:
  --hub-url <url>         Hub WebSocket endpoint (default: wss://benchday.zztech.io/daemon)
  --daemon-id <id>        Machine identifier (default: existing daemon's id if one
                          is already installed, else the hostname). Forces a fresh
                          daemon with this id even when one already exists.
  --capability-public-key <key>
                           Hub Ed25519 public key for daemon capability auth
  --tmux-path <path>       tmux executable path (default: auto-detect)
  --install-dir <path>    Binary install location (default: /usr/local/bin)
  --no-service            Start the daemon directly instead of installing a system service
  --user-service          Install a SEPARATE per-user service + per-user daemon-id,
                          for the genuine case of two independent OS users sharing
                          one host with separate per-UID tmux servers. NOT needed
                          to give another account access to the same machine — for
                          that, just re-run with that account's --enrollment-code
                          and this installer adds it to the existing daemon.
  -h, --help              Show this help

Examples:
  # With a code from the app — also the command shown in Account → Add Machine.
  # Works for a new machine, a re-install, or granting another account.
  curl -fsSL https://benchday.zztech.io/install.sh | bash -s -- \
    --enrollment-code abc123def456

  # No code: re-installs an existing machine, or QR-pairs a brand-new one.
  curl -fsSL https://benchday.zztech.io/install.sh | bash
EOF
      exit 0
      ;;
    *)
      echo "ERROR: unknown argument: $1" >&2
      echo "Try --help" >&2
      exit 2
      ;;
  esac
done

# ── Validation ──────────────────────────────────────────────────────────

if [ -z "$HUB_URL" ]; then
  HUB_URL="wss://benchday.zztech.io/daemon"
fi

# The installer has ONE idempotent behavior, not several modes. There are only
# two underlying concepts:
#   • machine identity — a persistent Ed25519 key, generated once, NOT tied to
#     any account. This key (not the code) is what authenticates the daemon to
#     the hub on every reconnect.
#   • access grant — "account X may use this machine", bootstrapped once by an
#     enrollment code (or a QR pair). The hub treats redeeming a code the owner
#     already has as a no-op, so re-running is always safe.
# From those, the single command resolves itself:
#   • code given            → enroll a new machine, or additively grant a new
#                             account on an existing one (idempotent on the hub).
#   • no code, key present   → reuse the existing identity (silent re-install).
#   • no code, no identity   → fall back to QR pairing (below) — the zero-code
#                             bootstrap — instead of dead-ending on an error.
# So the same one-liner always makes progress; the caller never has to pick a mode.
EXISTING_DAEMON_PRESENT=0
mark_existing_if() { if [ -e "$1" ]; then EXISTING_DAEMON_PRESENT=1; fi; }
# Standard standalone + supervisor (mesh-deploy) layouts...
mark_existing_if "${CONFIG_DIR}/benchday-daemon.toml"
mark_existing_if "${CONFIG_DIR}/key"
mark_existing_if "${HOME}/.config/benchday-daemon-supervisor.toml"
mark_existing_if "${HOME}/.config/benchday-daemon.worker.toml"
# ...plus the supervisor state dir and any off-default config name, so a re-run
# on a box that was set up before is recognized even if its config moved. An
# unmatched glob stays literal and the -e test below simply fails — safe.
mark_existing_if "${HOME}/.local/state/benchday-daemon-supervisor"
for _cfg in "${HOME}"/.config/benchday-daemon*.toml; do
  mark_existing_if "$_cfg"
done
unset -f mark_existing_if

if [ -z "$ENROLLMENT_CODE" ] && [ "$PAIR_MODE" != 1 ] && [ "$EXISTING_DAEMON_PRESENT" != 1 ]; then
  # Brand-new machine with no identity yet: bootstrap trust via QR pairing rather
  # than erroring out. Pass --enrollment-code <code> from the app to skip the QR.
  echo "No enrollment code and no existing daemon on this machine — starting QR pairing."
  echo "(Tip: pass --enrollment-code <code> from the app, Account → Add Machine, to skip the QR.)"
  PAIR_MODE=1
fi
if [ -z "$ENROLLMENT_CODE" ] && [ "$EXISTING_DAEMON_PRESENT" = 1 ]; then
  echo "  Re-install on an already-enrolled machine: reusing the existing key/identity (no code needed)."
fi

# ── Existing-daemon detection (idempotent install + add-account) ───────────
# A machine runs a SINGLE daemon with a single machine identity (one keypair,
# not tied to any one account). Additional accounts gain access by redeeming an
# enrollment code that the operator runs on the box — the daemon presents the
# code to the hub, which adds an access grant (no second daemon, no rename).
#
# So a re-run of this installer on a machine that already has a daemon must be
# idempotent: reuse the existing daemon_id, and if a new enrollment code was
# given, hand it to the existing daemon ("add account") instead of forking a
# second daemon. We look in BOTH known config layouts: the supervisor layout
# (deploy-mesh: ~/.config/benchday-daemon-supervisor.toml -> worker template)
# and the standalone layout this installer writes.
THIS_USER="$(id -un 2>/dev/null || whoami)"
CONFIG_PATH="${CONFIG_DIR}/benchday-daemon.toml"

read_toml_value() {  # $1=file $2=key  → prints first matching value (unquoted)
  [ -f "$1" ] || return 0
  grep -E "^${2}[[:space:]]*=" "$1" 2>/dev/null | head -n1 \
    | sed -E "s/^${2}[[:space:]]*=[[:space:]]*\"?([^\"]*)\"?.*/\1/"
}

EXISTING_LAYOUT=""      # supervisor | standalone | ""
EXISTING_CONFIG=""      # the file whose [outbound] holds daemon_id / enrollment_code
EXISTING_DAEMON_ID=""
SUPERVISOR_CONFIG="${HOME}/.config/benchday-daemon-supervisor.toml"
if [ -f "$SUPERVISOR_CONFIG" ]; then
  EXISTING_LAYOUT="supervisor"
  EXISTING_CONFIG="$(read_toml_value "$SUPERVISOR_CONFIG" worker_config_template)"
  [ -z "$EXISTING_CONFIG" ] && EXISTING_CONFIG="${HOME}/.config/benchday-daemon.worker.toml"
  EXISTING_DAEMON_ID="$(read_toml_value "$EXISTING_CONFIG" daemon_id)"
elif [ -f "$CONFIG_PATH" ]; then
  EXISTING_LAYOUT="standalone"
  EXISTING_CONFIG="$CONFIG_PATH"
  EXISTING_DAEMON_ID="$(read_toml_value "$CONFIG_PATH" daemon_id)"
fi

# Add-account mode: an existing daemon was found and the caller did not force a
# brand-new identity (--daemon-id) or a separate per-user daemon (--user-service).
ADD_ACCOUNT=0
if [ -n "$EXISTING_DAEMON_ID" ] && [ "$DAEMON_ID_EXPLICIT" != 1 ] && [ "$FORCE_USER_SCOPE" != 1 ]; then
  ADD_ACCOUNT=1
  DAEMON_ID="$EXISTING_DAEMON_ID"
fi

# Per-user scope is now EXPLICIT only (--user-service), for the genuine case of
# two independent OS users sharing one host with separate per-UID tmux servers.
# The old auto-detection ("binary present + no standalone config = secondary
# user") misfired on the supervisor layout and on same-user re-runs, minting a
# phantom <hostname>-<user> daemon that never connected.
USER_SCOPED="$FORCE_USER_SCOPE"
if [ "$USER_SCOPED" = 1 ]; then
  SERVICE_NAME="benchday-daemon-${THIS_USER}"
fi

if [ -z "$DAEMON_ID" ]; then
  # Reinstall in the standalone layout: reuse the daemon_id already enrolled.
  if [ -f "$CONFIG_PATH" ]; then
    DAEMON_ID="$(read_toml_value "$CONFIG_PATH" daemon_id)"
  fi
  if [ -z "$DAEMON_ID" ]; then
    DAEMON_ID="$(hostname -s 2>/dev/null || hostname | cut -d. -f1)"
    # Explicit per-user daemon on a shared host: disambiguate so the hub's
    # globally-unique daemon_id does not collide with the first user's daemon.
    if [ "$USER_SCOPED" = 1 ]; then
      DAEMON_ID="${DAEMON_ID}-${THIS_USER}"
    fi
  fi
fi

# Sanitize daemon_id: lowercase, alphanumeric + hyphen/underscore only
DAEMON_ID="$(echo "$DAEMON_ID" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_-]/-/g' | sed 's/^-*//;s/-*$//')"
if [ -z "$DAEMON_ID" ]; then
  DAEMON_ID="benchday-$(date +%s | tail -c 5)"
fi

echo "=== Benchday Daemon Installer ==="
echo ""
echo "  Machine ID:   $DAEMON_ID"
echo "  Hub URL:      $HUB_URL"
echo "  Install dir:  $INSTALL_DIR"
if [ "$ADD_ACCOUNT" = 1 ]; then
  echo "  Mode:         add account to existing daemon (${EXISTING_LAYOUT} layout)"
fi
if [ "$USER_SCOPED" = 1 ]; then
  echo "  Scope:        per-user ($THIS_USER) — coexisting with another user on this host"
fi
echo ""

# ── Detect OS/arch ──────────────────────────────────────────────────────

UNAME_S="$(uname -s)"
UNAME_M="$(uname -m)"

case "$UNAME_S" in
  Linux)  OS="linux";;
  Darwin) OS="macos";;
  *)      echo "ERROR: unsupported OS: $UNAME_S" >&2; exit 3;;
esac

case "$UNAME_M" in
  x86_64)  ARCH="x86_64";;
  amd64)   ARCH="x86_64";;
  arm64)   ARCH="aarch64";;
  aarch64) ARCH="aarch64";;
  *)       echo "ERROR: unsupported architecture: $UNAME_M" >&2; exit 3;;
esac

echo "  OS: $OS, Arch: $ARCH"

# ── Detect tmux ────────────────────────────────────────────────────────

if [ -z "$TMUX_PATH" ]; then
  if command -v tmux >/dev/null 2>&1; then
    TMUX_PATH="$(command -v tmux)"
  else
    for candidate in /opt/homebrew/bin/tmux /usr/local/bin/tmux /usr/bin/tmux /bin/tmux; do
      if [ -x "$candidate" ]; then
        TMUX_PATH="$candidate"
        break
      fi
    done
  fi
fi

if [ -z "$TMUX_PATH" ] || [ ! -x "$TMUX_PATH" ]; then
  echo "ERROR: tmux was not found." >&2
  echo "Install tmux first, or pass --tmux-path /path/to/tmux." >&2
  exit 3
fi

echo "  tmux: $TMUX_PATH"

detect_mesh_ipv4() {
  if command -v tailscale >/dev/null 2>&1; then
    tailscale ip -4 2>/dev/null | head -n 1 | tr -d '[:space:]'
    return
  fi
  if command -v ip >/dev/null 2>&1; then
    ip -4 -o addr show scope global 2>/dev/null \
      | awk '{print $4}' \
      | cut -d/ -f1 \
      | grep -E '^(100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\.)' \
      | head -n 1
    return
  fi
  if command -v ifconfig >/dev/null 2>&1; then
    ifconfig 2>/dev/null \
      | awk '/inet / {print $2}' \
      | grep -E '^(100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\.)' \
      | head -n 1
  fi
}

fetch_install_metadata() {
  local metadata_url="${HUB_HTTP_BASE}/v1/public/install-metadata"
  local metadata=""
  metadata="$(curl -fsS --connect-timeout 10 --max-time 20 "$metadata_url" 2>/dev/null || true)"
  if [ -z "$metadata" ]; then
    return 1
  fi
  METADATA_JSON="$metadata" python3 - <<'PY'
import json
import os

data = json.loads(os.environ["METADATA_JSON"])
key = data.get("capability_public_key")
port = data.get("direct_ws_port")
if isinstance(key, str) and key.strip():
    print(f"CAPABILITY_PUBLIC_KEY={key.strip()}")
if isinstance(port, int) and port > 0:
    print(f"DIRECT_WS_PORT={port}")
elif isinstance(port, str) and port.strip().isdigit():
    print(f"DIRECT_WS_PORT={port.strip()}")
PY
}

wait_for_hub_enrollment() {
  echo ""
  echo "Waiting for hub to acknowledge daemon enrollment (up to ${VERIFY_TIMEOUT_SECONDS}s)..."
  local log_paths=(
    "/tmp/benchday-daemon.log"
    "${HOME}/.benchday/daemon.log"
    "${HOME}/.benchday/daemon.err"
  )
  local deadline=$(( $(date +%s) + VERIFY_TIMEOUT_SECONDS ))
  while [ "$(date +%s)" -lt "$deadline" ]; do
    for log_path in "${log_paths[@]}"; do
      if [ -f "$log_path" ] && grep -q 'outbound: authenticated, hub READY' "$log_path" 2>/dev/null; then
        echo "  Hub enrollment verified via daemon log."
        return 0
      fi
    done
    if command -v systemctl >/dev/null 2>&1 && systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then
      if sudo journalctl -u "$SERVICE_NAME" --since '2 minutes ago' --no-pager 2>/dev/null \
        | grep -q 'outbound: authenticated, hub READY'; then
        echo "  Hub enrollment verified via systemd journal."
        return 0
      fi
    fi
    sleep 2
  done
  echo "  WARNING: hub did not confirm enrollment within ${VERIFY_TIMEOUT_SECONDS}s." >&2
  echo "  The daemon may still connect shortly. Check logs and the Benchday app." >&2
  return 1
}

# ── Add-account fast path (idempotent re-run on an existing daemon) ────────
# The machine already runs a daemon. Do not download a binary, rewrite config,
# touch ports, or install a competing service. Just hand the new enrollment code
# to the existing daemon and restart it; the daemon presents the code on its
# next Hello and the hub adds this account as an access grant.
if [ "$ADD_ACCOUNT" = 1 ]; then
  echo ""
  echo "Existing daemon detected: $DAEMON_ID ($EXISTING_LAYOUT layout)"
  if [ "$PAIR_MODE" = 1 ]; then
    echo "ERROR: --pair is not supported for add-account on an existing daemon." >&2
    echo "  The machine already has an identity; generate a code in the app instead." >&2
    exit 2
  fi
  if [ ! -f "$EXISTING_CONFIG" ]; then
    echo "ERROR: existing daemon config not found at $EXISTING_CONFIG" >&2
    echo "  Pass --daemon-id to force a fresh install instead." >&2
    exit 2
  fi

  echo "Adding this account to the existing daemon..."
  echo "  Config: $EXISTING_CONFIG"
  echo "  Enrollment code: ${ENROLLMENT_CODE:0:8}..."

  # Set enrollment_code under the [outbound] table (the daemon reads it there).
  # Drop any prior enrollment_code line, then insert a fresh one right after the
  # [outbound] header. Atomic replace via temp file.
  TMP_CFG="${EXISTING_CONFIG}.benchday.tmp.$$"
  grep -v -E '^[[:space:]]*enrollment_code[[:space:]]*=' "$EXISTING_CONFIG" \
    | awk -v code="$ENROLLMENT_CODE" '
        { print }
        /^[[:space:]]*\[outbound\][[:space:]]*$/ { print "enrollment_code = \"" code "\"" }
      ' > "$TMP_CFG"
  if ! grep -q -E '^enrollment_code[[:space:]]*=' "$TMP_CFG"; then
    echo "ERROR: could not find an [outbound] section in $EXISTING_CONFIG" >&2
    rm -f "$TMP_CFG"
    exit 2
  fi
  cat "$TMP_CFG" > "$EXISTING_CONFIG"
  rm -f "$TMP_CFG"

  # Re-running the installer should ALSO pick up daemon binary updates, not just
  # hand over a new account — otherwise a user who "re-installs to update" stays
  # frozen on the old binary (exactly how a stale daemon never gets a fix).
  # Standalone layout only: the supervisor owns its timestamped worker binaries
  # (a bare swap wouldn't reload them), and supervisor/mesh nodes update via
  # deploy + self-update. Best-effort: a failed download keeps the old binary.
  if [ "$EXISTING_LAYOUT" = "standalone" ]; then
    REFRESH_BIN="${INSTALL_DIR}/benchday-daemon"
    REFRESH_NAME="benchday-daemon-${OS}-${ARCH}"
    REFRESH_BASE="$(echo "$HUB_URL" | sed 's#^wss://#https://#;s#^ws://#http://#;s#/daemon$##')"
    REFRESH_TMP="/tmp/benchday-daemon.refresh.$$"
    echo ""
    echo "Refreshing daemon binary (${REFRESH_NAME})..."
    if curl -fsSL --connect-timeout 15 --max-time 120 \
         "${REFRESH_BASE}/update/daemon/${REFRESH_NAME}" -o "$REFRESH_TMP" 2>/dev/null \
       && [ -s "$REFRESH_TMP" ]; then
      # install -m 0755 unlinks+recreates, so the running executable is replaced
      # safely; the restart below picks up the new binary. sudo only if needed.
      if install -m 0755 "$REFRESH_TMP" "$REFRESH_BIN" 2>/dev/null \
         || sudo install -m 0755 "$REFRESH_TMP" "$REFRESH_BIN" 2>/dev/null; then
        echo "  Updated $REFRESH_BIN"
      else
        echo "  WARNING: could not replace $REFRESH_BIN; keeping the existing binary." >&2
      fi
    else
      echo "  WARNING: binary download failed; keeping the existing binary." >&2
    fi
    rm -f "$REFRESH_TMP"
  fi

  echo ""
  echo "Restarting daemon to apply the new code..."
  case "$OS" in
    macos)
      if ! launchctl kickstart -k "gui/$(id -u)/ai.benchday.daemon" 2>/dev/null; then
        launchctl stop ai.benchday.daemon 2>/dev/null || true
        launchctl start ai.benchday.daemon 2>/dev/null || true
      fi
      ;;
    linux)
      RESTARTED=0
      for unit in benchday-daemon-supervisor "benchday-daemon-${THIS_USER}" benchday-daemon; do
        if systemctl list-units --all 2>/dev/null | grep -q "${unit}.service" \
          || systemctl cat "$unit" >/dev/null 2>&1; then
          sudo systemctl restart "$unit" 2>/dev/null && { RESTARTED=1; break; }
        fi
      done
      if [ "$RESTARTED" != 1 ]; then
        echo "  WARNING: could not find a systemd unit to restart; restart the daemon manually." >&2
      fi
      ;;
  esac

  VERIFY_STATUS=0
  wait_for_hub_enrollment || VERIFY_STATUS=1

  echo ""
  echo "=== Add-account Complete ==="
  echo ""
  echo "Machine:  $DAEMON_ID (existing daemon, unchanged identity)"
  echo "Config:   $EXISTING_CONFIG"
  if [ "$VERIFY_STATUS" -eq 0 ]; then
    echo "This account was handed to the daemon and it reconnected to the hub."
  else
    echo "Code applied, but hub reconnect was not confirmed within ${VERIFY_TIMEOUT_SECONDS}s."
  fi
  echo "Return to the Benchday app — this machine should appear shortly."
  exit 0
fi

# ── Download binary ─────────────────────────────────────────────────────

# GitHub release configuration
GITHUB_REPO="zigzagtech/benchday"
BINARY_NAME="benchday-daemon-${OS}-${ARCH}"
HUB_HTTP_BASE="$(echo "$HUB_URL" | sed 's#^wss://#https://#;s#^ws://#http://#;s#/daemon$##')"

if [ -z "$CAPABILITY_PUBLIC_KEY" ]; then
  echo ""
  echo "Fetching hub install metadata..."
  while IFS='=' read -r key value; do
    case "$key" in
      CAPABILITY_PUBLIC_KEY) CAPABILITY_PUBLIC_KEY="$value" ;;
      DIRECT_WS_PORT) DIRECT_WS_PORT="$value" ;;
    esac
  done < <(fetch_install_metadata || true)
  if [ -n "$CAPABILITY_PUBLIC_KEY" ]; then
    echo "  Capability public key: ${CAPABILITY_PUBLIC_KEY:0:16}..."
  else
    echo "  WARNING: hub did not supply capability public key; terminal auth may fail until config is updated." >&2
  fi
fi

MESH_IP="$(detect_mesh_ipv4 || true)"
# Always bind the direct listener on a wildcard address. The daemon
# auto-advertises its private/LAN (RFC1918) and mesh (100.64) interface IPs and
# NEVER its public IP, so wildcard binding only ever exposes the port to the LAN
# and mesh — it lets a phone on the SAME network reach the daemon directly
# instead of detouring every keystroke through the hub. Without this, an
# off-mesh machine advertises no candidates ("no port -> no direct listener")
# and is stuck on the slow hub-proxy path even when the phone is on the same
# Wi-Fi.
DIRECT_WS_LISTEN_ADDR="0.0.0.0:$DIRECT_WS_PORT"
if [ -n "$MESH_IP" ]; then
  echo "  Mesh IP detected: $MESH_IP (direct websocket on port $DIRECT_WS_PORT; LAN + mesh)"
else
  echo "  No mesh IP; enabling LAN-direct on port $DIRECT_WS_PORT (a phone on the same network connects directly, bypassing the hub)."
fi

BINARY_URLS=()
if [ -n "${BENCHDAY_DAEMON_BINARY:-}" ]; then
  # Local binary override for testing.
  BINARY_URLS+=("file://${BENCHDAY_DAEMON_BINARY}")
  echo "Using local binary: $BENCHDAY_DAEMON_BINARY"
elif [ -n "${GITHUB_TOKEN:-}" ]; then
  # Use GitHub API with token for private repos
  LATEST_RELEASE=$(curl -fsSL -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
  BINARY_URLS+=("https://github.com/${GITHUB_REPO}/releases/download/${LATEST_RELEASE}/${BINARY_NAME}")
else
  # Public hub first, GitHub release as fallback.
  BINARY_URLS+=("${HUB_HTTP_BASE}/update/daemon/${BINARY_NAME}")
  BINARY_URLS+=("https://github.com/${GITHUB_REPO}/releases/latest/download/${BINARY_NAME}")
fi

BINARY_PATH="${INSTALL_DIR}/benchday-daemon"

echo ""
echo "Downloading benchday-daemon..."

# Create install dir if needed
if [ ! -d "$INSTALL_DIR" ]; then
  echo "  Creating $INSTALL_DIR (may require sudo)..."
  sudo mkdir -p "$INSTALL_DIR" || {
    echo "ERROR: failed to create $INSTALL_DIR" >&2
    exit 4
  }
fi

# Download with bounded retries. New-user onboarding must not hang forever if
# a release host is unreachable.
downloaded=0
for binary_url in "${BINARY_URLS[@]}"; do
  echo "  Source: $binary_url"
  for attempt in 1 2; do
    if [[ "$binary_url" == file://* ]]; then
      local_path="${binary_url#file://}"
      if [ -f "$local_path" ] && cp "$local_path" /tmp/benchday-daemon.tmp; then
        downloaded=1
        break
      fi
    elif curl -fsSL --connect-timeout 15 --max-time 120 "$binary_url" -o /tmp/benchday-daemon.tmp; then
      downloaded=1
      break
    fi
    echo "  Retry $attempt/2 failed"
    sleep 2
  done
  if [ "$downloaded" = 1 ]; then
    break
  fi
done
if [ "$downloaded" != 1 ]; then
  echo "ERROR: failed to download daemon binary" >&2
  echo "  OS: $OS, Arch: $ARCH" >&2
  echo "  Tried:" >&2
  printf '    %s\n' "${BINARY_URLS[@]}" >&2
  echo "  You can build from source or set BENCHDAY_DAEMON_BINARY" >&2
  exit 4
fi

chmod +x /tmp/benchday-daemon.tmp

if [ -w "$INSTALL_DIR" ]; then
  mv /tmp/benchday-daemon.tmp "$BINARY_PATH"
else
  echo "  Installing to $INSTALL_DIR (requires sudo)..."
  sudo mv /tmp/benchday-daemon.tmp "$BINARY_PATH"
fi

echo "  Installed: $BINARY_PATH"
echo "  Version:   $($BINARY_PATH --version)"

# ── Generate keypair ────────────────────────────────────────────────────

KEY_PATH="${CONFIG_DIR}/key"

echo ""
echo "Generating ed25519 keypair..."
mkdir -p "$CONFIG_DIR"

if [ -f "$KEY_PATH" ]; then
  echo "  Found existing key at $KEY_PATH"
else
  PUBLIC_KEY="$($BINARY_PATH --generate-key "$KEY_PATH" 2>/dev/null)"
  echo "  Key generated: $KEY_PATH"
fi

PUBLIC_KEY="$($BINARY_PATH --print-pubkey "$KEY_PATH" 2>/dev/null)"
echo "  Public key: ${PUBLIC_KEY:0:32}..."

# ── Host-initiated pairing (Phase 2) ────────────────────────────────────

if [ "$PAIR_MODE" = 1 ]; then
  echo ""
  echo "Starting host-initiated pairing..."
  CONFIG_PATH="${CONFIG_DIR}/benchday-daemon.toml"
  "$BINARY_PATH" --pair \
    --hub-url "$HUB_URL" \
    --daemon-id "$DAEMON_ID" \
    --key-path "$KEY_PATH" \
    --config-out "$CONFIG_PATH" \
    --ttl-seconds 600

  echo "  Config written by pairing: $CONFIG_PATH"

  # Append optional settings that --pair doesn't know about
  if [ -n "$TMUX_PATH" ]; then
    cat >> "$CONFIG_PATH" <<EOF
tmux_path = "$TMUX_PATH"
EOF
  fi
  if [ -n "$CAPABILITY_PUBLIC_KEY" ]; then
    cat >> "$CONFIG_PATH" <<EOF
capability_public_key = "$CAPABILITY_PUBLIC_KEY"
EOF
  fi
  # Wildcard direct listener so a same-network phone gets LAN-direct; the daemon
  # advertises its private/LAN + mesh IPs (never its public IP). The mesh IP, if
  # any, is the advertised primary for older hubs.
  cat >> "$CONFIG_PATH" <<EOF
direct_websocket_listen_addr = "$DIRECT_WS_LISTEN_ADDR"
EOF
  if [ -n "$MESH_IP" ]; then
    cat >> "$CONFIG_PATH" <<EOF
direct_websocket_advertise_url = "ws://$MESH_IP:$DIRECT_WS_PORT/"
EOF
  fi

fi

# ── Write config ────────────────────────────────────────────────────────

CONFIG_PATH="${CONFIG_DIR}/benchday-daemon.toml"

if [ "$PAIR_MODE" != 1 ]; then
  echo ""
  echo "Writing configuration..."

  AUTH_TOKEN="$(
    if command -v openssl >/dev/null 2>&1; then
      openssl rand -base64 32
    else
      dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64
    fi
  )"
  RECORDING_HUB_URL="$HUB_HTTP_BASE"

  cat > "$CONFIG_PATH" <<EOF
# Benchday daemon configuration
# Generated by install.sh on $(date -Iseconds)

auth_token = "$AUTH_TOKEN"
daemon_id = "$DAEMON_ID"
listen_addr = "127.0.0.1:7777"
mode = "outbound"
recording_hub_url = "$RECORDING_HUB_URL"
tmux_path = "$TMUX_PATH"
EOF

  if [ -n "$CAPABILITY_PUBLIC_KEY" ]; then
    cat >> "$CONFIG_PATH" <<EOF
capability_public_key = "$CAPABILITY_PUBLIC_KEY"
EOF
  else
    echo "  WARNING: no capability public key supplied; terminal auth may fail until daemon config is updated." >&2
  fi

  # Wildcard direct listener so a same-network phone gets LAN-direct; the daemon
  # advertises its private/LAN + mesh IPs (never its public IP). The mesh IP, if
  # any, is the advertised primary for older hubs.
  cat >> "$CONFIG_PATH" <<EOF
direct_websocket_listen_addr = "$DIRECT_WS_LISTEN_ADDR"
EOF
  if [ -n "$MESH_IP" ]; then
    cat >> "$CONFIG_PATH" <<EOF
direct_websocket_advertise_url = "ws://$MESH_IP:$DIRECT_WS_PORT/"
EOF
  fi

  cat >> "$CONFIG_PATH" <<EOF

[outbound]
hub_url = "$HUB_URL"
daemon_id = "$DAEMON_ID"
enrollment_code = "$ENROLLMENT_CODE"
private_key_path = "$KEY_PATH"
EOF

  echo "  Config: $CONFIG_PATH"

  # ── Register with hub ───────────────────────────────────────────────────

  echo ""
  echo "Daemon will register with hub on first connection."
  echo "  Enrollment code: ${ENROLLMENT_CODE:0:8}..."
  echo "  Machine ID:      $DAEMON_ID"
fi

# ── Install service ─────────────────────────────────────────────────────

check_port_available() {
  local port="$1"
  if command -v ss >/dev/null 2>&1; then
    ! ss -ltn 2>/dev/null | awk '{print $4}' | grep -q ":${port}$"
    return
  fi
  if command -v lsof >/dev/null 2>&1; then
    ! lsof -iTCP:"$port" -sTCP:LISTEN >/dev/null 2>&1
    return
  fi
  return 0
}

echo ""
echo "Preflight checks..."
if ! check_port_available "$DIRECT_WS_PORT"; then
  if [ -n "$MESH_IP" ]; then
    echo "  ERROR: port $DIRECT_WS_PORT is already in use; free it before enabling direct websocket." >&2
    exit 5
  fi
  echo "  WARNING: port $DIRECT_WS_PORT is in use; LAN-direct may not bind — the daemon will fall back to the hub-proxy route." >&2
fi

echo ""
echo "Installing service..."

case "$OS" in
  linux)
    if [ "$NO_SERVICE" = 1 ]; then
      echo "Starting daemon directly (--no-service)..."
      nohup "$BINARY_PATH" --config "$CONFIG_PATH" > "${BENCHDAY_DAEMON_LOG:-/tmp/benchday-daemon.log}" 2>&1 &
      echo "  PID: $!"
      echo "  Logs: ${BENCHDAY_DAEMON_LOG:-/tmp/benchday-daemon.log}"
    elif command -v systemctl >/dev/null 2>&1; then
      SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
      
      SERVICE_CONTENT="[Unit]
Description=Benchday Daemon
After=network.target

[Service]
Type=simple
ExecStart=$BINARY_PATH --config $CONFIG_PATH
Environment=BENCHDAY_SYSTEMD_UNIT=%n
Restart=always
RestartSec=5
User=$USER

[Install]
WantedBy=multi-user.target
"
      
      if [ -w "$(dirname "$SERVICE_FILE")" ]; then
        echo "$SERVICE_CONTENT" > "$SERVICE_FILE"
      else
        echo "  Creating systemd service (requires sudo)..."
        echo "$SERVICE_CONTENT" | sudo tee "$SERVICE_FILE" >/dev/null
      fi
      
      sudo systemctl daemon-reload
      sudo systemctl enable "$SERVICE_NAME"
      # `restart`, not `start`: on a re-install the service is already running, and
      # `systemctl start` is a NO-OP for an active unit — it would leave the OLD
      # daemon process running the OLD binary, so a freshly-downloaded daemon never
      # takes effect. `restart` covers both the first-install (not yet running) and
      # re-install (swap to the new binary) cases.
      sudo systemctl restart "$SERVICE_NAME"

      echo "  Service (re)started: $SERVICE_NAME"
      echo "  Status: $(sudo systemctl is-active "$SERVICE_NAME")"
    else
      echo "WARNING: systemd not found. Starting daemon manually..."
      nohup "$BINARY_PATH" --config "$CONFIG_PATH" > /tmp/benchday-daemon.log 2>&1 &
      echo "  PID: $!"
      echo "  Logs: /tmp/benchday-daemon.log"
    fi
    ;;
    
  macos)
    if [ "$NO_SERVICE" = 1 ]; then
      echo "Starting daemon directly (--no-service)..."
      nohup "$BINARY_PATH" --config "$CONFIG_PATH" > "${BENCHDAY_DAEMON_LOG:-/tmp/benchday-daemon.log}" 2>&1 &
      echo "  PID: $!"
      echo "  Logs: ${BENCHDAY_DAEMON_LOG:-/tmp/benchday-daemon.log}"
    else
      # Canonical daemon LaunchAgent label, matching deploy-daemon.sh and the
      # daemon's own self-update restart (self_update.rs LAUNCHD_LABEL). The
      # public installer historically used io.benchday.daemon, which self-update
      # could not kickstart — migrate any such legacy agent to avoid double-run.
      LAUNCHD_LABEL="ai.benchday.daemon"
      LEGACY_PLIST="${HOME}/Library/LaunchAgents/io.benchday.daemon.plist"
      if [ -f "$LEGACY_PLIST" ]; then
        launchctl bootout "gui/$(id -u)/io.benchday.daemon" 2>/dev/null || true
        launchctl unload "$LEGACY_PLIST" 2>/dev/null || true
        rm -f "$LEGACY_PLIST"
        echo "  Migrated legacy LaunchAgent io.benchday.daemon -> $LAUNCHD_LABEL"
      fi
      PLIST_FILE="${HOME}/Library/LaunchAgents/${LAUNCHD_LABEL}.plist"

    cat > "$PLIST_FILE" <<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>${LAUNCHD_LABEL}</string>
  <key>ProgramArguments</key>
  <array>
    <string>$BINARY_PATH</string>
    <string>--config</string>
    <string>$CONFIG_PATH</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
  <key>KeepAlive</key>
  <true/>
  <key>StandardOutPath</key>
  <string>/tmp/benchday-daemon.log</string>
  <key>StandardErrorPath</key>
  <string>/tmp/benchday-daemon.log</string>
</dict>
</plist>
EOF

    # A re-install MUST actually swap the running binary + config. `launchctl
    # load` is a NO-OP when the label is already bootstrapped (the common
    # re-install / curl-again case): the rewritten plist is ignored and the
    # KeepAlive process keeps running the OLD binary with its OLD in-memory
    # config, so the freshly-downloaded daemon just sits on disk and the update
    # silently never takes effect. Bootout the old instance first, then
    # bootstrap the rewritten plist and force a (re)start. kickstart -k is the
    # belt-and-suspenders restart in case the agent was already bootstrapped.
    LAUNCHD_DOMAIN="gui/$(id -u)"
    launchctl bootout "$LAUNCHD_DOMAIN/$LAUNCHD_LABEL" 2>/dev/null || true
    launchctl bootstrap "$LAUNCHD_DOMAIN" "$PLIST_FILE" 2>/dev/null \
      || launchctl load "$PLIST_FILE" 2>/dev/null || true
    launchctl kickstart -k "$LAUNCHD_DOMAIN/$LAUNCHD_LABEL" 2>/dev/null \
      || launchctl start "$LAUNCHD_LABEL" 2>/dev/null || true

      echo "  LaunchAgent (re)started: $LAUNCHD_LABEL"
      echo "  Logs: /tmp/benchday-daemon.log"
    fi
    ;;
esac

VERIFY_STATUS=0
wait_for_hub_enrollment || VERIFY_STATUS=1

# ── Summary ─────────────────────────────────────────────────────────────

echo ""
echo "=== Installation Complete ==="
echo ""
echo "Machine:     $DAEMON_ID"
echo "Public Key:  $PUBLIC_KEY"
echo "Config:      $CONFIG_PATH"
echo "Binary:      $BINARY_PATH"
echo ""
if [ "$VERIFY_STATUS" -eq 0 ]; then
  echo "Install verified: daemon is connected to the hub."
else
  echo "Install finished, but hub verification timed out."
fi
if [ "$PAIR_MODE" = 1 ]; then
  echo "Machine paired via host-initiated QR flow."
else
  echo "Return to the Benchday app — your machine should appear shortly."
fi
echo ""
echo "Troubleshooting:"
echo "  Logs:    tail -f /tmp/benchday-daemon.log"
echo "  Config:  cat $CONFIG_PATH"
echo "  Restart: sudo systemctl restart $SERVICE_NAME  (Linux)"
echo "           launchctl kickstart -k gui/\$(id -u)/ai.benchday.daemon  (macOS)"
echo ""
