#!/bin/bash
# CodeBuddy Hook 脚本 — 由 cls-codingagent 注入到 ~/.codebuddy/settings.json
#
# CodeBuddy 触发 hook 时，通过 stdin 传入 JSON 事件数据。
# 本脚本将 JSON 追加写入 JSONL 日志文件，供 HookInput 轮询读取。
#
# 设计原则（fail-open）：
#   - hook 脚本绝不阻塞 CodeBuddy 执行
#   - 任何错误都静默处理，只写 stderr 不影响 CodeBuddy
#   - 超时由 CodeBuddy 的 hook timeout 控制（默认 10s）
#   - 不依赖 python3 / node / perl 等外部解释器，仅使用 bash 内建和 date/mkdir/cat

set -eo pipefail

# 事件名作为第一个参数传入（kebab-case）
EVENT_NAME="${1:-unknown}"

# 读取 stdin（CodeBuddy 传入的 JSON）
INPUT=$(cat)

# 日志目录
LOG_DIR="${HOME}/.cls-codebuddy/logs/codebuddy"
mkdir -p "$LOG_DIR" 2>/dev/null || true

# 今日日志文件
TODAY=$(date +%Y-%m-%d)
LOG_FILE="${LOG_DIR}/codebuddy-${TODAY}.jsonl"

# ── 毫秒时间戳（纯 shell，无需 python3）──────────────────────────────────────
# 优先级：
#   1. $EPOCHREALTIME（bash 5.0+ / zsh 内建，格式 "秒.微秒"，精度最高，无外部命令）
#   2. date +%N（Linux GNU date 支持纳秒，取前 3 位得毫秒）
#   3. date +%s * 1000（兜底，秒级精度）
if [ -n "${EPOCHREALTIME+x}" ]; then
  # bash 5.0+ / zsh：$EPOCHREALTIME 形如 "1700000000.123456"
  # 取整数部分（秒）和小数点后前 3 位（毫秒），纯算术无外部命令
  _epoch_int="${EPOCHREALTIME%.*}"          # 秒（去掉小数部分）
  _epoch_frac="${EPOCHREALTIME#*.}"         # 小数部分（微秒，6位）
  _ms_frac="${_epoch_frac:0:3}"             # 取前 3 位作为毫秒
  # 去掉前导零，防止被 shell 算术解释为八进制
  _ms_frac="${_ms_frac#"${_ms_frac%%[!0]*}"}"
  _ms_frac="${_ms_frac:-0}"
  TIMESTAMP_MS=$(( _epoch_int * 1000 + _ms_frac ))
else
  # 降级：尝试 GNU date +%N（纳秒）
  _sec=$(date +%s)
  _ns=$(date +%N 2>/dev/null) || _ns=""
  # macOS BSD date 不支持 %N，可能返回字面量 "N" 或 "%N"；
  # 用纯数字校验（grep -E '^[0-9]+$'）确保只有真正的纳秒数值才进入毫秒计算，
  # 避免非数字字符串在算术展开中被当作变量名。
  if [ -n "$_ns" ] && printf '%s' "$_ns" | grep -qE '^[0-9]+$'; then
    # GNU date：取纳秒前 3 位作为毫秒部分，去前导零防八进制
    _ms=$(printf '%s' "$_ns" | cut -c1-3)
    _ms="${_ms#"${_ms%%[!0]*}"}"
    _ms="${_ms:-0}"
    TIMESTAMP_MS=$(( _sec * 1000 + _ms ))
  else
    # 兜底：BSD date（macOS）/ 不支持 %N，秒级精度
    TIMESTAMP_MS=$(( _sec * 1000 ))
  fi
fi

# ── 纯 shell JSON 字段注入 ────────────────────────────────────────────────────
# 策略：在原始 JSON 的最后一个 "}" 前插入两个字段。
# 假设 CodeBuddy 传入的是合法单行 JSON 对象；若解析失败则构造最小对象兜底。
#
# 转义 EVENT_NAME 中可能出现的双引号和反斜杠（hook 子命令名通常是 kebab-case，
# 实际不含特殊字符，但防御性处理更安全）。
SAFE_EVENT=$(printf '%s' "$EVENT_NAME" | sed 's/\\/\\\\/g; s/"/\\"/g')

# 检查 INPUT 是否看起来像一个 JSON 对象（以 { 开头、} 结尾）
TRIMMED=$(printf '%s' "$INPUT" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
if printf '%s' "$TRIMMED" | grep -q '^{.*}$'; then
  # 去掉末尾的 "}"，追加新字段后补回 "}"
  # 若对象非空（末尾 } 前有内容），在已有字段后加逗号再追加
  INNER=$(printf '%s' "$TRIMMED" | sed 's/}$//')
  # 判断对象是否为空 {}
  INNER_TRIMMED=$(printf '%s' "$INNER" | sed 's/^{[[:space:]]*//')
  if [ -z "$INNER_TRIMMED" ]; then
    # 空对象
    RECORD="{\"hook_event_name\":\"${SAFE_EVENT}\",\"timestamp\":${TIMESTAMP_MS}}"
  else
    # 非空对象：在末尾追加字段
    RECORD="${INNER},\"hook_event_name\":\"${SAFE_EVENT}\",\"timestamp\":${TIMESTAMP_MS}}"
  fi
else
  # 非法 JSON：构造最小兜底对象
  RECORD="{\"hook_event_name\":\"${SAFE_EVENT}\",\"timestamp\":${TIMESTAMP_MS}}"
fi

# ── 原子追加写入 ──────────────────────────────────────────────────────────────
# 先写临时文件再 cat 追加，防止并发写入产生半行数据。
TMP_FILE="${LOG_FILE}.tmp.$$"
printf '%s\n' "$RECORD" > "$TMP_FILE" 2>/dev/null || true
cat "$TMP_FILE" >> "$LOG_FILE" 2>/dev/null || true
rm -f "$TMP_FILE" 2>/dev/null || true

# 静默退出（CodeBuddy 不关心 hook 的 stdout）
exit 0
