#!/bin/sh
set -e

REPO_ROOT=$(git rev-parse --show-toplevel)
cd "$REPO_ROOT"

remote="$1"
url="$2"

echo "=== Pre-push validation ==="
echo "Pushing to: $remote ($url)"

# Decide whether this push touches Rust/Cargo files. If it does not (docs,
# markdown, shell, CI yaml, etc.), the heavy clippy+test gates are skipped —
# they cannot be affected and running the full suite on a README edit is pure
# friction. Manifest, format, and semgrep stay unconditional (fast / security).
RUST_TOUCHED=0
HAVE_RANGE=0
while read -r _lref lsha _rref rsha; do
  [ -z "$lsha" ] && continue
  case "$lsha" in *[!0]*) : ;; *) continue ;; esac  # ref deletion: nothing to test
  HAVE_RANGE=1
  if printf '%s' "$rsha" | grep -qE '^0+$' || ! git cat-file -e "$rsha" 2>/dev/null; then
    base=$(git merge-base "$lsha" origin/develop 2>/dev/null || true)
    if [ -n "$base" ]; then
      files=$(git diff --name-only "$base" "$lsha")
    else
      files=$(git show --name-only --format= "$lsha")
    fi
  else
    files=$(git diff --name-only "$rsha" "$lsha")
  fi
  if printf '%s\n' "$files" | grep -qE '\.rs$|(^|/)Cargo\.(toml|lock)$|(^|/)rust-toolchain'; then
    RUST_TOUCHED=1
  fi
done
# No parseable range (manual run / edge): be safe and run the full gate.
[ "$HAVE_RANGE" = "0" ] && RUST_TOUCHED=1
if [ "$RUST_TOUCHED" = "1" ]; then
  echo "Rust/Cargo changes detected -> full gate."
else
  echo "No Rust/Cargo changes -> light gate (manifest + fmt + semgrep)."
fi

echo ""
echo "[1/5] Manifest portability..."
python3 - <<'PY'
try:
    import tomllib
except ModuleNotFoundError:
    import tomli as tomllib

with open("Cargo.toml", "rb") as fh:
    data = tomllib.load(fh)

# Path deps are portable as long as they carry a registry `version` —
# cargo strips the path on publish and consumers resolve the version.
# The unportable (and publish-breaking) form is a path dep WITHOUT a
# version: it cannot be uploaded and silently pins the build to this
# checkout. Flag only that form.
bad = [
    (section, name, spec["path"])
    for section in ("dependencies", "dev-dependencies", "build-dependencies")
    for name, spec in data.get(section, {}).items()
    if isinstance(spec, dict) and "path" in spec and "version" not in spec
]

if bad:
    raise SystemExit(
        "Manifest portability check failed:\n"
        + "\n".join(
            f"  - {section}.{name} is a local path dependency without a registry version: {path}"
            for section, name, path in bad
        )
    )

print("Manifest portability: ok (path dependencies carry registry versions)")
PY

echo ""
echo "[2/5] Format check (make fmt-check)..."
make fmt-check

if [ "$RUST_TOUCHED" = "1" ]; then
  echo ""
  echo "[3/5] Clippy (make clippy)..."
  make clippy

  echo ""
  echo "[4/5] Tests (make test)..."
  make test
else
  echo ""
  echo "[3/5] Clippy... skipped (no Rust/Cargo changes)"
  echo "[4/5] Tests... skipped (no Rust/Cargo changes)"
fi

echo ""
echo "[5/5] Semgrep (make semgrep)..."
make semgrep

echo ""
echo "=== All pre-push checks passed ==="
