#!/bin/bash
# sofagent commit-msg hook v1.4.0
# 安装：sofagent-audit --init 或 sofagent-audit --install-hook
# commit-msg hook 接收 $1 = commit message 文件路径

# 0. 读取 commit message（commit-msg hook 独有优势）
#    - subject（第一行）：A3 越界检查依赖此参数
#    - 完整 message：A9 注入检查依赖此参数（body 里的注入 payload 不能漏检）
COMMIT_MSG_FILE="$1"
COMMIT_SUBJECT=""
COMMIT_FULL_MSG=""
if [ -f "$COMMIT_MSG_FILE" ]; then
  COMMIT_SUBJECT=$(head -1 "$COMMIT_MSG_FILE")
  COMMIT_FULL_MSG=$(cat "$COMMIT_MSG_FILE")
fi

DIFF=$(git diff --cached --name-only)
if [ -z "$DIFF" ]; then
  exit 0
fi

# 1. Node.js 检测
if ! command -v node &>/dev/null; then
  echo "❌ sofagent-audit: Node.js 未找到，审计未运行"
  echo "   请安装 Node.js >= 18: https://nodejs.org"
  exit 1
fi

# 2. sofagent-audit 检测（优先用仓库本地 engine/audit/dist/，避免全局旧版版本漂移）
if [ -f "engine/audit/dist/index.js" ]; then
  AUDIT_CMD=(node "engine/audit/dist/index.js")
elif command -v sofagent-audit &>/dev/null; then
  AUDIT_CMD=(sofagent-audit)
else
  echo "❌ sofagent-audit 未安装，审计未运行"
  echo "   请运行: npm install -g @sofagent/audit"
  exit 1
fi

# 2.1 P1-A2: dist 完整性校验——防止本地覆写 dist 致审计失效
#     比对 engine/audit/dist/index.js 的 SHA-256 与安装时记录的基准哈希。
#     基准哈希存储在 ~/.sofagent/internal/audit-hash.txt（install.sh 首装或 --doctor 首次运行时记录）。
#     v1.4.0 P1-A3：基准缺失时 FAIL 而非跳过——此前 if [ -f ] 缺基准即静默放行，
#     首次部署窗口内可植入冒牌引擎（基准仅 --doctor 生成而首装不生成，空窗被利用）。
if [ -f "engine/audit/dist/index.js" ]; then
  SOFAGENT_HOME="${SOFAGENT_HOME:-$HOME/.sofagent}"
  HASH_RECORD="$SOFAGENT_HOME/internal/audit-hash.txt"
  if [ ! -f "$HASH_RECORD" ]; then
    # 基准缺失 = 完整性校验形同虚设 → fail-closed：先补生成基准再放行本次，
    # 让后续每次 commit 都有可比对的锚点（与 install.sh 首装生成策略闭环）
    echo "⚠️ [sofagent] 审计引擎哈希基准缺失——正在补生成..."
    mkdir -p "$SOFAGENT_HOME/internal"
    if node -e "const c=require('crypto'),f=require('fs');process.stdout.write(c.createHash('sha256').update(f.readFileSync('engine/audit/dist/index.js')).digest('hex'))" > "$HASH_RECORD" 2>/dev/null && [ -s "$HASH_RECORD" ]; then
      chmod 600 "$HASH_RECORD" 2>/dev/null || true
      echo "ℹ️ [sofagent] 基准哈希已记录（后续 commit 将比对完整性）"
    else
      echo "🔴 [sofagent] 基准哈希生成失败——无法保证审计引擎未被替换，本次提交终止"
      echo "   请运行: sofagent-audit --doctor（自动记录基准）后重试"
      exit 1
    fi
  else
    CURRENT_HASH=$(node -e "const c=require('crypto'),f=require('fs');process.stdout.write(c.createHash('sha256').update(f.readFileSync('engine/audit/dist/index.js')).digest('hex'))" 2>/dev/null)
    RECORDED_HASH=$(cat "$HASH_RECORD" 2>/dev/null | tr -d '[:space:]')
    if [ -n "$CURRENT_HASH" ] && [ -n "$RECORDED_HASH" ] && [ "$CURRENT_HASH" != "$RECORDED_HASH" ]; then
      echo "🔴 [sofagent] 审计引擎完整性校验失败（P1-A2 dist 哈希不匹配）"
      echo "   engine/audit/dist/index.js 可能被替换（影子审计器劫持风险）。"
      echo "   记录哈希: ${RECORDED_HASH:0:12}...  当前哈希: ${CURRENT_HASH:0:12}..."
      echo "   如需恢复，运行: npm run build --workspace=engine/audit"
      echo "   如为故意重建 dist，运行: sofagent-audit --doctor（会更新基准哈希）"
      exit 1
    fi
  fi
fi

# 2.5 v1.3.6 B22: .sofagent/ ignore 兜底——防御 --install-hook 后用户手工删 .gitignore、
#     或旧版本安装未写入的间隙。若 .gitignore 未含 .sofagent/，自动补写（幂等），
#     防止 .sofagent/.git-shadow/ 快照数据被本次 commit 卷入仓库。
if [ -f ".gitignore" ] && ! grep -q '^\.sofagent/$' ".gitignore" 2>/dev/null && ! grep -q '^\.sofagent/' ".gitignore" 2>/dev/null; then
  printf '\n# sofagent 审计数据（本地配置 + 知识库 + 审计历史）\n.sofagent/\n' >> ".gitignore"
  echo "ℹ️ [sofagent] 已自动补充 .gitignore（排除 .sofagent/）"
elif [ ! -f ".gitignore" ]; then
  printf '# sofagent 审计数据（本地配置 + 知识库 + 审计历史）\n.sofagent/\n' > ".gitignore"
  echo "ℹ️ [sofagent] 已自动创建 .gitignore（排除 .sofagent/）"
fi
# 已暂存的 .sofagent/ 路径移出暂存区（.sofagent/ 永不入库；git reset 单路径只影响本目录，不动其他暂存）
git reset -q -- .sofagent/ 2>/dev/null || true

# 3. 审计范围固定为「暂存区（staged）= 即将提交的变更」
#    无论是否首次提交都用 --cached：
#    - commit-msg 钩子运行时提交尚未生成，staged 即本次提交的真实内容；
#    - 若用 HEAD，git diff HEAD 会包含工作区未暂存的无关改动（如未提交的源码残文件），
#      导致 A3「不改越界」误报不属于本次提交的文件（ghost bug，v1.1.4 修复）。
AUDIT_DIFF_ARG="--cached"

# 4. 正常运行审计
# --task 传 subject（A3 越界检查用）
# --commit-msg 传完整 message（A9 注入检查用）——两个参数都传
# AUDIT_CMD 用 bash 数组——仓库路径含空格时（如 /Users/foo/my repo/）不炸
if [ -n "$COMMIT_SUBJECT" ]; then
  "${AUDIT_CMD[@]}" --diff "$AUDIT_DIFF_ARG" --silent --ci --task "$COMMIT_SUBJECT" --commit-msg "$COMMIT_FULL_MSG"
else
  "${AUDIT_CMD[@]}" --diff "$AUDIT_DIFF_ARG" --silent --ci
fi
EXIT_CODE=$?

if [ $EXIT_CODE -eq 2 ]; then
  echo ""
  echo "❌ sofagent audit: 检测到违规，commit 已阻止。"
  echo "   请修复违规项后重新提交。"
  exit 1
fi

if [ $EXIT_CODE -eq 1 ]; then
  echo ""
  echo "⚠️  sofagent audit: 检测到警告，但允许 commit。"
fi

exit 0
