#!/usr/bin/env bash
# CANONICAL COPY of the Claude Code statusline. Deployed to (and read from):
#
#     ~/.claude/statusline-command.sh        <- the LIVE path CC actually runs
#
# Install / update after editing here:
#     cp scripts/claude-statusline-command.sh ~/.claude/statusline-command.sh
#
# Copies, not symlinks — same rule as stacks/: this tree is intent, the live
# path is reality, and they diverge until someone deploys. Diff them with
#     diff -u scripts/claude-statusline-command.sh ~/.claude/statusline-command.sh
#
# It is version-controlled here because the althing v3 cutover broke it in a way
# that was invisible: the segment gated on `command -v althing-cli`, a binary the
# cutover deleted, so the 📬 badge and 🔔 bell silently vanished for every session
# on the box. With 71 of 73 handles pull-only, that badge is the ONLY out-of-band
# signal telling a session with no armed waiter that it has mail — a dead
# statusline made a working bus look like an empty one.
#
# Smoke test (CC pipes session JSON on stdin):
#     echo '{"model":{"display_name":"opus"},"workspace":{"current_dir":"/tmp"},"cwd":"/tmp"}' \
#       | ALTHING_HANDLE=<a-handle-with-unread> bash scripts/claude-statusline-command.sh
#   want: a leading `📬 N`; and with an unreachable post office, degradation in
#   ~2s rather than a hang.
# Claude Code statusline. Layout:
#   [📬N] [🔔/🔕] | <proj> ⎇<branch> *<dirty> ↑<unpushed> | <model> | ctx:<pct> <toks> | $<session-cost> | 5h:% 7d:%
# ctx% and rate-limit %s are threshold-colored: green <60, yellow 60-90, red >90.
# All segments degrade gracefully (missing tool / non-git dir / no handle => segment omitted).
input=$(cat)

# --- threshold color: $1=numeric pct, $2=display text -> colored text ---
color_pct() {
  local p="$1" txt="$2" c
  if   awk "BEGIN{exit !($p < 60)}"; then c=$'\033[32m'   # green  <60
  elif awk "BEGIN{exit !($p > 90)}"; then c=$'\033[31m'   # red    >90
  else                                    c=$'\033[33m'   # yellow 60-90
  fi
  printf '%s%s\033[0m' "$c" "$txt"
}

# --- reset countdown: $1=unix ts -> "1d3h"/"3h20m"/"45m" (2-unit; "now"/empty edge) ---
reset_in() {
  local ts="$1" now delta d h m
  [ -z "$ts" ] && return
  now=$(date +%s)
  delta=$(( ts - now ))
  [ "$delta" -le 0 ] && { printf 'now'; return; }
  if [ "$delta" -ge 86400 ]; then
    d=$(( delta / 86400 )); h=$(( (delta % 86400) / 3600 ))
    if [ "$h" -gt 0 ]; then printf '%dd%dh' "$d" "$h"; else printf '%dd' "$d"; fi
  elif [ "$delta" -ge 3600 ]; then
    h=$(( delta / 3600 )); m=$(( (delta % 3600) / 60 ))
    if [ "$m" -gt 0 ]; then printf '%dh%dm' "$h" "$m"; else printf '%dh' "$h"; fi
  else
    printf '%dm' $(( delta / 60 ))
  fi
}

