#!/bin/sh
# Repository pre-commit gate. Content integrity and attribution are blocking;
# gitleaks is blocking when installed and emits an explicit warning when
# unavailable.
#
# WHY BOTH LIVE HERE: git honours exactly one hook path. This repository sets
# `core.hooksPath = .githooks`, and the pre-commit framework refuses to install
# while that is set ("Cowardly refusing to install hooks with `core.hooksPath`
# set"). Keeping the secret scan only in .pre-commit-config.yaml therefore meant
# a maintainer with the repo's hook path configured got the attribution guard
# and NO secret scan, while a contributor who ran `pre-commit install` got the
# reverse. Either way exactly one gate was live, and nothing said which.
#
# .pre-commit-config.yaml remains supported for contributors who prefer the
# framework: unset core.hooksPath first. CI enforces the attribution rule
# independently (.github/workflows/no-ai-attribution.yml), so a contributor who
# runs neither hook path is still caught before merge.

if command -v python3 >/dev/null 2>&1 &&
    python3 -c 'import sys; raise SystemExit(sys.version_info < (3, 11))' \
        >/dev/null 2>&1; then
    python_command=python3
elif command -v python >/dev/null 2>&1 &&
    python -c 'import sys; raise SystemExit(sys.version_info < (3, 11))' \
        >/dev/null 2>&1; then
    python_command=python
else
    echo "ERROR: Python 3.11 or newer is required for repository policy checks." >&2
    exit 1
fi

# ---------------------------------------------------------- content integrity
# Scan the complete proposed index, including unchanged staged blobs. A diff or
# working-tree scan can miss a marked blob that is already staged, and an
# incomplete scan is not clean.
"$python_command" .github/scripts/check_content_marks.py --index
content_status=$?
if [ "$content_status" -ne 0 ]; then
    echo "ERROR: staged content-integrity scan did not pass." >&2
    exit "$content_status"
fi

# ---------------------------------------------------------------- attribution
# The policy scanner reads the complete proposed index as exact Git blobs. Its
# lexical grammar keeps sentence-form product discussion and Markdown vendor
# headings/lists legal; no path is exempt from the scan.
"$python_command" -I .github/scripts/check_attribution.py --index
attribution_status=$?
if [ "$attribution_status" -ne 0 ]; then
    echo "ERROR: staged attribution scan did not pass." >&2
    exit "$attribution_status"
fi

# --------------------------------------------------------------------- secrets
# Load-bearing, not decoration. GitHub secret scanning and push protection are
# enabled on this public repository and catch a pushed credential server side.
# When gitleaks is installed and this hook is not bypassed, it is the only
# scan that runs before a pasted credential reaches the remote at all; the
# server workflow runs after push.
# .gitleaks.toml allowlists the synthetic token the redaction tests need by
# VALUE rather than by path, so a real secret in the same file is still caught.
if command -v gitleaks >/dev/null 2>&1; then
    if ! gitleaks git --staged --config .gitleaks.toml --redact --no-banner; then
        echo "ERROR: gitleaks flagged a potential secret in the staged changes." >&2
        echo "If it is a synthetic test value, allowlist it BY VALUE in .gitleaks.toml." >&2
        exit 1
    fi
else
    # Warn rather than refuse. A fresh clone has no gitleaks, and blocking every
    # commit until it is installed teaches people to reach for --no-verify,
    # which would disable the attribution guard above as well.
    echo "WARNING: gitleaks is not installed - the secret scan did NOT run." >&2
    echo "  brew install gitleaks   (or use .pre-commit-config.yaml)" >&2
fi
