#!/usr/bin/env bash
# dead-advisor session resolver + agent-id sidecar.
# Fail-closed: any unexpected state -> nonzero exit + stderr, never a wrong-session guess.
#
#   resolve.sh lookup <MARKER>        resolve THIS session's transcript via MARKER; print
#                                     TRANSCRIPT/SESSION/AGENT/LASTREAD/NOW. exit 3 = no match.
#   resolve.sh save <SESSION> <ID>    persist the spawned subagent's agentId for this session.
#   resolve.sh agent <SESSION>        print the stored agentId for this session (empty if none). Read-only.
#   resolve.sh mark <SESSION> <N>     record the transcript line count the subagent just read,
#                                     so the next call's delta range starts there (read cursor).
set -euo pipefail

STATE_DIR="${HOME}/.claude/dead-advisor-state"

die() { echo "dead-advisor: $*" >&2; exit 1; }

slug_of_cwd() {
  # cwd slug = PWD with every '/' and '.' -> '-' (matches ~/.claude/projects/<slug> layout)
  printf '%s' "$PWD" | tr '/.' '--'
}

# Every top-level transcript under $2 (exactly $3 levels deep) containing the literal
# MARKER, into CANDIDATES. NUL-delimited throughout: a path may hold any character
# except NUL, and this one is eval'd by callers.
scan_for() {
  local marker="$1" root="$2" depth="$3" file
  CANDIDATES=()
  [ -d "$root" ] || return 0
  while IFS= read -r -d '' file; do
    CANDIDATES+=("$file")
  done < <(find "$root" -mindepth "$depth" -maxdepth "$depth" -name '*.jsonl' -type f \
             -exec grep -lZF -- "$marker" {} + </dev/null 2>/dev/null)
  return 0
}

# One transcript, or nothing. Two files carrying the MARKER are two conversations it
# cannot tell apart — a newest-mtime guess would advise on the wrong one — so the only
# safe outcomes are the single match or a refusal naming what it found.
sole_candidate() {
  [ "${#CANDIDATES[@]}" -eq 1 ] || return 1
  printf '%s' "${CANDIDATES[0]}"
}

refuse_ambiguous() {
  local file
  echo "dead-advisor: MARKER matches ${#CANDIDATES[@]} transcripts:" >&2
  for file in "${CANDIDATES[@]}"; do echo "  $(basename -- "$file" .jsonl)" >&2; done
  die "supply a MARKER unique to this session — a substring of its most recent user turn"
}

cmd_lookup() {
  local marker="${1:-}"
  [ -n "$marker" ] || die "lookup needs a MARKER"

  # The transcript directory is keyed on the session's ORIGINAL cwd, so a session that
  # moved into a worktree no longer matches its own slug: try the slug, then every
  # project. The MARKER, not the cwd, is the identity.
  local match=""
  CANDIDATES=()
  scan_for "$marker" "${HOME}/.claude/projects/$(slug_of_cwd)" 1
  if [ "${#CANDIDATES[@]}" -gt 0 ]; then
    match=$(sole_candidate) || refuse_ambiguous
  else
    scan_for "$marker" "${HOME}/.claude/projects" 2
    [ "${#CANDIDATES[@]}" -eq 0 ] || match=$(sole_candidate) || refuse_ambiguous
  fi
  [ -n "$match" ] || { echo "dead-advisor: MARKER matched no transcript under ${HOME}/.claude/projects; supply a more unique/earlier substring" >&2; exit 3; }

  local session agent_file agent_id="" read_file lastread=0 now
  session=$(basename "$match" .jsonl)
  agent_file="${STATE_DIR}/${session}.id"
  [ -f "$agent_file" ] && agent_id=$(head -1 "$agent_file" 2>/dev/null || true)
  read_file="${STATE_DIR}/${session}.read"
  [ -f "$read_file" ] && lastread=$(head -1 "$read_file" 2>/dev/null || echo 0)
  case "$lastread" in (*[!0-9]*|'') lastread=0 ;; esac
  now=$(wc -l < "$match" 2>/dev/null | tr -d ' '); [ -n "$now" ] || now=0

  # Shell-quoted: callers eval this, and a path may carry spaces or metacharacters.
  printf 'TRANSCRIPT=%q\n' "$match"
  printf 'SESSION=%q\n' "$session"
  printf 'AGENT=%q\n' "$agent_id"
  printf 'LASTREAD=%q\n' "$lastread"
  printf 'NOW=%q\n' "$now"
}

cmd_agent() {
  local session="${1:-}"
  [ -n "$session" ] || die "agent needs SESSION"
  local f="${STATE_DIR}/${session}.id"
  [ -f "$f" ] && head -1 "$f" 2>/dev/null || true
}

cmd_mark() {
  local session="${1:-}" n="${2:-}"
  [ -n "$session" ] || die "mark needs SESSION"
  case "$n" in (''|*[!0-9]*) die "mark needs a numeric line count" ;; esac
  mkdir -p "$STATE_DIR"
  printf '%s\n' "$n" > "${STATE_DIR}/${session}.read"
  echo "marked ${session} read=${n}"
}

cmd_save() {
  local session="${1:-}" id="${2:-}"
  [ -n "$session" ] || die "save needs SESSION"
  [ -n "$id" ] || die "save needs AGENT_ID"
  mkdir -p "$STATE_DIR"
  printf '%s\n' "$id" > "${STATE_DIR}/${session}.id"
  echo "saved ${session} -> ${id}"
}

case "${1:-}" in
  lookup) shift; cmd_lookup "$@" ;;
  save)   shift; cmd_save "$@" ;;
  agent)  shift; cmd_agent "$@" ;;
  mark)   shift; cmd_mark "$@" ;;
  *)      die "usage: resolve.sh {lookup <MARKER>|save <SESSION> <ID>|agent <SESSION>|mark <SESSION> <N>}" ;;
esac
