#!/usr/bin/env bash
# `git cp` wrapper — the real pre-apply guard the hooks can't be.
#
#   git cp <sha>...             pre-check ledger, cherry-pick -x, auto-reconcile
#   git cp --force <sha>...     pick even known "Won't merge" SHAs (documented override)
#   git cp --continue|--abort|--skip|--quit   forwarded to git cherry-pick
#
# Behaviour:
#   1. Resolve requested SHAs (single, list, or A^..B range) BEFORE touching the tree.
#   2. If any is disposition "wont-merge", print SHA + reason and ABORT unless --force.
#   3. git cherry-pick -x <args>   (ensures -x is present exactly once).
#   4. On success, run `scripts/triage.py reconcile` to land the picks in the ledger.
set -uo pipefail

root="$(git rev-parse --show-toplevel 2>/dev/null)" || {
  echo "git cp: not a git repository" >&2
  exit 1
}
triage="$root/scripts/triage.py"
py="$(command -v python3 || command -v python 2>/dev/null)"

force=0
control=0
passthru=()
picks=()

for arg in "$@"; do
  case "$arg" in
    --force | --allow-reject)
      force=1
      ;;
    --continue | --abort | --skip | --quit)
      control=1
      passthru+=("$arg")
      ;;
    -*)
      passthru+=("$arg")
      ;;
    *)
      passthru+=("$arg")
      picks+=("$arg")
      ;;
  esac
done

reconcile() {
  if [ -n "$py" ] && [ -f "$triage" ]; then
    "$py" "$triage" reconcile
  else
    echo "git cp: python/triage.py unavailable — skipping auto-reconcile" >&2
  fi
}

ensure_x() {
  # Prepend -x unless the caller already passed it.
  for a in "${passthru[@]}"; do
    [ "$a" = "-x" ] && return 0
  done
  passthru=("-x" "${passthru[@]}")
}

# Sequencer control verbs: forward and (for --continue) reconcile whatever landed.
if [ "$control" -eq 1 ]; then
  git cherry-pick "${passthru[@]}"
  rc=$?
  if [ "$rc" -eq 0 ]; then
    reconcile
  fi
  exit "$rc"
fi

# Pre-apply reject check.
if [ -n "$py" ] && [ -f "$triage" ] && [ "${#picks[@]}" -gt 0 ]; then
  resolved=()
  for tok in "${picks[@]}"; do
    if [[ "$tok" == *..* ]]; then
      while IFS= read -r s; do
        [ -n "$s" ] && resolved+=("$s")
      done < <(git rev-list --reverse "$tok" 2>/dev/null)
    else
      s="$(git rev-parse --verify --quiet "${tok}^{commit}" 2>/dev/null)" && resolved+=("$s")
    fi
  done

  rejects=()
  for s in ${resolved[@]+"${resolved[@]}"}; do
    reason="$("$py" "$triage" check-reject "$s" 2>/dev/null)"
    if [ "$?" -eq 3 ]; then
      rejects+=("${s:0:12}  $reason")
    fi
  done

  if [ "${#rejects[@]}" -gt 0 ]; then
    echo "git cp: the following SHA(s) are marked \"Won't merge\" in the triage ledger:" >&2
    for r in "${rejects[@]}"; do
      echo "  ⛔ $r" >&2
    done
    if [ "$force" -eq 0 ]; then
      echo "" >&2
      echo "Refusing to cherry-pick a known-reject. To override intentionally: git cp --force ..." >&2
      exit 1
    fi
    echo "  --force set — proceeding anyway." >&2
  fi
fi

ensure_x

# --force must also satisfy the commit-msg backstop, so allow rejects through the hook too.
if [ "$force" -eq 1 ]; then
  export SH_CHERRYPICK_ALLOW_REJECT=1
fi

git cherry-pick "${passthru[@]}"
rc=$?

if [ "$rc" -eq 0 ]; then
  reconcile
else
  echo "" >&2
  echo "git cp: cherry-pick stopped (rc=$rc). Resolve, then: git cp --continue (or --abort/--skip)." >&2
  echo "        Landed picks are journaled; reconcile runs on --continue or via make triage-reconcile." >&2
fi
exit "$rc"
