#!/bin/sh
# Rejects a commit message that breaks the rules in AGENTS.md: a subject of 50
# characters or fewer without a final period, one blank line, a body, body lines
# of 72 characters or fewer, and real line breaks instead of "\n". Prose
# paragraphs fill each line before wrapping. Formatted blocks keep their line
# breaks. A body line without spaces, such as a URL, may be longer.
set -eu

file=$1
subject=$(sed -n 1p "$file")
case $subject in
  "Merge "* | "Revert "* | "fixup! "* | "squash! "*) exit 0 ;;
esac

fail() {
  printf 'commit-msg: %s\n' "$1" "write the message to a file and commit with git commit -F." >&2
  exit 1
}

if grep -q '\\n' "$file"; then
  fail 'the message has a literal "\n"; use real line breaks'
fi

[ ${#subject} -le 50 ] || fail "the subject has ${#subject} characters; the limit is 50"

case $subject in
  *.) fail "the subject ends with a period" ;;
esac

[ -z "$(sed -n 2p "$file")" ] || fail "the second line must be blank"

sed -n '3,$p' "$file" | grep -q '[^[:space:]]' || fail "the message has no body"

long=$(sed -n '3,$p' "$file" | awk 'length($0) > 72 && $0 ~ / / { print NR + 2; exit }')
[ -z "$long" ] || fail "line $long is longer than 72 characters"

short=$(awk '
  function check_paragraph(    i, word) {
    if (!formatted && !bad_line) {
      for (i = 1; i < count; i++) {
        word = lines[i + 1]
        sub(/[[:space:]].*$/, "", word)
        if (length(lines[i]) + 1 + length(word) <= 72) {
          bad_line = numbers[i]
          break
        }
      }
    }

    count = 0
    formatted = 0
  }

  NR <= 2 { next }

  fence != "" {
    if ($0 ~ /^(`{3,}|~{3,})[[:space:]]*$/ && substr($0, 1, 1) == fence) {
      match($0, /^(`+|~+)/)
      if (RLENGTH >= fence_length) fence = ""
    }
    next
  }

  /^(`{3,}|~{3,})/ {
    check_paragraph()
    match($0, /^(`+|~+)/)
    fence = substr($0, 1, 1)
    fence_length = RLENGTH
    next
  }

  /^[[:space:]]*$/ {
    check_paragraph()
    next
  }

  {
    if ($0 ~ /^[[:space:]>#|]/ ||
        $0 ~ /^([-*+][[:space:]]|[0-9]+[.)][[:space:]])/ ||
        $0 ~ /^[-=*_]{3,}$/ ||
        $0 ~ /^[[:alnum:]][[:alnum:]-]*:[[:space:]]/) {
      formatted = 1
    }

    lines[++count] = $0
    numbers[count] = NR
  }

  END {
    check_paragraph()
    if (bad_line) print bad_line
  }
' "$file")
[ -z "$short" ] || fail "unnecessary line break after line $short; reflow the paragraph to 72 characters"