# --- one jq pass for every payload field ---
# \x1f (unit separator) delimiter, NOT tab: tab is IFS-whitespace so `read` would
# collapse consecutive tabs and shift every field after an empty one (e.g. a
# session with no rate_limits). \x1f is non-whitespace -> empty fields preserved.
IFS=$'\x1f' read -r model used_pct input_tok five_pct week_pct cwd fast cost_usd model_id five_reset week_reset < <(
  printf '%s' "$input" | jq -r '[
    (.model.display_name // "unknown"),
    (.context_window.used_percentage // ""),
    (.context_window.total_input_tokens // 0),
    (.rate_limits.five_hour.used_percentage // ""),
    (.rate_limits.seven_day.used_percentage // ""),
    (.cwd // .workspace.current_dir // ""),
    (.fast_mode // false),
    (.cost.total_cost_usd // 0),
    (.model.id // ""),
    (.rate_limits.five_hour.resets_at // ""),
    (.rate_limits.seven_day.resets_at // "")
  ] | map(tostring) | join("")'
)
[ -z "$model" ] && model="unknown"

# --- model (compact) + fast-mode flag ---
model="${model%% (*}"                       # "Opus 4.8 (1M context)" -> "Opus 4.8"
[ "$fast" = "true" ] && model="⚡$model"

# --- context % (colored) + absolute input tokens ---
if [ -n "$used_pct" ]; then
  ctx_seg=$(color_pct "$used_pct" "ctx:$(printf '%.0f%%' "$used_pct")")
else
  ctx_seg="ctx:--"
fi
if [ "${input_tok:-0}" -ge 1000 ] 2>/dev/null; then
  toks=$(awk "BEGIN{printf \"%.0fk\", ${input_tok}/1000}")
else
  toks="${input_tok:-0}"
fi

# --- per-session cost (Claude Code's own cache/model-aware accounting) ---
# adaptive precision: whole dollars once it's real money, cents when small.
cost=$(awk "BEGIN{c=${cost_usd:-0}; if(c>=100) printf \"%.0f\",c; else if(c>=10) printf \"%.1f\",c; else printf \"%.2f\",c}")

# --- rate limits (each % colored on the same thresholds, + reset countdown) ---
_rl() {  # $1=pct $2=label $3=reset_ts -> "<label>:NN%·<reset>"
  local seg r; seg=$(color_pct "$1" "$2:$(printf '%.0f' "$1")%")
  r=$(reset_in "$3"); [ -n "$r" ] && seg="$seg·$r"
  printf '%s' "$seg"
}
rate=""
[ -n "$five_pct" ] && rate=$(_rl "$five_pct" "5h" "$five_reset")
if [ -n "$week_pct" ]; then
  wk=$(_rl "$week_pct" "7d" "$week_reset")
  [ -n "$rate" ] && rate="$rate "
  rate="${rate}${wk}"
fi

# --- project tag + git state (branch, dirty, unpushed) ---
proj=""; gitseg=""
if [ -n "$cwd" ]; then
  proj=$(basename "$cwd")
  if git -C "$cwd" rev-parse --git-dir >/dev/null 2>&1; then
    br=$(git -C "$cwd" branch --show-current 2>/dev/null)
    [ -z "$br" ] && br=$(git -C "$cwd" rev-parse --short HEAD 2>/dev/null)
    dirty=$(git -C "$cwd" status --porcelain 2>/dev/null | grep -c .)
    ahead=$(git -C "$cwd" rev-list --count '@{upstream}..HEAD' 2>/dev/null)
    gitseg="⎇${br:-?}"
    [ "${dirty:-0}" -gt 0 ] 2>/dev/null && gitseg="$gitseg *$dirty"
    [ -n "$ahead" ] && [ "$ahead" -gt 0 ] 2>/dev/null && gitseg="$gitseg ↑$ahead"
  fi
fi

# --- althing: unread count (📬 N) + waiter-armed (🔔 armed / 🔕 not) ---
# v3 (the post office, 2026-08-28). ⚠ This block used to gate on
# `command -v althing-cli`, which the v3 cutover DELETED -- so the whole segment,
# badge and bell both, silently disappeared for every session on this box. That is
# worse than a cosmetic loss: 71 of 73 handles are pull-only (no waiter armed, never
# poked), and this badge is the ONLY out-of-band signal telling such a session it has
# mail waiting. A dead statusline made the new bus look like an empty one.
althing=""; mon=""
if command -v postbox >/dev/null 2>&1; then
  # postbox has NO default post-office address and the statusline runs in a bare
  # shell with neither var set. Hardcoded here deliberately: an unset address makes
  # postbox error, which in a must-never-crash segment is indistinguishable from
  # "no mail" -- the exact conflation v3 exists to prevent.
  export ALTHING_POST_OFFICE="${ALTHING_POST_OFFICE:-http://10.100.50.40:8390}"
  h="${ALTHING_HANDLE:-}"
  # ALTHING_HANDLE isn't set in the statusline env, so resolve the handle from the
  # cwd Claude Code passes on stdin.
  #
  # ⚠ PREFER launch-history.json. session_handles.json is a **v2 artifact** — v3's
  # postbox never opens it (`grep -rn session_handles althing/` is empty; resolve_config
  # takes --handle then ALTHING_HANDLE and nothing else), and the tool that used to
  # maintain it, `althing-cli use`, was deleted at the cutover. Whatever is in it now is
  # hand-kept and drifts silently.
  #
  # launch-history.json is written by dev_launch, which is the thing that sets
  # ALTHING_HANDLE in the first place, so it is the real cwd->handle binding. Shape is
  # {cwd: {command: {at, handle}}} with several commands per directory (claude, kimi,
  # grok), so take the most recent by `at` rather than whichever key sorts first.
  if [ -z "$h" ] && [ -n "$cwd" ]; then
    h=$(jq -r --arg d "$cwd" '(.[$d] // {}) | to_entries | max_by(.value.at) | .value.handle // empty' \
          "$HOME/.althing/launch-history.json" 2>/dev/null)
    # Fallback only: broader coverage, but frozen and hand-maintained.
    [ -z "$h" ] && h=$(jq -r --arg d "$cwd" '.[$d] // empty' "$HOME/.althing/session_handles.json" 2>/dev/null)
  fi
  if [ -n "$h" ]; then
    # `timeout` is load-bearing, not belt-and-braces: v2 read a local SQLite file,
    # v3 makes an HTTP call. An unreachable post office must cost this segment two
    # seconds and nothing else — a statusline that hangs blocks the whole prompt.
    unread=$(timeout 2 postbox --handle "$h" status --json 2>/dev/null </dev/null | jq -r '.unread // 0' 2>/dev/null)
    case "${unread:-0}" in ''|0|*[!0-9]*) : ;; *) althing="📬 $unread" ;; esac
    # v3 has ONE arming mechanism where v2 had three: `althing-listen` takes
    # wake-listener-<handle>.lock. monitor-*.lock and light-monitor-*.lock belonged
    # to binaries that no longer exist. kill -0 discards a crashed listener's lock.
    mon="🔕"
    lk="$HOME/.althing/wake-listener-$h.lock"
    if [ -f "$lk" ]; then
      pid=$(tr -dc '0-9' < "$lk" 2>/dev/null)
      [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null && mon="🔔"
    fi
  fi
fi

# --- assemble ---
lead="$althing"
[ -n "$mon" ] && lead="${lead:+$lead }$mon"
pg="$proj"
[ -n "$gitseg" ] && pg="${pg:+$pg }$gitseg"

parts="$lead"
[ -n "$pg" ] && parts="${parts:+$parts | }$pg"
parts="${parts:+$parts | }$model | $ctx_seg $toks | \$$cost"
[ -n "$rate" ] && parts="$parts | $rate"

printf '%s' "$parts"
