#!/usr/bin/env bash
# hook-health-monitor.sh -- Standalone health check for governance hooks.
#
# Run periodically (e.g., cron or on session start) to verify:
#   - All hook files exist and are executable
#   - Hook files haven't been tampered with (SHA-256 verification)
#   - Required tools (jq, openssl) are available
#   - Log directories are writable
#   - No hooks are missing from the configuration
#
# T-049: Hook health monitoring.
#
# Usage:
#   bash hook-health-monitor.sh [--manifest path/to/manifest.json]
#
# @connexum/ai-governance

set -euo pipefail

HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOG_DIR="${GOVERNANCE_LOG_DIR:-/tmp/governance/logs}"
MANIFEST_FILE=""
ERRORS=0
WARNINGS=0

# Parse args
while [[ $# -gt 0 ]]; do
  case "$1" in
    --manifest) MANIFEST_FILE="$2"; shift 2 ;;
    *) shift ;;
  esac
done

mkdir -p "$LOG_DIR"

TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
REPORT_FILE="$LOG_DIR/hook-health-$(date -u +%Y%m%d).jsonl"

log_check() {
  local status="$1"
  local check="$2"
  local detail="$3"

  echo "{\"timestamp\":\"$TIMESTAMP\",\"status\":\"$status\",\"check\":\"$check\",\"detail\":\"$detail\"}" >> "$REPORT_FILE"

  if [[ "$status" == "FAIL" ]]; then
    echo "✗ FAIL: $check -- $detail" >&2
    ((ERRORS++)) || true
  elif [[ "$status" == "WARN" ]]; then
    echo "! WARN: $check -- $detail" >&2
    ((WARNINGS++)) || true
  else
    echo "✓ PASS: $check" >&2
  fi
}

# --- 1. Check required tools ---

for tool in jq sha256sum; do
  if command -v "$tool" &>/dev/null; then
    log_check "PASS" "tool:$tool" "Available"
  else
    # sha256sum might be shasum on macOS
    if [[ "$tool" == "sha256sum" ]] && command -v shasum &>/dev/null; then
      log_check "PASS" "tool:$tool" "Available (via shasum)"
    else
      log_check "WARN" "tool:$tool" "Not found -- some features may not work"
    fi
  fi
done

# --- 2. Check hook files exist and are executable ---

EXPECTED_HOOKS=(
  # F-NEW-PROBE-2026-05-03: list rebuilt to cover all runtime hooks
  # shipped with the package. Excludes: hook-utils.sh helper,
  # hook-wrapper.sh wrapper utility, and install-hooks.sh install-time
  # scaffold.
  # 2026-05-12: audit-dir-picker.sh added under F-NEW-CRIT-39g
  # vendor-neutral audit dir resolution. Total now 13 runtime hooks.
  "audit-dir-picker.sh"
  "audit-logger.sh"
  "cost-budget-gate.sh"
  "destructive-command-guard.sh"
  "file-boundary-guard.sh"
  "file-change-tracker.sh"
  "governance-file-shield.sh"
  "governance-integrity-check.sh"
  "hook-health-monitor.sh"
  "output-exfiltration-scanner.sh"
  "secret-leak-scanner.sh"
  "token-tracker.sh"
  "web-access-gate.sh"
)

for hook in "${EXPECTED_HOOKS[@]}"; do
  HOOK_PATH="$HOOK_DIR/$hook"

  if [[ ! -f "$HOOK_PATH" ]]; then
    log_check "FAIL" "hook:exists:$hook" "File not found"
    continue
  fi

  if [[ ! -r "$HOOK_PATH" ]]; then
    log_check "FAIL" "hook:readable:$hook" "File not readable"
    continue
  fi

  # Check for valid shebang
  FIRST_LINE=$(head -1 "$HOOK_PATH")
  if [[ "$FIRST_LINE" != "#!/usr/bin/env bash" && "$FIRST_LINE" != "#!/bin/bash" ]]; then
    log_check "WARN" "hook:shebang:$hook" "Non-standard shebang: $FIRST_LINE"
  fi

  log_check "PASS" "hook:exists:$hook" "Present and readable"
done

# --- 3. SHA-256 integrity verification ---

if [[ -n "$MANIFEST_FILE" && -f "$MANIFEST_FILE" ]]; then
  if command -v jq &>/dev/null; then
    # Read expected hashes from manifest
    while IFS= read -r line; do
      FILE=$(echo "$line" | jq -r '.file')
      EXPECTED_HASH=$(echo "$line" | jq -r '.sha256')
      FULL_PATH="$HOOK_DIR/$FILE"

      if [[ -f "$FULL_PATH" ]]; then
        if command -v sha256sum &>/dev/null; then
          ACTUAL_HASH=$(sha256sum "$FULL_PATH" | awk '{print $1}')
        elif command -v shasum &>/dev/null; then
          ACTUAL_HASH=$(shasum -a 256 "$FULL_PATH" | awk '{print $1}')
        else
          log_check "WARN" "integrity:$FILE" "No hash tool available"
          continue
        fi

        if [[ "$ACTUAL_HASH" == "$EXPECTED_HASH" ]]; then
          log_check "PASS" "integrity:$FILE" "Hash matches"
        else
          log_check "FAIL" "integrity:$FILE" "TAMPERED: expected=$EXPECTED_HASH actual=$ACTUAL_HASH"
        fi
      fi
    done < <(jq -c '.hooks[]' "$MANIFEST_FILE" 2>/dev/null)
  else
    log_check "WARN" "integrity:manifest" "jq not available, cannot verify manifest"
  fi
else
  log_check "WARN" "integrity:manifest" "No manifest file provided (use --manifest)"
fi

# --- 4. Check log directories ---

for dir in "$LOG_DIR" "${GOVERNANCE_AUDIT_DIR:-/tmp/governance/audit}"; do
  if [[ -d "$dir" ]]; then
    if [[ -w "$dir" ]]; then
      log_check "PASS" "dir:writable:$dir" "Writable"
    else
      log_check "FAIL" "dir:writable:$dir" "Not writable"
    fi
  else
    log_check "WARN" "dir:exists:$dir" "Does not exist (will be created on first use)"
  fi
done

# --- 5. Check disk space ---

if command -v df &>/dev/null; then
  AVAIL_KB=$(df -P "$LOG_DIR" 2>/dev/null | tail -1 | awk '{print $4}')
  if [[ -n "$AVAIL_KB" ]] && [[ "$AVAIL_KB" -lt 102400 ]]; then
    log_check "WARN" "disk:space" "Low disk space: ${AVAIL_KB}KB available in $LOG_DIR"
  else
    log_check "PASS" "disk:space" "${AVAIL_KB}KB available"
  fi
fi

# --- Summary ---

TOTAL=$((ERRORS + WARNINGS))
echo "" >&2
echo "Hook Health Summary: ${ERRORS} errors, ${WARNINGS} warnings" >&2

if [[ $ERRORS -gt 0 ]]; then
  echo "{\"timestamp\":\"$TIMESTAMP\",\"status\":\"UNHEALTHY\",\"errors\":$ERRORS,\"warnings\":$WARNINGS}" >> "$REPORT_FILE"
  exit 1
fi

echo "{\"timestamp\":\"$TIMESTAMP\",\"status\":\"HEALTHY\",\"errors\":0,\"warnings\":$WARNINGS}" >> "$REPORT_FILE"
exit 0
