#!/usr/bin/env bash
# governance-integrity-check.sh -- SessionStart hook
# SHA-256 verifies all governance files on session start.
# Alerts if any files have been tampered with.
# Never blocks session start (always exits 0), but reports issues.
#
# Part of @connexum/ai-governance

set -uo pipefail

# AIAG-010: macOS ships without `sha256sum`. Provide a portable wrapper
# that uses `shasum -a 256` when sha256sum is unavailable.
if command -v sha256sum >/dev/null 2>&1; then
  _sha256() { sha256sum "$@"; }
elif command -v shasum >/dev/null 2>&1; then
  _sha256() { shasum -a 256 "$@"; }
else
  _sha256() {
    echo "ERROR: neither sha256sum nor shasum found; cannot verify integrity" >&2
    return 1
  }
fi

# Find hash manifest
HASH_MANIFEST=""
for candidate in \
  ".claude/governance/file-hashes.json" \
  ".claude/rules/file-hashes.json" \
  "GOVERNANCE/file-hashes.json"; do
  if [ -f "$candidate" ]; then
    HASH_MANIFEST="$candidate"
    break
  fi
done

# If no manifest exists, create one from current files
if [ -z "$HASH_MANIFEST" ]; then
  MANIFEST_DIR=".claude/governance"
  mkdir -p "$MANIFEST_DIR" 2>/dev/null || exit 0
  HASH_MANIFEST="$MANIFEST_DIR/file-hashes.json"

  # Scan for governance files and create initial manifest
  echo "{}" > "$HASH_MANIFEST"

  GOVERNANCE_FILES=()
  for pattern in \
    ".claude/rules/*.md" \
    ".claude/governance/*.md" \
    "GOVERNANCE/*.md"; do
    for f in $pattern; do
      [ -f "$f" ] && GOVERNANCE_FILES+=("$f")
    done
  done

  if [ ${#GOVERNANCE_FILES[@]} -gt 0 ]; then
    MANIFEST="{}"
    for f in "${GOVERNANCE_FILES[@]}"; do
      HASH=$(_sha256 "$f" 2>/dev/null | cut -d' ' -f1)
      MANIFEST=$(echo "$MANIFEST" | jq --arg file "$f" --arg hash "$HASH" '. + {($file): $hash}' 2>/dev/null)
    done
    echo "$MANIFEST" > "$HASH_MANIFEST" 2>/dev/null
    echo "Governance integrity manifest created with ${#GOVERNANCE_FILES[@]} files." >&2
  fi

  exit 0
fi

# Verify each file in the manifest
TAMPERED=0
MISSING=0
REPORT=""

while IFS= read -r file; do
  EXPECTED_HASH=$(jq -r --arg f "$file" '.[$f]' "$HASH_MANIFEST" 2>/dev/null)

  if [ ! -f "$file" ]; then
    MISSING=$((MISSING + 1))
    REPORT="${REPORT}MISSING: $file\n"
    continue
  fi

  CURRENT_HASH=$(_sha256 "$file" 2>/dev/null | cut -d' ' -f1)

  if [ "$CURRENT_HASH" != "$EXPECTED_HASH" ]; then
    TAMPERED=$((TAMPERED + 1))
    REPORT="${REPORT}TAMPERED: $file (expected: ${EXPECTED_HASH:0:16}..., got: ${CURRENT_HASH:0:16}...)\n"
  fi
done < <(jq -r 'keys[]' "$HASH_MANIFEST" 2>/dev/null)

if [ $TAMPERED -gt 0 ] || [ $MISSING -gt 0 ]; then
  echo "GOVERNANCE INTEGRITY ALERT" >&2
  echo "=========================" >&2
  echo "$TAMPERED file(s) tampered, $MISSING file(s) missing." >&2
  echo -e "$REPORT" >&2
  echo "Review immediately. Run 'git diff' to see changes." >&2
  echo "To update manifest after authorized changes: update file-hashes.json" >&2

  # Strict mode: if GOVERNANCE_STRICT_MODE=true, block the session when
  # tampering or missing files are detected. Default behavior (warn only)
  # is preserved for backward compatibility.
  if [ "${GOVERNANCE_STRICT_MODE:-false}" = "true" ]; then
    echo "GOVERNANCE_STRICT_MODE is enabled -- blocking session start." >&2
    exit 2
  fi
else
  echo "Governance integrity verified. All files match expected hashes." >&2
fi

exit 0
