#!/usr/bin/env bash
# Tear down a dev observability session. Kills every PID recorded in
# the session's .pids file plus belt-and-braces wipes any orphans that
# could survive a parent-shell exit (Metro, simctl spawn log streams,
# the metro.log tailers). Without the orphan sweep, an interrupted
# `tail-logs.sh` leaves behind processes that hold port 8081 and
# silently breaks the next session.
#
# Usage:
#   scripts/dev/stop-logs.sh                    # latest session
#   scripts/dev/stop-logs.sh temp/dev-logs/202… # a specific session
#   scripts/dev/stop-logs.sh --all              # every session + every orphan

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
cd "$REPO_ROOT"

ALL=0
SESSION=""
for arg in "$@"; do
  case "$arg" in
    --all) ALL=1 ;;
    -h|--help)
      sed -n '2,11p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
      exit 0
      ;;
    *) SESSION="$arg" ;;
  esac
done

kill_pids_from() {
  local pids_file="$1"
  if [[ ! -f "$pids_file" ]]; then
    return
  fi
  while read -r pid; do
    if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
      kill "$pid" 2>/dev/null || true
    fi
  done < "$pids_file"
}

if [[ "$ALL" -eq 1 ]]; then
  echo "[stop-logs] --all: killing every recorded session + orphans"
  for d in temp/dev-logs/202*/; do
    kill_pids_from "$d.pids"
  done
else
  SESSION="${SESSION:-temp/dev-logs/latest}"
  if [[ ! -e "$SESSION" ]]; then
    echo "No session at $SESSION"
    exit 1
  fi
  SESSION_REAL="$(cd "$SESSION" && pwd -P)"
  PIDS="$SESSION_REAL/.pids"
  if [[ ! -f "$PIDS" ]]; then
    echo "No .pids in $SESSION_REAL"
    exit 1
  fi

  # Auto-snapshot before killing streams. A session arriving later
  # (next Claude run, post-lunch operator) always has *some* frozen
  # state to inspect, even if the previous operator forgot to
  # snapshot manually. Best-effort — a failed snapshot must never
  # block the cleanup that follows.
  if find "$SESSION_REAL" -maxdepth 1 -type f -name '*.log' -size +0c 2>/dev/null | grep -q .; then
    echo "[stop-logs] Auto-snapshotting before teardown…"
    "$SCRIPT_DIR/snapshot.sh" "$SESSION_REAL" || \
      echo "[stop-logs] ⚠ snapshot failed — continuing with kill sweep."
  fi

  echo "[stop-logs] Killing PIDs in $PIDS"
  kill_pids_from "$PIDS"
fi

# Belt-and-braces orphan sweep. `kill_pids_from` only sees what
# tail-logs.sh recorded; subshells forked from `( ... ) &` and the
# Expo CLI's worker children can outlive the parent and end up
# reparented to init. These patterns are tight enough that they
# won't match unrelated user processes.
pkill -f 'simctl spawn .* log stream'        2>/dev/null || true
# tail-of-tail chain: metro.log → [net] demuxer (net.log) → tapestry/429
# carve-outs. Two patterns to catch the first link AND the second.
pkill -f 'tail -F .*dev-logs.*metro\.log'    2>/dev/null || true
pkill -f 'tail -F .*dev-logs.*net\.log'      2>/dev/null || true
pkill -f 'tail -F .*dev-logs.*watch-app\.log' 2>/dev/null || true
# Metro / Expo CLI — match the dev-server invocation specifically so
# we don't kill arbitrary `expo` commands the user might be running.
pkill -f 'expo start --dev-client'           2>/dev/null || true
# Foreground `simctl launch --console` wrappers from tail-logs.sh.
pkill -f 'simctl launch --console .* ht\.halflig\.app' 2>/dev/null || true
# metro-hmr.mjs Node child — exits cleanly on SIGTERM via its handler.
pkill -f 'node .*scripts/dev/metro-hmr\.mjs' 2>/dev/null || true

# Wait briefly for the kills to settle, then check the port. If 8081
# is still bound we missed something — surface that so the operator
# can pkill -9 by hand.
sleep 0.5
if lsof -nP -iTCP:8081 -sTCP:LISTEN >/dev/null 2>&1; then
  echo "[stop-logs] ⚠ Something still bound to :8081:"
  lsof -nP -iTCP:8081 -sTCP:LISTEN | tail -n +2
  echo "[stop-logs]   Run: kill -9 \$(lsof -ti :8081)"
fi

echo "[stop-logs] Done."
