#!/usr/bin/env bash
# purge.sh  -  deletion backend for /multi-agent:purge (full reset).
#
# Wipes every multi-agent worktree under <repo>/.worktrees/ (registered or
# orphaned), deletes the local branches those worktrees carried, and resets
# the project's task counter at
#   $HOME/.claude/logs/multi-agent/<project>/.counter
#
# The confirmation UX (double confirmation, remote-branch question) lives in
# the /multi-agent:purge skill; this script is the mechanical part. Task log
# dirs are /multi-agent:prune-logs territory (prune-logs.sh --project=<name>).
#
# PRESERVED ALWAYS:
#   - the repo's main worktree, its checked-out branch, and main/master/develop
#   - everything outside <repo>/.worktrees/ (symlinked entries are never
#     followed; every rm target must resolve strictly inside .worktrees/)
#   - the audit trail + metrics corpus at the log root (only the project's
#     .counter file is removed there)
#
# SAFE BY DEFAULT: dry-run. Lists what WOULD be removed; deletes nothing until
# you pass --yes. Remote branches are deleted only with --delete-remote AND
# --yes; a missing/unreachable remote is a warning, not a failure.
#
# Usage:
#   purge.sh                        # dry-run in the current repo
#   purge.sh --yes                  # wipe worktrees + branches + counter
#   purge.sh --repo <path>          # operate on another repo
#   purge.sh --delete-remote --yes  # also delete the remote branches
#
# Env:
#   PURGE_LOG_ROOT   override the log root (default $HOME/.claude/logs/multi-agent)
#
# Exit: 0 on success (including "nothing to do"), 2 on usage error or refused
# repo.

set -uo pipefail

REPO="$PWD"
DELETE=0
DELETE_REMOTE=0
LOG_ROOT="${PURGE_LOG_ROOT:-$HOME/.claude/logs/multi-agent}"

usage() {
  grep -E '^#( |$)' "$0" | sed -E 's/^# ?//'
}

while [ $# -gt 0 ]; do
  case "$1" in
    --yes | --force) DELETE=1; shift ;;
    --delete-remote) DELETE_REMOTE=1; shift ;;
    --repo)
      REPO="${2:-}"
      [ -z "$REPO" ] && { echo "purge: --repo needs a path" >&2; exit 2; }
      shift 2
      ;;
    -h | --help) usage; exit 0 ;;
    *) echo "purge: unknown argument: $1" >&2; exit 2 ;;
  esac
done

if ! git -C "$REPO" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
  echo "purge: $REPO is not a git work tree  -  nothing to do"
  exit 0
fi

# Anchor everything on the MAIN worktree (first porcelain entry), so running
# purge from inside a linked worktree still targets <repo>/.worktrees/.
MAIN_WT="$(git -C "$REPO" worktree list --porcelain 2>/dev/null | sed -n 's/^worktree //p' | head -1)"
MAIN_WT="$(cd "$MAIN_WT" 2>/dev/null && pwd -P)"
HOME_REAL="$(cd "$HOME" 2>/dev/null && pwd -P || printf '%s' "$HOME")"
if [ -z "$MAIN_WT" ] || [ "$MAIN_WT" = "/" ] || [ "$MAIN_WT" = "$HOME_REAL" ]; then
  echo "purge: refusing to operate on repo root '$MAIN_WT'" >&2
  exit 2
fi
WT_ROOT="$MAIN_WT/.worktrees"
CURRENT_BRANCH="$(git -C "$MAIN_WT" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"

# Branches that must survive a purge no matter what a worktree carried.
is_protected_branch() {
  case "$1" in
    "" | main | master | develop | "$CURRENT_BRANCH") return 0 ;;
  esac
  return 1
}

resolve_dir() {
  cd "$1" 2>/dev/null && pwd -P
}

