#!/usr/bin/env bash
#
# worktree-prepare.sh  -  create (or re-enter) one task worktree, healed and
# excluded. Phase 0 Step 6b calls it once per repo; worktree-finalize.sh is its
# counterpart at the other end of the run.
#
# In order, for one repo:
#   1. heal stale admin state: a stale lock on the path is cleared and
#      `worktree prune` drops entries whose directory is gone. A run killed
#      mid-add leaves one behind, and the retry then fails with "already exists".
#   2. the residue guard: lib/repo-hygiene.sh keeps .worktrees/, .pipeline/ and
#      the loose logs out of the index through the clone-local exclude file.
#   3. a path still registered and readable after the heal is entered and
#      fast-forwarded, not re-added; paths are compared physically, so a path
#      named through a symlink still matches its registration. Otherwise the
#      worktree is added on <branch> when that branch already exists (a
#      re-entered task keeps its commits), else `worktree add -b <branch>
#      origin/<base>`.
#   4. the identity, when given, is set on the worktree.
#
# One simple command per repo, so an unattended run can take this step under
# the guard, which refuses shell control flow.
#
# Usage:
#   worktree-prepare.sh --project <repo-root> --path <worktree> --branch <name>
#                       --base <base-branch> [--name <git user.name>]
#                       [--email <git user.email>]
#
# Output (stdout, JSON): {project, worktreePath, branch, baseBranch,
#   action: created|reused|failed, pulled: true|false|null, error}
#
# Exit: 0 ready, 1 the add failed (nothing was created; the caller runs its
#       rollback / collision flow), 2 usage.
set -uo pipefail

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT=""; WT=""; BRANCH=""; BASE=""; NAME=""; EMAIL=""

usage() {
  echo "usage: worktree-prepare.sh --project <repo-root> --path <worktree> --branch <name> --base <base-branch> [--name <n>] [--email <e>]" >&2
  exit 2
}

while [ "$#" -gt 0 ]; do
  case "$1" in
    --project) [ "$#" -ge 2 ] || usage; PROJECT="$2"; shift 2 ;;
    --path) [ "$#" -ge 2 ] || usage; WT="$2"; shift 2 ;;
    --branch) [ "$#" -ge 2 ] || usage; BRANCH="$2"; shift 2 ;;
    --base) [ "$#" -ge 2 ] || usage; BASE="$2"; shift 2 ;;
    --name) [ "$#" -ge 2 ] || usage; NAME="$2"; shift 2 ;;
    --email) [ "$#" -ge 2 ] || usage; EMAIL="$2"; shift 2 ;;
    *) usage ;;
  esac
done
[ -n "$PROJECT" ] && [ -n "$WT" ] && [ -n "$BRANCH" ] && [ -n "$BASE" ] || usage
command -v jq >/dev/null 2>&1 || { echo "worktree-prepare: jq is required" >&2; exit 2; }
git -C "$PROJECT" rev-parse --git-dir >/dev/null 2>&1 || {
  echo "worktree-prepare: not a git repository: $PROJECT" >&2
  exit 2
}

report() { # $1 action, $2 pulled (true|false|null), $3 error
  jq -n --arg project "$PROJECT" --arg wt "$WT" --arg branch "$BRANCH" --arg base "$BASE" \
    --arg action "$1" --argjson pulled "$2" --arg error "$3" \
    '{project: $project, worktreePath: $wt, branch: $branch, baseBranch: $base,
      action: $action, pulled: $pulled, error: (if $error == "" then null else $error end)}'
}

# Physical path of $1, for comparing paths that may go through a symlink.
# A path that does not exist yet resolves through its nearest existing parent.
canonical() {
  local p="${1%/}" tail=""
  [ -n "$p" ] || p="/"
  while [ ! -d "$p" ]; do
    tail="/$(basename "$p")$tail"
    p=$(dirname "$p")
  done
  printf '%s%s\n' "$(cd -P "$p" 2>/dev/null && pwd -P)" "$tail"
}

registered() {
  local want line
  want=$(canonical "$WT")
  while IFS= read -r line; do
    case "$line" in
      "worktree "*) [ "$(canonical "${line#worktree }")" = "$want" ] && return 0 ;;
    esac
  done < <(git -C "$PROJECT" worktree list --porcelain 2>/dev/null)
  return 1
}

# Unlock before pruning: prune skips a locked entry, so a lock left by a killed
# add would otherwise keep a missing directory registered.
if registered; then
  git -C "$PROJECT" worktree unlock "$WT" 2>/dev/null || true
fi
git -C "$PROJECT" worktree prune 2>/dev/null || true

HYGIENE="$HERE/../lib/repo-hygiene.sh"
[ -f "$HYGIENE" ] && bash "$HYGIENE" ensure "$PROJECT"

set_identity() {
  [ -n "$NAME" ] && git -C "$WT" config user.name "$NAME"
  [ -n "$EMAIL" ] && git -C "$WT" config user.email "$EMAIL"
  return 0
}

if registered && git -C "$WT" rev-parse --git-dir >/dev/null 2>&1; then
  pulled=false
  git -C "$WT" pull --ff-only -q >/dev/null 2>&1 && pulled=true
  set_identity
  report reused "$pulled" ""
  exit 0
fi

if git -C "$PROJECT" show-ref --verify --quiet "refs/heads/$BRANCH"; then
  add_args=("$WT" "$BRANCH")
else
  add_args=("$WT" -b "$BRANCH" "origin/$BASE")
fi
if ! err=$(git -C "$PROJECT" worktree add -q "${add_args[@]}" 2>&1); then
  report failed null "$err"
  exit 1
fi
set_identity
report created null ""
