#!/usr/bin/env bash
#
# entroly pre-push hook
#
# Blocks accidental pushes of paths listed in .github-ignore to GitHub.
# Useful when you keep proprietary helpers, internal docs, or unreleased
# experiments alongside the public source. Pushes to non-GitHub remotes
# pass through unchecked.
#
# Activate (once per clone):
#
#   git config core.hooksPath .githooks
#
# Bypass on a one-off basis:
#
#   git push --no-verify
#
# Reads $REPO_ROOT/.github-ignore — one path or directory pattern per
# line. `# comment` and blank lines are ignored. Trailing slash means
# "this directory and everything under it."

set -euo pipefail

REMOTE_NAME="${1:-}"
REMOTE_URL="${2:-}"

# Only guard pushes that go to GitHub. Anything else (private mirror,
# local bare repo, etc.) is the user's call.
if [[ "$REMOTE_URL" != *"github.com"* ]] && [[ "$REMOTE_NAME" != "github" ]]; then
    exit 0
fi

REPO_ROOT="$(git rev-parse --show-toplevel)"
IGNORE_FILE="$REPO_ROOT/.github-ignore"

# No .github-ignore = nothing to guard. Hook is a no-op.
[[ -f "$IGNORE_FILE" ]] || exit 0

BLOCKED=()
while IFS= read -r line || [[ -n "$line" ]]; do
    line="${line%%#*}"        # strip trailing comments
    line="${line##*( )}"      # leading whitespace
    line="${line%%*( )}"      # trailing whitespace
    [[ -z "$line" ]] && continue
    BLOCKED+=("$line")
done < "$IGNORE_FILE"

[[ ${#BLOCKED[@]} -eq 0 ]] && exit 0

VIOLATIONS=()

while read -r LOCAL_REF LOCAL_SHA REMOTE_REF REMOTE_SHA; do
    [[ -z "${LOCAL_SHA:-}" ]] && continue

    # Branch deletion — nothing to scan.
    if [[ "$LOCAL_SHA" == "0000000000000000000000000000000000000000" ]]; then
        continue
    fi

    if [[ "$REMOTE_SHA" == "0000000000000000000000000000000000000000" ]]; then
        # New branch on the remote — diff against the default branch's
        # merge-base if available, otherwise just inspect the tip commit.
        BASE=$(git merge-base "$LOCAL_SHA" origin/HEAD 2>/dev/null || echo "")
        if [[ -n "$BASE" ]]; then
            RANGE="$BASE..$LOCAL_SHA"
        else
            RANGE=""
        fi
    else
        RANGE="$REMOTE_SHA..$LOCAL_SHA"
    fi

    if [[ -n "$RANGE" ]]; then
        FILES=$(git diff --diff-filter=ACMR --name-only "$RANGE" 2>/dev/null || true)
    else
        FILES=$(git diff-tree --no-commit-id --diff-filter=ACMR --name-only -r "$LOCAL_SHA" 2>/dev/null || true)
    fi

    for file in $FILES; do
        for pattern in "${BLOCKED[@]}"; do
            if [[ "$pattern" == */ ]]; then
                # Directory pattern — match anything under it
                if [[ "$file" == "$pattern"* ]]; then
                    VIOLATIONS+=("$file  (matched pattern: $pattern)")
                fi
            else
                # Exact-path pattern
                if [[ "$file" == "$pattern" ]]; then
                    VIOLATIONS+=("$file  (matched pattern: $pattern)")
                fi
            fi
        done
    done
done

if [[ ${#VIOLATIONS[@]} -gt 0 ]]; then
    echo ""
    echo "pre-push: BLOCKED — these paths are listed in .github-ignore:"
    echo ""
    for v in "${VIOLATIONS[@]}"; do
        echo "  $v"
    done
    echo ""
    echo "Either:"
    echo "  - remove the file from your push, or"
    echo "  - drop the pattern from .github-ignore if it's no longer sensitive, or"
    echo "  - bypass for this push: git push --no-verify"
    exit 1
fi

exit 0
