#!/bin/bash
# pre-push — Rebase-collision integrity guard
# Prevents silent loss of upstream content during rebase operations
# Usage: Installed as git pre-push hook
# Exit: 0=success, 1=error/blocked, 2=no-op (not a rebase)

set -euo pipefail

if [[ -n "${NO_COLOR:-}" ]]; then
  RED="" GREEN="" YELLOW="" RESET=""
else
  RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' RESET='\033[0m'
fi

check_prereqs() {
  command -v git &>/dev/null || { echo "Missing: git" >&2; exit 1; }
}

# Check if ALLOW_CONTENT_LOSS bypass is set
if [[ -n "${ALLOW_CONTENT_LOSS:-}" ]]; then
  echo -e "${YELLOW}⚠${RESET} ALLOW_CONTENT_LOSS set - bypassing rebase integrity check"
  exit 0
fi

# Read pre-push hook args: remote_name remote_url
remote_name="$1"
remote_url="$2"

# Read stdin: local_ref local_oid remote_ref remote_oid
while read local_ref local_oid remote_ref remote_oid; do
  # Only check pushes to main/master
  if [[ "$remote_ref" != "refs/heads/main" && "$remote_ref" != "refs/heads/master" ]]; then
    continue
  fi

  # Extract branch name from remote_ref (refs/heads/main -> main)
  base_branch="${remote_ref#refs/heads/}"

  # Check if we're ahead of origin/<base>
  if ! git fetch --quiet origin "$base_branch" 2>/dev/null; then
    echo -e "${RED}✗${RESET} Failed to fetch origin/$base_branch" >&2
    exit 1
  fi

  # Find merge base
  merge_base=$(git merge-base HEAD "origin/$base_branch" 2>/dev/null || echo "")
  if [[ -z "$merge_base" ]]; then
    echo -e "${YELLOW}⚠${RESET} No merge base with origin/$base_branch - allowing push"
    exit 0
  fi

  # Check if this is a rebase (not just fast-forward)
  # If the first parent of HEAD is not the remote HEAD, it's likely a rebase
  if git merge-base --is-ancestor "origin/$base_branch" HEAD 2>/dev/null; then
    # Fast-forward push, allow it
    exit 0
  fi

  echo "🔍 Checking rebase integrity against origin/$base_branch..."

  # Find upstream-only changes since merge base
  lost_files=()

  # Check for upstream-added files that are now missing
  while IFS= read -r -d '' file; do
    if [[ -n "$file" && ! -f "$file" ]]; then
      lost_files+=("LOST-NEW: $file")
    fi
  done < <(git diff --name-only --diff-filter=A -z "$merge_base" "origin/$base_branch" 2>/dev/null || true)

  # Check for upstream-modified files that have lost content
  while IFS= read -r -d '' file; do
    if [[ -n "$file" && -f "$file" ]]; then
      # Check if the file has upstream changes that are missing in HEAD
      upstream_content=$(git show "origin/$base_branch:$file" 2>/dev/null || echo "")
      head_content=$(git show "HEAD:$file" 2>/dev/null || echo "")
      merge_base_content=$(git show "$merge_base:$file" 2>/dev/null || echo "")

      # If upstream has content that merge-base didn't have, but HEAD doesn't have it
      if [[ -n "$upstream_content" ]] && [[ "$upstream_content" != "$merge_base_content" ]] && [[ "$head_content" != "$upstream_content" ]]; then
        # Check if HEAD has any of the upstream additions
        if ! echo "$head_content" | grep -Fq "$(echo "$upstream_content" | grep -v -F "$merge_base_content" || echo '__NO_DIFF__')" 2>/dev/null; then
          lost_files+=("LOST-MOD: $file")
        fi
      fi
    fi
  done < <(git diff --name-only --diff-filter=M -z "$merge_base" "origin/$base_branch" 2>/dev/null || true)

  if [[ ${#lost_files[@]} -gt 0 ]]; then
    echo -e "${RED}✗${RESET} Rebase integrity check FAILED"
    echo -e "${RED}✗${RESET} Detected silent loss of upstream content:"
    printf '%s\n' "${lost_files[@]}"
    echo ""
    echo "To fix:"
    echo "  (a) Re-rebase with: git rebase origin/$base_branch --strategy=ort -X theirs"
    echo "  (b) Use merge instead: git merge origin/$base_branch --no-ff"
    echo "  (c) If deletion was intentional, set: ALLOW_CONTENT_LOSS=1 git push"
    echo ""
    exit 1
  fi
done

echo -e "${GREEN}✓${RESET} Rebase integrity check passed"
exit 0