#!/bin/bash
# sofagent post-commit hook v1.4.0
# --no-verify 跳过 commit-msg，但不跳过 post-commit
# 安装：sofagent-audit --init（v1.0.6 起 --init 同时安装 commit-msg + post-commit）
#
# CRITICAL: post-commit 永远 exit 0——任何时候都不能阻断 commit
# post-commit 是 best-effort 检测——不保证 100% 覆盖，建议配合 CI 侧 sofagent-audit --diff 兜底
#
# v1.2.9 P0-2: 对账逻辑适配 parentSha——commit-msg hook 在 commit 对象生成前运行，
# 审计记录写 parentSha（= 审计时 HEAD = 新提交的父提交）而非 commitSha。
# 匹配顺序：① commitSha 精确匹配 ② pre-commit 记录的 parentSha 匹配
#           ③ 旧记录（无 commitPhase 字段）回退 300s 时间窗口（向后兼容）。

# 检测 Node.js
if ! command -v node &>/dev/null; then
  exit 0  # 静默退出——post-commit 不应该阻断任何操作
fi

# 检测 sofagent-audit
if command -v sofagent-audit &>/dev/null; then
  AUDIT_CMD="sofagent-audit"
elif [ -f "engine/audit/dist/index.js" ]; then
  AUDIT_CMD="node engine/audit/dist/index.js"
else
  exit 0  # 静默退出
fi

# 获取当前 commit SHA
CURRENT_SHA=$(git rev-parse HEAD 2>/dev/null)
if [ -z "$CURRENT_SHA" ]; then
  exit 0
fi

# 父提交 SHA（首次提交时 HEAD^ 不存在，留空）
PARENT_SHA=$(git rev-parse HEAD^ 2>/dev/null)

# 当前 commit 的 message 主题行——parentSha 匹配后叠加主题消歧，防跨 commit 误认领：
# commit N 的 SHA 天然是 commit N+1 审计记录的 parentSha，--no-verify 绕过提交 B 后
# 紧跟的正常提交 C 会让 B 的对账命中 C 的审计记录。记录的 task 字段（hook 写入时
# 来自 commit message 主题行）须与本 commit 主题一致才认领。
COMMIT_SUBJECT=$(git log -1 --pretty=%s HEAD 2>/dev/null)

# 检查 history.jsonl：SHA 精确匹配 + parentSha 匹配
# 路径动态获取用户级 data 目录（与 data-paths.ts 的 SOFAGENT_HOME 解析链对齐）
HISTORY_FILE="${SOFAGENT_HOME:-$HOME/.sofagent}/data/audit/history.jsonl"
if [ -f "$HISTORY_FILE" ]; then
  # v1.3.5 #2: 假阳性回声修复——对账命中 pre-commit 记录时校验审计结果。
  # 旧逻辑（grep -q parentSha）会命中**被拦截**的审计记录（exit 2）：
  # 带 token commit 被 commit-msg 拦 → 拦截记录写入 history（含 parentSha）→
  # 同内容 --no-verify 强推 → post-commit 命中失败记录 → 假绿。
  # 改为 node 解析：只有 commitSha 匹配、或 pre-commit 记录 parentSha 匹配
  # **且 exitCode === 0** 才静默退出（真回声）；命中失败记录输出疑似绕过提示。
  # post-commit 永远 exit 0（不阻断任何操作）。
  COMMIT_SHA="$CURRENT_SHA" PARENT_SHA="$PARENT_SHA" COMMIT_SUBJECT="$COMMIT_SUBJECT" HISTORY_FILE="$HISTORY_FILE" node -e '
const fs = require("fs");
const COMMIT_SHA = process.env.COMMIT_SHA;
const PARENT_SHA = process.env.PARENT_SHA;
const COMMIT_SUBJECT = (process.env.COMMIT_SUBJECT || "").trim();
const HISTORY_FILE = process.env.HISTORY_FILE;
if (!HISTORY_FILE) process.exit(0);
let lines;
try {
  lines = fs.readFileSync(HISTORY_FILE, "utf-8").trim().split("\n").filter(Boolean);
} catch { process.exit(0); }
if (lines.length === 0) process.exit(0);
try {
  // 反向查找（最新记录在末尾）
  for (let i = lines.length - 1; i >= 0; i--) {
    const entry = JSON.parse(lines[i]);
    const entryCommit = entry.commitSha || "";
    if (entryCommit === COMMIT_SHA) {
      process.exit(0);  // commitSha 找到，commit 已审计
    }
    if (entry.commitPhase === "pre-commit" && entry.parentSha === PARENT_SHA) {
      // 主题消歧：记录的 task（hook 写入时来自 commit message 主题行）与本 commit
      // 主题不一致 → 这是相邻 commit 的审计记录，不认领，继续找。
      const recordSubject = (typeof entry.task === "string" && entry.task.trim() !== ""
        ? entry.task
        : (typeof entry.commitMsg === "string" ? (entry.commitMsg.split("\n")[0] || "") : "")
      ).trim();
      if (COMMIT_SUBJECT && recordSubject && recordSubject !== COMMIT_SUBJECT) {
        continue;
      }
      // v1.3.5 #2: 校验该次审计的结果——exitCode 三档分流（v1.3.6 B8 修正）：
      //   0 = 审计真通过 → 真回声；
      //   1 = WARN 放行——commit-msg 只对 exit 2 阻断，exit 1 时 commit 合法走过审计，
      //       不是绕过（v1.3.6 B8 修复：此前 exit 1 被误判为拦截记录）；
      //   2 = FAIL 拦截后仍出现同父新 commit = --no-verify 绕过，输出疑似绕过提示。
      if (entry.exitCode === 0) {
        console.log("  ✓ [sofagent] 审计通过");
        process.exit(0);  // parentSha 找到（pre-commit 记录，审计通过）
      }
      if (entry.exitCode === 1) {
        console.log("  ✓ [sofagent] 审计通过（含警告，WARN 放行）");
        process.exit(0);  // WARN 放行 = 合法走过审计，不是绕过
      }
      console.log("");
      console.log("  ℹ️ [sofagent] 父提交存在审计拦截记录（exit " + entry.exitCode + "）但本次 commit 未走审计——疑似 --no-verify 绕过。");
      console.log("  可运行 sofagent-audit --verify-commit " + COMMIT_SHA + " 复核。");
      process.exit(0);  // post-commit 永不阻断
    }
  }
  // 未找到任何匹配——降级为 INFO 提示（避免狼来了）。真绕过仍由 --verify-commit 复核。
  console.log("");
  console.log("  ℹ️ [sofagent] 未确认审计记录（post-commit 对账未命中）。");
  console.log("  如未使用 --no-verify 可忽略；如需确认运行 sofagent-audit --verify-commit " + COMMIT_SHA);
} catch { process.exit(0); }
' 2>/dev/null
fi

exit 0
