#!/bin/sh
# commit-msg hook — ai-code-style-kit
# 规则源：skills/commit-style/SKILL.md（v1.0）§2/§3.5/§7.1
# 同规则的权威实现见 extension/rules.cjs（两侧口径必须一致，tests 负责对照）
# 跨平台：Linux / macOS 原生 sh；Windows 经 Git for Windows 自带 sh。零依赖。
#
# 安装：hooks/install.sh（或 install.ps1），或手动：
#   mkdir -p .githooks && cp hooks/commit-msg .githooks/ && git config core.hooksPath .githooks

msg_file="$1"
[ -f "$msg_file" ] || exit 0

# 首行（去首尾空白与 CR，与 rules.cjs 的 trim 对齐）
first=$(sed -n '1p' "$msg_file" | tr -d '\r' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')

# 豁免：git 自动生成的 merge / revert 消息（SKILL.md §3.5）
case "$first" in
  "Merge "*|'Revert "'*) exit 0 ;;
esac

errors=0
fail() {
  echo "commit-msg: $*" >&2
  errors=$((errors + 1))
}

TYPE_RE='(feat|fix|perf|refactor|test|docs|build|ci|chore|style|revert)'

# header 格式：type(scope)!: 主题
if ! printf '%s' "$first" | grep -Eq "^${TYPE_RE}(\\([a-z0-9_.-]+\\))?!?: .+"; then
  echo "commit-msg: header 不符合 type(scope)!: 主题 格式（type ∈ feat/fix/perf/refactor/test/docs/build/ci/chore/style/revert）：$first" >&2
  echo "commit-msg: 提交被拒绝。规范全文见 skills/commit-style/SKILL.md" >&2
  exit 1
fi

subject=$(printf '%s' "$first" | sed -E "s/^${TYPE_RE}(\\([a-z0-9_.-]+\\))?!?: //" | sed -E 's/^[[:space:]]+//')
hdr_type=$(printf '%s' "$first" | sed -E "s/^(${TYPE_RE}).*/\1/")

# 主题结尾不加句号（"v1.0" 这类数字点结尾不算）
case "$subject" in
  *。) fail "主题结尾不加句号" ;;
esac
if printf '%s' "$subject" | grep -Eq '[^0-9]\.$'; then
  fail "主题结尾不加句号"
fi

# 显示宽度：全角记 2 / 半角记 1，上限 100（约 50 个汉字）
# 探测 wc -m 是否按码点计数（UTF-8 locale）；不行则依次尝试常见 UTF-8 locale；
# 都不行（无 UTF-8 环境的极端情况）跳过宽度检查（echo 0），其余规则仍生效。
dw_width() {
  _probe=$(printf '\344\275\240' | wc -m)
  if [ "$_probe" -eq 1 ] 2>/dev/null; then
    _c=$(printf '%s' "$1" | wc -m); _b=$(printf '%s' "$1" | wc -c)
    echo $(( _c + (_b - _c) / 2 )); return
  fi
  for _l in C.UTF-8 en_US.UTF-8; do
    _probe=$(printf '\344\275\240' | LC_ALL="$_l" wc -m)
    if [ "$_probe" -eq 1 ] 2>/dev/null; then
      _c=$(printf '%s' "$1" | LC_ALL="$_l" wc -m); _b=$(printf '%s' "$1" | wc -c)
      echo $(( _c + (_b - _c) / 2 )); return
    fi
  done
  echo 0
}
width=$(dw_width "$subject")
if [ "$width" -gt 100 ] 2>/dev/null; then
  fail "主题显示宽度 $width > 100（全角记 2/半角记 1，约 50 个汉字）"
fi

# 空泛词封闭匹配（与 extension/rules.cjs 一致）：黑名单词单独，或仅带泛称宾语
# en 归一化与 JS 的 trim+小写+折叠空白对齐（tr 只做小写与压缩空格会漏多空格/tab 形态）
zh=$(printf '%s' "$subject" | tr -d '[:space:]')
for _w in 更新 优化 修复 完善 调整 修改 重构 小改动; do
  for _g in "" 代码 性能 bug BUG Bug 功能 问题 文件 逻辑 一些问题; do
    if [ "$zh" = "$_w$_g" ]; then
      fail "主题为空泛词（单独或仅带泛称宾语）：\"$subject\" —— 宾语须具体到模块/现象/数字"
      break 2
    fi
  done
done
en=$(printf '%s' "$subject" | tr '[:upper:]' '[:lower:]' | sed -E 's/[[:space:]]+/ /g; s/^ //; s/ $//')
for _w in update fix optimize refactor cleanup tweak improve wip; do
  for _g in "" code codes performance perf bug bugs file files logic stuff things; do
    if [ -z "$_g" ]; then _pat="$_w"; else _pat="$_w $_g"; fi
    if [ "$en" = "$_pat" ]; then
      fail "主题为空泛词（单独或仅带泛称宾语）：\"$subject\" —— 宾语须具体到模块/现象/数字"
      break 2
    fi
  done
done

# 验证行：feat/fix/perf 必须有（token 双语：验证: / Verified:）
case "$hdr_type" in
  feat|fix|perf)
    if ! grep -Eq '^验证:[[:blank:]]*[^[:space:]]' "$msg_file" && \
       ! grep -Eq '^[Vv]erified:[[:blank:]]*[^[:space:]]' "$msg_file"; then
      fail "type 为 $hdr_type，必须有\"验证:\"行（英文项目 Verified:）——只写真实跑过的检查与结果；没跑过就先别提交"
    fi
    ;;
esac

# BREAKING CHANGE 脚注必须全大写
if grep -Eiq '^[[:blank:]]*breaking[[:blank:]]+change[[:blank:]]*:' "$msg_file" && \
   ! grep -Eq '^[[:blank:]]*BREAKING CHANGE[[:blank:]]*:' "$msg_file"; then
  fail "脚注 BREAKING CHANGE 必须全大写"
fi

if [ "$errors" -gt 0 ]; then
  echo "commit-msg: 提交被拒绝（$errors 个问题）。规范全文见 skills/commit-style/SKILL.md" >&2
  exit 1
fi
exit 0
