#!/usr/bin/env bash
# Install the AgentVox host daemon as a systemd USER service, so it starts at
# boot and is restarted if it dies. Run as the user that will own the agents.
#
#   ./install-systemd.sh
#
# Idempotent: re-running rewrites the unit and restarts it.
#
# A user service, not a system service: the host owns tmux sessions belonging
# to this user and needs this user's HOME, PATH and credentials. Lingering
# makes it start at boot rather than at first login.
#
# Every failure mode this script guards against was hit for real on a previous
# box, in this order:
#   1. "Failed to connect to bus: No medium found" — no per-user systemd
#      manager running, because lingering was never enabled.
#   2. status=203/EXEC — ExecStart pointed at a path that did not exist.
#   3. `env node` unresolvable — systemd's PATH contains almost nothing, so
#      both the interpreter and the script must be absolute.
#   4. An nvm shim in ExecStart — resolves for an interactive shell, not for
#      systemd.
set -euo pipefail

SERVICE="agentvox-host"
UNIT_DIR="$HOME/.config/systemd/user"
UNIT="$UNIT_DIR/$SERVICE.service"

command -v systemctl >/dev/null || { echo "ERROR: systemd not found on this machine." >&2; exit 1; }
command -v tmux >/dev/null || {
  echo "ERROR: tmux is not installed. The host drives every agent through tmux" >&2
  echo "       panes, so it will fail at the first agent launch without it." >&2
  exit 1
}

TARGET="$(command -v agentvox-host || true)"
if [[ -z "$TARGET" ]]; then
  echo "ERROR: agentvox-host is not on PATH. Install it first:" >&2
  echo "       npm install -g @agentvox/host" >&2
  exit 1
fi

# Resolve the wrapper symlink to the real script and pin the interpreter.
# `readlink -f` over a shim, plus an absolute node, is what keeps systemd from
# failing with 203/EXEC on a machine using nvm.
SCRIPT_PATH="$(readlink -f "$TARGET")"
NODE_BIN="$(readlink -f "$(command -v node)")"
NODE_DIR="$(dirname "$NODE_BIN")"

# The daemon starts fine without the agent binaries on its PATH and then fails
# at the first start_agent, which is a confusing way to break. Agent CLIs turn
# up in a few different places depending on how they were installed (npm user
# prefix, ~/.local/bin, an nvm prefix), so find the ones that exist now and put
# their directories on the service PATH explicitly.
AGENT_DIRS=""
for tool in claude codex; do
  p="$(command -v "$tool" 2>/dev/null || true)"
  [[ -n "$p" ]] || continue
  # Deliberately NOT readlink -f here: these tools self-update, and resolving
  # the symlink pins a versioned directory that disappears on the next upgrade.
  # The shim's own directory is the stable one.
  AGENT_DIRS="${AGENT_DIRS:+$AGENT_DIRS:}$(dirname "$p")"
  echo "→ agent  : $tool at $p"
done

SERVICE_PATH=""
for d in ${AGENT_DIRS:+${AGENT_DIRS//:/ }} \
         "$HOME/.local/bin" "$HOME/.npm-global/bin" "$NODE_DIR" \
         /usr/local/bin /usr/bin /bin /usr/local/sbin /usr/sbin /sbin; do
  case ":$SERVICE_PATH:" in *":$d:"*) continue ;; esac
  SERVICE_PATH="${SERVICE_PATH:+$SERVICE_PATH:}$d"
done

echo "→ node   : $NODE_BIN ($("$NODE_BIN" --version))"
echo "→ host   : $SCRIPT_PATH"
echo "→ unit   : $UNIT"

if [[ ! -f "$HOME/.agentvox/host.json" ]]; then
  echo
  echo "WARNING: no ~/.agentvox/host.json — this machine is not paired yet." >&2
  echo "         Run 'agentvox-host pair', claim the code in the iOS app," >&2
  echo "         then re-run this script." >&2
  echo
fi

# A user manager only exists at boot if lingering is on. Without it,
# `systemctl --user` fails with "Failed to connect to bus: No medium found"
# the moment you log out.
if ! loginctl show-user "$USER" --property=Linger 2>/dev/null | grep -q "Linger=yes"; then
  echo "→ enabling lingering for $USER (needs sudo once)"
  sudo loginctl enable-linger "$USER" || {
    echo "WARNING: could not enable lingering. The service will run while you are" >&2
    echo "         logged in, but will NOT start at boot. Ask an admin to run:" >&2
    echo "         sudo loginctl enable-linger $USER" >&2
  }
fi

# Non-login shells (like this script under sudo, or a CI runner) often lack
# these, and systemctl --user cannot find the bus without them.
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
export DBUS_SESSION_BUS_ADDRESS="${DBUS_SESSION_BUS_ADDRESS:-unix:path=$XDG_RUNTIME_DIR/bus}"

mkdir -p "$UNIT_DIR" "$HOME/.agentvox"

cat > "$UNIT" <<UNITEOF
[Unit]
Description=AgentVox host daemon
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
# Absolute interpreter + absolute script. systemd's PATH resolves neither
# "env node" nor an nvm shim, which is a 203/EXEC every time.
ExecStart=$NODE_BIN $SCRIPT_PATH
# The daemon reports its cwd to the proxy as this host's workingDir, which Vox
# uses as the fallback path when creating an agent with no explicit directory.
WorkingDirectory=$HOME
Environment=PATH=$SERVICE_PATH
Environment=HOME=$HOME
Restart=always
RestartSec=10
StandardOutput=append:$HOME/.agentvox/host.log
StandardError=append:$HOME/.agentvox/host.log

[Install]
WantedBy=default.target
UNITEOF

systemctl --user daemon-reload
systemctl --user enable "$SERVICE"
systemctl --user restart "$SERVICE"

sleep 4
echo
if systemctl --user is-active --quiet "$SERVICE"; then
  echo "✓ $SERVICE is active"
  systemctl --user show "$SERVICE" --property=MainPID --property=ActiveState
else
  echo "✗ $SERVICE failed to start:" >&2
  systemctl --user status "$SERVICE" --no-pager | tail -20 >&2
  exit 1
fi

echo
echo "Logs:    tail -f $HOME/.agentvox/host.log"
echo "         journalctl --user -u $SERVICE -f"
echo "Stop:    systemctl --user stop $SERVICE"
echo "Restart: systemctl --user restart $SERVICE"
