#!/bin/bash
# Install Runabout for a traveler on this pad.
#
# Usage: runabout-install <kit_path> [display_name]
#   kit_path     — path to traveler's runabout.json (created here if missing)
#   display_name — required only if kit doesn't exist yet
#
# Creates (if missing):
#   runabout.json      — kit config in traveler's repo (travels on departure)
#   runabout-pad.json  — pad config alongside kit (gitignored, stays on pad)
#   runabout-<agent>   — named launch wrapper
#   ~/.local/share/applications/runabout-<agent>.desktop

set -e

KIT_PATH="${1:?Usage: runabout-install /path/to/runabout.json [display_name]}"
KIT_DIR="$(dirname "$(realpath "$KIT_PATH")")"
GIVEN_NAME="${2:-}"

# Write a file, respecting ownership of the target directory
write_file() {
  local path="$1"
  local content="$2"
  local dir_owner
  dir_owner=$(stat -c '%U' "$(dirname "$path")" 2>/dev/null || echo "root")
  if [ "$(id -un)" = "$dir_owner" ]; then
    echo "$content" > "$path"
  else
    echo "$content" | sudo -u "$dir_owner" tee "$path" > /dev/null
  fi
}

# ── Kit ───────────────────────────────────────────────────────────────────────

if [ -f "$KIT_PATH" ]; then
  AGENT=$(python3 -c "import json; print(json.load(open('$KIT_PATH'))['agent'])")
  NAME=$(python3 -c "import json; print(json.load(open('$KIT_PATH'))['display_name'])")
  AVATAR=$(python3 -c "import json; v=json.load(open('$KIT_PATH')).get('avatar'); print(v or '')" 2>/dev/null || true)
else
  # Derive agent from directory name — /home/echo → echo
  AGENT="$(basename "$KIT_DIR")"
  NAME="${GIVEN_NAME:-$AGENT}"
  if [ -z "$AGENT" ]; then
    echo "Cannot determine agent name from path. Provide display_name as second arg." >&2
    exit 1
  fi
  echo "Creating kit: $KIT_PATH"
  write_file "$KIT_PATH" "{
  \"agent\": \"${AGENT}\",
  \"display_name\": \"${NAME}\",
  \"avatar\": null,
  \"dormant_flag\": \"/var/lib/${AGENT}/injector.muted\"
}"
  AVATAR=""
fi

[ -z "$AGENT" ] && { echo "Could not determine agent." >&2; exit 1; }

# ── Agent runtime directory ───────────────────────────────────────────────────

sudo mkdir -p "/var/lib/${AGENT}"
if id "$AGENT" &>/dev/null; then
  sudo chown "${AGENT}:${AGENT}" "/var/lib/${AGENT}" 2>/dev/null || true
fi

# ── Pad detection ─────────────────────────────────────────────────────────────

PAD_HOSTNAME=$(python3 -c "import socket; print(socket.gethostname().split('.')[0].capitalize())")

# Detect tmux socket: named sock → UID-based (standard user tmux) → root default
TMUX_SOCKET="/tmp/tmux-${AGENT}.sock"
if [ ! -S "$TMUX_SOCKET" ] && id "$AGENT" &>/dev/null; then
  AGENT_UID=$(id -u "$AGENT" 2>/dev/null || true)
  [ -S "/tmp/tmux-${AGENT_UID}/default" ] && TMUX_SOCKET="/tmp/tmux-${AGENT_UID}/default"
fi
[ ! -S "$TMUX_SOCKET" ] && TMUX_SOCKET="/tmp/tmux-0/default"
TMUX_SESSION="$AGENT"

# Locate running ttyd service for this agent
TTYD_SERVICE=$(systemctl list-units --type=service --state=running --no-legend 2>/dev/null \
  | awk '{print $1}' | grep -i "ttyd" | grep -i "$AGENT" | head -1)

TTYD_URL=""
if [ -n "$TTYD_SERVICE" ]; then
  # ExecStart is often multi-line with backslash continuations — search full config
  SVC_CONF=$(systemctl cat "$TTYD_SERVICE" 2>/dev/null)
  PORT=$(echo "$SVC_CONF" | grep -oP '(?<=--port\s)\d+' | head -1)
  BASE=$(echo "$SVC_CONF" | grep -oP '(?<=--base-path\s)\S+' | head -1 || true)
  IFACE=$(echo "$SVC_CONF" | grep -oP '(?<=--interface\s)\S+' | head -1 || true)
  HOST="${IFACE:-localhost}"
  TTYD_URL="http://${HOST}:${PORT}${BASE}/"
fi

if [ -z "$TTYD_URL" ]; then
  echo "Note: no running ttyd service found for '${AGENT}'. Operator tab URL left as placeholder."
  TTYD_URL="http://localhost:7680/"
fi

# ── Pad config (gitignored) ───────────────────────────────────────────────────

PAD_PATH="${KIT_DIR}/runabout-pad.json"
write_file "$PAD_PATH" "{
  \"tmux_socket\": \"${TMUX_SOCKET}\",
  \"tmux_session\": \"${TMUX_SESSION}\",
  \"terminals\": [
    {
      \"id\": \"${AGENT}-main\",
      \"label\": \"${NAME}\",
      \"location\": \"${PAD_HOSTNAME}\",
      \"url\": \"${TTYD_URL}\"
    }
  ]
}"

