#!/usr/bin/env bash
# ~/.claude/statusline.sh
# Pastel-powerline statusline for Claude Code.
# Colors mirror the starship pastel-powerline preset exactly.
# Requires: jaq (ou jq), git (optional), node (optional)
#
# Optimisations perf/mémoire (vs version précédente) :
#   - stdin lu par le builtin `read -d ''` au lieu de `cat` (0 fork)
#   - 1 seul parse JSON (jaq @tsv) au lieu de 5 appels jq
#   - git : 1 seul `status --porcelain=v2 --branch` au lieu de 5 procs + 2 grep
#   - hex_to_ansi via `printf -v` (0 fork) au lieu de ~20 command-substitutions
#   - node : version mise en cache (invalidée par mtime du binaire) → 0 spawn V8 à chaud
#   - pourcentages/couleurs via `printf -v`/globals au lieu de $(...)
#   → ~30-40 forks + V8 par rendu  ⇒  ~1-2 forks à chaud.

# ---------- helpers ----------

# hex_to_ansi <out_var> <#rrggbb> fg|bg   — assigne sans sous-shell.
hex_to_ansi() {
  local hex="${2#\#}"
  local r=$((16#${hex:0:2})) g=$((16#${hex:2:2})) b=$((16#${hex:4:2}))
  if [[ "$3" == "bg" ]]; then
    printf -v "$1" '\e[48;2;%d;%d;%dm' "$r" "$g" "$b"
  else
    printf -v "$1" '\e[38;2;%d;%d;%dm' "$r" "$g" "$b"
  fi
}

printf -v RESET '\e[0m'
printf -v BOLD  '\e[1m'

# Shades of Purple palette (aligné sur le thème terminal Ghostty, fond #1e1e3f)
C_OS='#7857fe'      # violet  – apple icon
C_DIR='#fb94ff'     # magenta – directory
C_GIT='#fad000'     # jaune   – git branch
C_NODE='#3ad900'    # vert    – nodejs version
C_MODEL='#6943ff'   # indigo  – model
C_EFFORT='#80fcff'  # cyan    – reasoning effort
C_CTX='#80fcff'     # cyan    – context usage (base / "% left")

FG_LIGHT='#FFFFFF'
FG_DARK='#1e1e3f'   # fond du thème : texte foncé lisible sur les segments clairs

SEP=''  # U+E0B0 powerline solid right-arrow

# Couleur d'une jauge d'usage selon seuil (Shades of Purple).
#   <70 : cyan  ·  >=70 : jaune  ·  >=90 : rose   →  positionne USAGE_BG / USAGE_FG
USAGE_BG="" USAGE_FG=""
usage_colors() {
  local p="${1:-0}"
  if   (( p >= 90 )); then USAGE_BG='#ff2c70'; USAGE_FG="$FG_LIGHT"  # rose
  elif (( p >= 70 )); then USAGE_BG='#fad000'; USAGE_FG="$FG_DARK"   # jaune
  else                     USAGE_BG="$C_CTX";  USAGE_FG="$FG_DARK"   # cyan
  fi
}

# ---------- chained segment builder ----------
# Tracks the previous segment's bg color so each new segment draws its own
# left-arrow separator in (prev fg on next bg).
PREV_BG=""
OUTPUT=""

# add_segment <bg-hex> <fg-hex> <text>
add_segment() {
  local bg="$1" fg="$2" text="$3" bg_ansi fg_ansi sep_fg
  hex_to_ansi bg_ansi "$bg" bg
  hex_to_ansi fg_ansi "$fg" fg
  if [[ -n "$PREV_BG" ]]; then
    hex_to_ansi sep_fg "$PREV_BG" fg          # sep bg == this segment's bg
    OUTPUT+="${RESET}${sep_fg}${bg_ansi}${SEP}${RESET}"
  fi
  OUTPUT+="${bg_ansi}${fg_ansi}${BOLD}${text}${RESET}"
  PREV_BG="$bg"
}

# close_powerline – append the final right-arrow in (prev fg on default bg)
close_powerline() {
  [[ -n "$PREV_BG" ]] || return
  local sep_fg
  hex_to_ansi sep_fg "$PREV_BG" fg
  OUTPUT+="${RESET}${sep_fg}${SEP}${RESET}"
}

# ---------- read JSON from stdin ----------

# `read -d ''` lit tout stdin dans INPUT via le builtin (pas de `cat`).
# Retourne non-zéro à EOF (pas de NUL) mais INPUT est rempli → `|| true`.
IFS= read -r -d '' INPUT || true

# 5 appels jq -> 1 seul parse (@tsv). Préfère jaq, retombe sur jq.
# Contexte absent -> "" (test -n conservé) ; quota absent -> -1 (segment masqué).
JQ=$(command -v jaq || command -v jq) || { printf 'jaq/jq introuvable'; exit 0; }
IFS=$'\x1f' read -r cwd model effort used_pct remaining_pct five_h seven_d < <(
  "$JQ" -r '[
    (.cwd // .workspace.current_dir // ""),
    (.model.display_name // .model.id // "Claude"),
    (.effort.level // ""),
    (.context_window.used_percentage // ""),
    (.context_window.remaining_percentage // ""),
    (.rate_limits.five_hour.used_percentage  // -1 | floor),
    (.rate_limits.seven_day.used_percentage  // -1 | floor)
  ] | map(tostring) | join("")' <<< "$INPUT"
)

# blindage : parser absent ou champ vide -> entier valide, le segment quota se masque
[[ "$five_h"  =~ ^-?[0-9]+$ ]] || five_h=-1
[[ "$seven_d" =~ ^-?[0-9]+$ ]] || seven_d=-1

# ---------- segment: macOS icon ----------
#  = Apple Nerd Font glyph (U+F8FF)
add_segment "$C_OS" "$FG_LIGHT" "  "

# ---------- segment: directory ----------
# If cwd is under ~/Github/<repo>, render as " <repo>[/<subpath>]"
# Otherwise: keep starship behaviour (~ for $HOME, last 3 components)

if [[ -n "$cwd" ]]; then
  github_root="$HOME/Github"
  if [[ "$cwd" == "$github_root" ]]; then
    short_dir=" "
  elif [[ "$cwd" == "$github_root"/* ]]; then
    rest="${cwd#"$github_root"/}"     # e.g. "fish" or "fish/src/lib"
    IFS='/' read -ra rparts <<< "$rest"
    if (( ${#rparts[@]} <= 3 )); then
      short_dir="  ${rest}"
    else
      short_dir="  ${rparts[0]}/…/${rparts[*]: -2:1}/${rparts[*]: -1:1}"
    fi
  else
    dir="${cwd/#$HOME/~}"
    IFS='/' read -ra raw_parts <<< "$dir"
    parts=()
    for p in "${raw_parts[@]}"; do
      [[ -n "$p" ]] && parts+=("$p")
    done
    total=${#parts[@]}
    if [[ "$dir" == "~" ]] || (( total <= 3 )); then
      short_dir="$dir"
    else
      short_dir="…/${parts[*]: -3:1}/${parts[*]: -2:1}/${parts[*]: -1:1}"
    fi
  fi
else
  short_dir="~"
fi

add_segment "$C_DIR" "$FG_DARK" " ${short_dir} "

# ---------- segment: git branch + status ----------
#  = git branch Nerd Font glyph (U+E0A0 / starship default)
# 1 seul `status --porcelain=v2 --branch` : branche, ahead/behind ET dirty d'un coup.
# Dirty marker starship-style [!] (unstaged/untracked) / [+] (staged-only).
if [[ -n "$cwd" ]]; then
  git_out=$(git -C "$cwd" --no-optional-locks status --porcelain=v2 --branch 2>/dev/null)
  if [[ $? -eq 0 && -n "$git_out" ]]; then
    branch="" oid="" ahead=0 behind=0 unstaged=0 staged=0 untracked=0
    while IFS= read -r line; do
      case "$line" in
        '# branch.head '*) branch="${line#\# branch.head }" ;;
        '# branch.oid '*)  oid="${line#\# branch.oid }" ;;
        '# branch.ab '*)
          ab="${line#\# branch.ab }"            # "+A -B"
          ahead="${ab%% *}";  ahead="${ahead#+}"
          behind="${ab##* }"; behind="${behind#-}"
          ;;
        '?'*) untracked=1 ;;
        'u '*) unstaged=1 ;;                     # unmerged → dirty
        '1 '*|'2 '*)
          xy="${line:2:2}"                       # champ XY (staged, unstaged)
          [[ "${xy:1:1}" != "." ]] && unstaged=1
          [[ "${xy:0:1}" != "." ]] && staged=1
          ;;
      esac
    done <<< "$git_out"

    # detached HEAD → short SHA
    [[ "$branch" == "(detached)" ]] && branch="${oid:0:7}"

    flags=""
    if   (( untracked || unstaged )); then flags="[!]"
    elif (( staged ));               then flags="[+]"
    fi
    (( ahead  > 0 )) && flags+="⇡${ahead}"
    (( behind > 0 )) && flags+="⇣${behind}"

    branch_text="  ${branch}"
    [[ -n "$flags" ]] && branch_text+=" ${flags}"
    add_segment "$C_GIT" "$FG_DARK" "${branch_text} "
  fi
fi

# ---------- segment: nodejs version ----------
#  = nodejs Nerd Font glyph (nf-dev-nodejs U+E718, used by starship)
# Shown only when the project looks like a JS/TS project.
node_relevant=0
if [[ -n "$cwd" ]]; then
  for marker in package.json .nvmrc .node-version pnpm-workspace.yaml bun.lockb deno.json; do
    [[ -e "$cwd/$marker" ]] && { node_relevant=1; break; }
  done
  if (( ! node_relevant )); then
    # Also check any JS/TS file in the immediate dir
    shopt -s nullglob
    js_files=("$cwd"/*.{js,mjs,cjs,ts,tsx,jsx})
    shopt -u nullglob
    (( ${#js_files[@]} > 0 )) && node_relevant=1
  fi
fi

if (( node_relevant )); then
  node_bin=$(command -v node 2>/dev/null)
  if [[ -n "$node_bin" ]]; then
    # Cache la version node, clé = chemin du binaire (mise/nvm → chemin versionné).
    # Invalidé dès que le binaire change (mtime) ⇒ aucun spawn V8 à chaud.
    cache_file="${TMPDIR:-/tmp}/ccsl-node${node_bin//\//_}"
    if [[ -s "$cache_file" && "$cache_file" -nt "$node_bin" ]]; then
      node_ver=$(<"$cache_file")               # builtin, pas de fork
    else
      node_ver=$(node -v 2>/dev/null)          # e.g. "v22.5.1"
      [[ -n "$node_ver" ]] && printf '%s' "$node_ver" > "$cache_file"
    fi
    [[ -n "$node_ver" ]] && add_segment "$C_NODE" "$FG_DARK" "  ${node_ver} "
  fi
fi

# ---------- segment: model ----------
add_segment "$C_MODEL" "$FG_LIGHT" " ${model} "

# ---------- segment: reasoning effort ----------
# 󰧑 = nf-md-brain (U+F09D1)
if [[ -n "$effort" ]]; then
  add_segment "$C_EFFORT" "$FG_DARK" " 󰧑 ${effort} "
fi

# ---------- segment: context usage (couleur selon seuil) ----------
if [[ -n "$used_pct" ]]; then
  printf -v pct_int '%.0f' "$used_pct"
  usage_colors "$pct_int"
  add_segment "$USAGE_BG" "$USAGE_FG" " ${pct_int}% ctx "
elif [[ -n "$remaining_pct" ]]; then
  printf -v pct_int '%.0f' "$remaining_pct"
  add_segment "$C_CTX" "$FG_DARK" " ${pct_int}% left "
fi

# ---------- segment: quota Max 5h / 7j ----------
# Masqué tant que rate_limits n'est pas sur stdin (avant la 1re réponse API, ou non-Max).
if (( five_h >= 0 || seven_d >= 0 )); then
  fh=$(( five_h  < 0 ? 0 : five_h ))
  sd=$(( seven_d < 0 ? 0 : seven_d ))
  worst=$(( fh > sd ? fh : sd ))
  usage_colors "$worst"
  q=""
  (( five_h  >= 0 )) && q="5h ${fh}%"
  (( five_h >= 0 && seven_d >= 0 )) && q+=" · "
  (( seven_d >= 0 )) && q+="7d ${sd}%"
  add_segment "$USAGE_BG" "$USAGE_FG" " ${q} "
fi

close_powerline

printf '%s' "$OUTPUT"
