#!/usr/bin/env bash
# _agent-session-tmux — host one agent session in a dedicated tmux server.
#
# The server is a systemd service, not a child of the terminal, so losing the terminal
# only detaches the client. The runtime still enters through _agent-session-admission, preserving
# the existing agent.slice confinement and its per-session resource scope.
set -uo pipefail

SELF_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)"
STATE_DIR="${AGENT_SESSIONS_DIR:-$HOME/.local/state/agent-sessions}"
SOCK="${AGENT_LEDGER_MUX_SOCKET:-}"
SESSION="${AGENT_LEDGER_MUX_TARGET:-main}"
ATTACH="${AGENT_LEDGER_MUX_ATTACH:-0}"
JAILED="${AGENT_LEDGER_MUX_JAILED:-0}"
CONF="$SELF_DIR/../lib/human-session.tmux.conf"
SLICE=agent.slice
# Per-session values: they must not reach the runtime's environment, where a nested launch
# would inherit this session's socket.
unset AGENT_LEDGER_MUX_SOCKET AGENT_LEDGER_MUX_TARGET AGENT_LEDGER_MUX_ATTACH \
  AGENT_LEDGER_MUX_JAILED

note() { printf '[agent-session] %s\n' "$*" >&2; }

(( $# >= 1 )) || { note "runtime command required"; exit 64; }
[[ -n "${AGENT_LEDGER_ID:-}" && -n "$SOCK" ]] || {
  note "ledger identity or socket missing"; exit 78;
}
[[ "$AGENT_LEDGER_ID" =~ ^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$ ]] || {
  note "invalid ledger identity"; exit 78;
}
[[ "$SESSION" == main && "$SOCK" == "$STATE_DIR/sock/$AGENT_LEDGER_ID" ]] || {
  note "ledger target does not match its dedicated session path"; exit 78;
}
[[ "$ATTACH" == 0 || "$ATTACH" == 1 ]] || {
  note "invalid attach mode"; exit 78;
}
[[ "$JAILED" == 0 || "$JAILED" == 1 ]] || {
  note "invalid jail mode"; exit 78;
}

RUNTIME="$1"
shift
UNIT="agent-session-${AGENT_LEDGER_ID}.service"
SPAWN=""
STATUS="$STATE_DIR/spawn/${AGENT_LEDGER_ID}.status"
SERVER_STARTED=0

tmux_() { tmux -S "$SOCK" "$@"; }

refuse() {
  local reason="$1"
  [[ -n "$SPAWN" ]] && rm -f -- "$SPAWN"
  rm -f -- "$STATUS"
  if (( SERVER_STARTED )); then
    systemctl --user stop "$UNIT" >/dev/null 2>&1 || :
  fi
  rm -f -- "$SOCK"
  note "$reason — refusing an unhosted agent launch"
  exit 78
}

mkdir -p -m 700 "$STATE_DIR/sock" "$STATE_DIR/spawn" \
  || refuse "cannot create session state"
chmod 700 "$STATE_DIR" "$STATE_DIR/sock" "$STATE_DIR/spawn" 2>/dev/null || :
[[ -r "$CONF" ]] || refuse "tmux config missing at $CONF"
command -v tmux >/dev/null 2>&1 || refuse "tmux is not installed"
command -v systemd-run >/dev/null 2>&1 || refuse "systemd-run is not installed"
command -v systemctl >/dev/null 2>&1 || refuse "systemctl is not installed"

rm -f -- "$SOCK"
systemctl --user reset-failed "$UNIT" 2>/dev/null || :
if ! systemd-run --user --quiet --collect \
  --unit="$UNIT" \
  --slice="$SLICE" \
  --service-type=forking \
  -p OOMPolicy=continue \
  -p KillMode=control-group \
  -- /usr/bin/tmux -S "$SOCK" -f "$CONF" start-server; then
  refuse "tmux server would not start in $SLICE"
fi
SERVER_STARTED=1

SERVER_PID=""
for _ in $(seq 1 50); do
  SERVER_PID="$(tmux_ display-message -p '#{pid}' 2>/dev/null || :)"
  [[ "$SERVER_PID" =~ ^[0-9]+$ ]] && break
  sleep 0.1
done
[[ "$SERVER_PID" =~ ^[0-9]+$ ]] || refuse "tmux server did not become ready"

SERVER_CGROUP="$(awk -F: '$1 == "0" { print $3 }' "/proc/$SERVER_PID/cgroup" 2>/dev/null)"
[[ "$SERVER_CGROUP" == *"/$SLICE/"* ]] \
  || refuse "tmux server did not enter $SLICE"

# A tmux pane inherits the server's sparse manager environment. Hand the caller's exact
# environment and argv through a mode-0700 file, then unlink it before the runtime execs.
umask 077
SPAWN="$(mktemp "$STATE_DIR/spawn/${AGENT_LEDGER_ID}.XXXXXXXX.sh")" \
  || refuse "cannot create the environment handoff"
rm -f -- "$STATUS"
{
  printf '#!/usr/bin/env bash\n'
  export -p
  printf 'cd %q || exit 1\n' "$PWD"
  printf 'rm -f %q\n' "$SPAWN"
  printf 'set +e\n'
  printf '%q' "$SELF_DIR/_agent-session-admission"
  [[ "$JAILED" == 0 ]] && printf ' tmpjail'
  printf ' %q' "$RUNTIME"
  for arg in "$@"; do printf ' %q' "$arg"; done
  printf '\nstatus=$?\nprintf "%%s\\n" "$status" >%q\nexit "$status"\n' "$STATUS"
} >"$SPAWN"
chmod 700 "$SPAWN"

printf -v PANE_COMMAND 'exec bash %q' "$SPAWN"
REAP_ID="$(</proc/sys/kernel/random/uuid)"
if ! tmux_ new-session -d -s "$SESSION" -c "$PWD" \
  -x "$(tput cols 2>/dev/null || echo 200)" \
  -y "$(tput lines 2>/dev/null || echo 50)" \
  "$PANE_COMMAND" \; set-option -g exit-empty on \; \
  set-option -t "$SESSION" @agent_reap_identity "$REAP_ID"; then
  refuse "tmux could not create the session"
fi

# The birth record claims no host; only an accepted session may. Without this target the
# collector cannot watch the pane, so refusing is safer than running an invisible session.
if ! node --input-type=module -e \
  'const [mod, id, name, sock, reapIdentity] = process.argv.slice(1);
   const { updateEntry } = await import(mod);
   if (!updateEntry(id, {
     mux: { kind: "tmux", socket: sock, target: name },
     tmuxSession: name,
     tmuxSocket: sock,
     tmuxReapIdentity: reapIdentity,
   })) process.exit(1);' \
  "$SELF_DIR/../lib/agent-session-reader.mjs" "$AGENT_LEDGER_ID" "$SESSION" "$SOCK" "$REAP_ID" \
  >/dev/null 2>&1; then
  refuse "tmux target could not be recorded in the ledger"
fi

if [[ "$ATTACH" == 1 ]]; then
  exec tmux -S "$SOCK" attach-session -t "$SESSION"
fi

# A dispatched caller still needs the runtime's lifecycle and exit status even though no tmux
# client is attached. The server owns the pane; this process merely waits for its status file.
while tmux_ has-session -t "$SESSION" 2>/dev/null; do
  sleep 0.1
done
status="$(cat "$STATUS" 2>/dev/null || printf 1)"
rm -f -- "$STATUS"
[[ "$status" =~ ^[0-9]+$ ]] || status=1
exit "$status"