# Ensure runabout-pad.json is gitignored in the kit's repo
GIT_ROOT=$(git -C "$KIT_DIR" rev-parse --show-toplevel 2>/dev/null || true)
if [ -n "$GIT_ROOT" ]; then
  GITIGNORE="${GIT_ROOT}/.gitignore"
  if ! grep -q "runabout-pad.json" "$GITIGNORE" 2>/dev/null; then
    local_owner=$(stat -c '%U' "$GIT_ROOT" 2>/dev/null || echo "root")
    echo "runabout-pad.json" | sudo -u "$local_owner" tee -a "$GITIGNORE" > /dev/null
    echo "Added runabout-pad.json to $(basename "$GIT_ROOT")/.gitignore"
  fi
fi

# ── Launch wrapper + .desktop ─────────────────────────────────────────────────

RUNABOUT_BIN=$(command -v runabout 2>/dev/null || echo "/usr/local/bin/runabout")
WRAPPER="/usr/local/bin/runabout-${AGENT}"

# Ensure every directory in the binary's resolved symlink chain has o+x so
# non-owner users can traverse it (e.g. echo traversing /home/oldbones/).
BIN_REAL=$(realpath "$RUNABOUT_BIN" 2>/dev/null || true)
if [ -n "$BIN_REAL" ]; then
  _dir=$(dirname "$BIN_REAL")
  while [ "$_dir" != "/" ] && [ "$_dir" != "." ]; do
    _perms=$(stat -c '%a' "$_dir" 2>/dev/null || echo "755")
    if [ $(( 8#$_perms % 8 & 1 )) -eq 0 ]; then
      sudo chmod o+x "$_dir" 2>/dev/null && echo "Fixed traversal: chmod o+x $_dir" || true
    fi
    _dir=$(dirname "$_dir")
  done
fi

# Determine agent home dir for XAUTHORITY fallback
AGENT_HOME=$(getent passwd "${AGENT}" | cut -d: -f6 2>/dev/null || echo "/home/${AGENT}")

# Copy current X auth cookie to agent's ~/.Xauthority for cross-user launch.
# Needed when the wrapper runs as a different user than the agent (e.g. oldbones
# launching echo's Runabout via desktop session).
if id "$AGENT" &>/dev/null && [ "$(id -un)" != "$AGENT" ] && [ -n "${DISPLAY:-}" ]; then
  if xauth extract - "$DISPLAY" 2>/dev/null | sudo -u "$AGENT" xauth -f "${AGENT_HOME}/.Xauthority" merge - 2>/dev/null; then
    echo "Copied X auth ($DISPLAY) → ${AGENT_HOME}/.Xauthority"
  fi
fi

# Log path: prefer /var/lib/${AGENT}/ if writable, else /tmp/
if [ -d "/var/lib/${AGENT}" ] && [ -w "/var/lib/${AGENT}" ]; then
  LOG_PATH="/var/lib/${AGENT}/runabout.log"
  touch "$LOG_PATH" 2>/dev/null || sudo touch "$LOG_PATH" 2>/dev/null || true
  chmod 666 "$LOG_PATH" 2>/dev/null || sudo chmod 666 "$LOG_PATH" 2>/dev/null || true
else
  LOG_PATH="/tmp/runabout-${AGENT}.log"
fi

# Pre-create shared files as 666 so launcher user (e.g. oldbones) can write
# into an agent-owned /var/lib/${AGENT}/ directory without sudo at runtime.
for _f in runabout-inbox runabout-chat.log runabout.log; do
  _p="/var/lib/${AGENT}/${_f}"
  sudo touch "$_p" 2>/dev/null || true
  sudo chmod 666 "$_p" 2>/dev/null || true
done

sudo tee "$WRAPPER" > /dev/null << WRAP
#!/bin/bash
CONFIG="${KIT_PATH}"

sudo pkill -f "runabout-dispatch ${AGENT}" 2>/dev/null || true

nohup env \
  RUNABOUT_CONFIG="\${CONFIG}" \
  DISPLAY="\${DISPLAY:-:1}" \
  XAUTHORITY="\${XAUTHORITY:-${AGENT_HOME}/.Xauthority}" \
  bash -c 'exec -a runabout-${AGENT} ${RUNABOUT_BIN}' \
  >> ${LOG_PATH} 2>&1 &
disown \$!
echo "runabout-${AGENT} launched (PID \$!)"
WRAP
sudo chmod +x "$WRAPPER"

# Use the real invoking user's home, not root's, when run via sudo
REAL_USER="${SUDO_USER:-$USER}"
REAL_HOME=$(getent passwd "$REAL_USER" | cut -d: -f6)
DESKTOP_DIR="${REAL_HOME}/.local/share/applications"
mkdir -p "$DESKTOP_DIR"
ICON="utilities-terminal"
[ -n "$AVATAR" ] && [ -f "$AVATAR" ] && ICON="$AVATAR"

cat > "${DESKTOP_DIR}/runabout-${AGENT}.desktop" << DESK
[Desktop Entry]
Name=Runabout · ${NAME}
Comment=Fleet companion — ${NAME}
Exec=${WRAPPER}
Icon=${ICON}
Type=Application
Categories=Utility;
StartupNotify=true
StartupWMClass=runabout-${AGENT}
DESK
chmod +x "${DESKTOP_DIR}/runabout-${AGENT}.desktop"

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

echo ""
echo "Installed: ${NAME} — Runabout on ${PAD_HOSTNAME}"
echo "  Kit:      ${KIT_PATH}"
echo "  Pad:      ${PAD_PATH}"
echo "  Wrapper:  ${WRAPPER}"
echo "  Launch:   runabout-${AGENT}"
