#!/usr/bin/env bash
set -euo pipefail

FAIL=0
errors=()

# Files that must never be staged (matched against path)
FORBIDDEN_PATHS=(
  'vault_pass$'
  '\.secret$'
  '\.vault$'
  '_ed25519$'
  '_rsa$'
  '_dsa$'
  '_ecdsa$'
  'user_credentials\.json$'
  '\.env$'
)

# Regex patterns that indicate secrets in file content.
# Each is matched with: grep -qiE -e "$pattern"
SECRET_PATTERNS=(
  'BEGIN (RSA|EC|DSA|OPENSSH) PRIVATE KEY'
  'password[[:space:]]*[:=][[:space:]]*["\x27]?[A-Za-z0-9+/!@#$%^&*]{16,}'
  'secret_key[[:space:]]*[:=][[:space:]]*["\x27][A-Za-z0-9+/]{24,}'
  'internal_token[[:space:]]*[:=][[:space:]]*["\x27][A-Za-z0-9+/]{24,}'
  'token[[:space:]]*[:=][[:space:]]*["\x27][a-f0-9]{40}'
  'api[_-]?key[[:space:]]*[:=][[:space:]]*["\x27][A-Za-z0-9]{24,}'
)

# Lines matching these are skipped before pattern matching (false-positive suppression)
SAFE_PATTERNS=(
  '\$ANSIBLE_VAULT'
  '\{\{.*\}\}'
  '^[[:space:]]*#'
  'vault_password_file'
  '^vault_[a-z_]*:'
  'grafana_admin_password: admin'
  '_password:.*vault_'
  'Example\|example\|placeholder\|CHANGEME'
)

staged=$(git diff --cached --name-only --diff-filter=ACMR 2>/dev/null)
[[ -z "$staged" ]] && exit 0

# --- Check forbidden file paths ---
for f in $staged; do
  for pattern in "${FORBIDDEN_PATHS[@]}"; do
    if echo "$f" | grep -qE -e "$pattern"; then
      errors+=("FORBIDDEN FILE: $f  (matches: $pattern)")
      FAIL=1
    fi
  done
done

# --- Check staged content for secret patterns ---
for f in $staged; do
  # Skip binary files
  if git diff --cached -- "$f" | grep -qP '^\+.*[\x00-\x08\x0b-\x1f\x7f-\xff]' 2>/dev/null; then
    continue
  fi

  while IFS= read -r line; do
    # Strip leading '+' from diff hunk line
    content="${line:1}"

    # Skip safe/allowlisted lines
    skip=0
    for safe in "${SAFE_PATTERNS[@]}"; do
      if echo "$content" | grep -qE -e "$safe"; then
        skip=1
        break
      fi
    done
    [[ $skip -eq 1 ]] && continue

    # Check against secret patterns
    for pattern in "${SECRET_PATTERNS[@]}"; do
      if echo "$content" | grep -qiE -e "$pattern"; then
        # Redact the actual value in the error message
        redacted=$(echo "$content" | sed -E 's/([=:][[:space:]]*["\x27]?)[A-Za-z0-9+\/!@#$%^&*]{8,}/\1**REDACTED**/g')
        errors+=("SECRET in $f: ${redacted:0:120}")
        FAIL=1
        break
      fi
    done
  done < <(git diff --cached -- "$f" | grep -E '^\+[^+]')
done

# --- Report ---
if [[ ${FAIL} -eq 1 ]]; then
  echo "" >&2
  echo "pre-commit: potential secrets detected — commit blocked" >&2
  echo "" >&2
  for e in "${errors[@]}"; do
    echo "  ✗ $e" >&2
  done
  echo "" >&2
  echo "  To bypass (only if certain this is a false positive):" >&2
  echo "    git commit --no-verify" >&2
  echo "" >&2
  exit 1
fi
