#!/usr/bin/env bash
# od-worktree — create/remove git worktrees INSIDE the repo, never in $HOME.
#
# CONTRACT
#   od-worktree add <name> [<committish>]   -> <repo-root>/.worktrees/<name> on branch wt/<name>
#   od-worktree list                        -> every worktree of this repo, flagging out-of-repo ones
#   od-worktree remove <name> [--force]
#   od-worktree adopt <path>                -> move an existing out-of-repo worktree into .worktrees/
#
# <name> is a bare slug — never a path. A path, an absolute destination, or any escape via ".."
# is refused. There is no flag to place a worktree outside the repo.

set -euo pipefail

die() { printf 'od-worktree: %s\n' "$1" >&2; exit 2; }

REPO_ROOT="$(git rev-parse --path-format=absolute --show-toplevel 2>/dev/null)" || die "not inside a git repository"
COMMON_DIR="$(git rev-parse --path-format=absolute --git-common-dir)"
MAIN_ROOT="$(cd "$COMMON_DIR/.." && pwd)"
WT_DIR="$MAIN_ROOT/.worktrees"
DISK_FLOOR_KIB=${OD_WORKTREE_MIN_FREE_KIB:-$((1024 * 1024))}
[[ "$DISK_FLOOR_KIB" =~ ^[0-9]{1,12}$ ]] || die "OD_WORKTREE_MIN_FREE_KIB must be an integer from 0 to 999999999999 KiB"
DISK_FLOOR_KIB=$((10#$DISK_FLOOR_KIB))

admit_disk() {
  local free_kib
  free_kib=$(df -Pk "$MAIN_ROOT" | awk 'NR == 2 { print $4 }') || die "disk capacity unknown for $MAIN_ROOT"
  [[ "$free_kib" =~ ^[0-9]+$ ]] || die "disk capacity unknown for $MAIN_ROOT"
  if ((free_kib < DISK_FLOOR_KIB)); then
    printf 'od-worktree: disk admission refused on %s: free=%s KiB required=%s KiB\n' "$MAIN_ROOT" "$free_kib" "$DISK_FLOOR_KIB" >&2
    exit 75
  fi
}

# shim-drift-check runs from a deploy clone with no link back to a dev checkout, so the
# checkouts that hold worktrees are recorded where it can find them.
register_checkout() {
  local file="${OD_SHIM_REPOS_FILE:-$HOME/.local/state/overdeck/shim-repos}" tmp ok=1
  if [[ -r "$file" ]] && grep -qxF "$MAIN_ROOT" "$file"; then
    return 0
  fi
  tmp="$file.$$"
  if mkdir -p "$(dirname "$file")"; then
    if [[ -f "$file" ]]; then
      cp "$file" "$tmp" || ok=0
    else
      : >"$tmp" || ok=0
    fi
    if ((ok)); then
      printf '%s\n' "$MAIN_ROOT" >>"$tmp" && mv "$tmp" "$file" || ok=0
    fi
  else
    ok=0
  fi
  if ((ok == 0)); then
    rm -f "$tmp"
    printf 'od-worktree: cannot record %s in %s — shim-drift-check will not see this checkout\n' "$MAIN_ROOT" "$file" >&2
  fi
}
register_checkout

require_slug() {
  local name="${1:-}"
  [[ -n "$name" ]] || die "name required"
  [[ "$name" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]] || die "name must be a bare slug (got '$name'); worktrees are always created under $WT_DIR"
}

cmd_add() {
  local name="${1:-}" base="${2:-}"
  require_slug "$name"
  local dest="$WT_DIR/$name"
  [[ -e "$dest" ]] && die "$dest already exists"
  admit_disk
  mkdir -p "$WT_DIR"
  if [[ -z "$base" ]]; then
    base="$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || echo origin/main)"
  fi
  local branch="wt/$name"
  if git show-ref --verify --quiet "refs/heads/$branch"; then
    git worktree add "$dest" "$branch"
  else
    git worktree add -b "$branch" "$dest" "$base"
  fi
  associate_request
  printf '%s\n' "$dest"
}

# S4: a worktree claim, by itself, is not enough evidence to CREATE a request row with an
# owner's-words title (no paraphrase — see docs/plans/2026-08-14-request-intake.md S1). But two
# cases ARE decidable from the invocation payload alone, best-effort, never failing this command:
#   1. the owner's ask already has a matching open, unclaimed row for this project — claiming a
#      worktree for it IS the moment work visibly starts, so associate (never create, never
#      duplicate).
#   2. no such row exists — the agent is starting work on its own judgement, with no owner ask
#      behind it. That is still real work the owner should see, so write a NEW row, explicitly
#      marked `--origin agent-judgement` (never `owner`, never paraphrased conversation text —
#      title is derived only from the worktree slug, the one concrete payload this seam has).
associate_request() {
  local project od_requests candidates raw_count count id slug title
  project="$(basename "$MAIN_ROOT")"
  slug="$name"
  od_requests="$(command -v od-requests 2>/dev/null || true)"
  [[ -n "$od_requests" ]] || return 0
  candidates="$("$od_requests" list --project "$project" --state asked --json 2>/dev/null)" || return 0
  raw_count="$(printf '%s' "$candidates" | bun -e 'const t = await Bun.stdin.text(); console.log(t.trim() ? JSON.parse(t).length : 0);' 2>/dev/null)" || return 0
  if [[ "$raw_count" != "0" ]]; then
    # Any open `asked` row exists for this project — even one already claimed by someone
    # else IS a matching row, so never create a second one for the same project here.
    count="$(printf '%s' "$candidates" | bun -e 'const rows = JSON.parse(await Bun.stdin.text()).filter((r) => !r.worker); console.log(rows.length);' 2>/dev/null)" || return 0
    if [[ "$count" == "1" ]]; then
      id="$(printf '%s' "$candidates" | bun -e 'const rows = JSON.parse(await Bun.stdin.text()).filter((r) => !r.worker); console.log(rows[0]?.id ?? "");' 2>/dev/null)" || return 0
      [[ -n "$id" ]] || return 0
      "$od_requests" claim "$id" >/dev/null 2>&1 || true
    fi
    return 0
  fi
  title="Agent started work on: ${slug//[-_]/ }"
  id="$("$od_requests" add "$title" --project "$project" --origin agent-judgement 2>/dev/null | grep -oE 'created #[^ ;]+' | cut -d'#' -f2)" || return 0
  [[ -n "$id" ]] || return 0
  "$od_requests" claim "$id" >/dev/null 2>&1 || true
}

cmd_list() {
  git worktree list --porcelain | awk -v root="$MAIN_ROOT" '
    /^worktree /{
      path = substr($0, 10)
      inside = (index(path, root "/") == 1 || path == root)
      printf "%s%s\n", path, inside ? "" : "   <- OUTSIDE THE REPO"
    }'
}

cmd_remove() {
  local name="${1:-}"; shift || true
  require_slug "$name"
  git worktree remove "$WT_DIR/$name" "$@"
}

cmd_adopt() {
  local src="${1:-}"
  [[ -n "$src" ]] || die "path required"
  src="$(cd "$src" && pwd)" || die "no such directory"
  [[ "$src" == "$MAIN_ROOT" ]] && die "refusing to move the main checkout"
  case "$src" in "$MAIN_ROOT"/*) die "$src is already inside the repo";; esac
  git worktree list --porcelain | grep -qx "worktree $src" || die "$src is not a worktree of this repo"
  mkdir -p "$WT_DIR"
  local dest="$WT_DIR/$(basename "$src")"
  [[ -e "$dest" ]] && die "$dest already exists"
  git worktree move "$src" "$dest"
  printf '%s\n' "$dest"
}

case "${1:-}" in
  add)    shift; cmd_add "$@";;
  list)   shift; cmd_list "$@";;
  remove) shift; cmd_remove "$@";;
  adopt)  shift; cmd_adopt "$@";;
  *) die "usage: od-worktree {add <name> [<base>]|list|remove <name>|adopt <path>}";;
esac