# Collect registered worktrees that live strictly inside .worktrees/, with
# the branch each carries (empty when detached). bash 3.2: parallel arrays.
wt_paths=()
wt_branches=()
cur_path=""
cur_branch=""
flush_block() {
  [ -z "$cur_path" ] && { cur_branch=""; return 0; }
  local resolved
  resolved="$(resolve_dir "$cur_path")"
  case "$resolved" in
    "$WT_ROOT"/*)
      wt_paths+=("$resolved")
      wt_branches+=("$cur_branch")
      ;;
  esac
  cur_path=""
  cur_branch=""
}
while IFS= read -r line; do
  case "$line" in
    worktree\ *) flush_block; cur_path="${line#worktree }" ;;
    branch\ refs/heads/*) cur_branch="${line#branch refs/heads/}" ;;
  esac
done <<EOF
$(git -C "$MAIN_WT" worktree list --porcelain 2>/dev/null)
EOF
flush_block

# Orphan dirs under .worktrees/ that git no longer registers.
orphans=()
if [ -d "$WT_ROOT" ]; then
  for d in "$WT_ROOT"/*/; do
    [ -d "$d" ] || continue
    [ -L "${d%/}" ] && continue # never follow symlinked entries out of the tree
    dir="$(resolve_dir "$d")"
    [ -z "$dir" ] && continue
    case "$dir" in "$WT_ROOT"/*) ;; *) continue ;; esac
    reg=0
    for p in ${wt_paths+"${wt_paths[@]}"}; do
      [ "$p" = "$dir" ] && { reg=1; break; }
    done
    [ "$reg" -eq 1 ] && continue
    orphans+=("$dir")
  done
fi

# Counter file for this project.
PROJECT="$(basename "$MAIN_WT")"
COUNTER=""
if [ -n "$PROJECT" ] && [ "$PROJECT" != "." ] && [ "$PROJECT" != ".." ] &&
  [ -f "$LOG_ROOT/$PROJECT/.counter" ]; then
  COUNTER="$LOG_ROOT/$PROJECT/.counter"
fi

total=$((${#wt_paths[@]} + ${#orphans[@]}))
if [ "$total" -eq 0 ] && [ -z "$COUNTER" ]; then
  echo "purge: no multi-agent worktrees, branches, or counter in $MAIN_WT  -  nothing to do"
  exit 0
fi

if [ "$DELETE" -eq 0 ]; then
  echo "DRY-RUN  -  purge would remove from $MAIN_WT:"
  i=0
  while [ "$i" -lt "${#wt_paths[@]}" ]; do
    br="${wt_branches[$i]}"
    echo "  worktree ${wt_paths[$i]#"$MAIN_WT"/} (branch: ${br:-detached})"
    if ! is_protected_branch "$br"; then
      echo "  local branch $br"
      [ "$DELETE_REMOTE" -eq 1 ] && echo "  remote branch origin/$br"
    fi
    i=$((i + 1))
  done
  for d in ${orphans+"${orphans[@]}"}; do
    echo "  orphan worktree dir ${d#"$MAIN_WT"/}"
  done
  [ -n "$COUNTER" ] && echo "  task counter $COUNTER"
  echo "Main worktree, current branch, and main/master/develop are never touched."
  echo "Re-run with --yes to delete."
  exit 0
fi

removed_wt=0
removed_br=0
i=0
while [ "$i" -lt "${#wt_paths[@]}" ]; do
  p="${wt_paths[$i]}"
  br="${wt_branches[$i]}"
  i=$((i + 1))
  wt_gone=0
  if git -C "$MAIN_WT" worktree remove --force "$p" >/dev/null 2>&1; then
    wt_gone=1
  else
    # Fall back to a guarded rm + prune when git refuses (locked, damaged).
    case "$p" in
      "$WT_ROOT"/*)
        rm -rf "$p" 2>/dev/null && wt_gone=1
        git -C "$MAIN_WT" worktree prune >/dev/null 2>&1
        ;;
      *) echo "purge: skipping worktree outside $WT_ROOT: $p" >&2 ;;
    esac
  fi
  if [ "$wt_gone" -eq 1 ]; then
    removed_wt=$((removed_wt + 1))
    echo "→ removed worktree ${p#"$MAIN_WT"/}"
  else
    echo "purge: warning: could not remove worktree $p" >&2
    continue
  fi
  if is_protected_branch "$br"; then
    [ -n "$br" ] && echo "→ kept protected branch $br"
    continue
  fi
  if git -C "$MAIN_WT" branch -D "$br" >/dev/null 2>&1; then
    removed_br=$((removed_br + 1))
    echo "→ deleted local branch $br"
  fi
  if [ "$DELETE_REMOTE" -eq 1 ]; then
    if git -C "$MAIN_WT" push origin --delete "$br" >/dev/null 2>&1; then
      echo "→ deleted remote branch origin/$br"
    else
      echo "purge: warning: could not delete remote branch origin/$br (no remote?)" >&2
    fi
  fi
done

for d in ${orphans+"${orphans[@]}"}; do
  # Containment guard: resolve again right before rm.
  dir="$(resolve_dir "$d")"
  case "$dir" in
    "$WT_ROOT"/*)
      rm -rf "$dir" 2>/dev/null && { removed_wt=$((removed_wt + 1)); echo "→ removed orphan worktree dir ${dir#"$MAIN_WT"/}"; }
      ;;
    *) echo "purge: skipping orphan outside $WT_ROOT: $d" >&2 ;;
  esac
done

if [ -n "$COUNTER" ]; then
  rm -f "$COUNTER" 2>/dev/null && echo "→ reset task counter $COUNTER"
fi

# Drop the now-empty .worktrees/ shell (only when truly empty).
[ -d "$WT_ROOT" ] && rmdir "$WT_ROOT" 2>/dev/null

echo "══ purge: removed $removed_wt worktree(s) and $removed_br local branch(es) in $MAIN_WT ══"
exit 0
