#!/usr/bin/env bash
#
# iter-157 installable commit-msg git hook.
#
# Purpose: close the natural git-workflow automation gap surfaced by iter-156
# audit — iter-153/154 advisor was a one-shot manual CLI requiring operator
# discipline to invoke. Industry-standard 2026 best practice per
# compilerla/conventional-pre-commit + commitlint is to install a `commit-msg`
# git hook that runs automatically AFTER the operator writes the commit message
# but BEFORE git records the commit. Silent-fail-class violations
# (COMPOUND-PREFIX, MISSING-TYPE) get rejected immediately with no operator
# discipline required.
#
# This file is the PORTABLE HOOK BODY — it is copied into the operator's
# `.git/hooks/commit-msg` by the iter-157 installer. It locates the cc-skills
# repo containing the iter-153 advisor via the following precedence chain:
#
#   1. ITER157_COMMIT_MSG_HOOK_CC_SKILLS_REPO_PATH_OVERRIDE env var (highest)
#   2. `git config --get cc-skills.repo-path` (repo-local config, persisted)
#   3. `$HOME/eon/cc-skills` (canonical default location)
#
# When the iter-153 advisor cannot be located the hook exits 0 (fail-OPEN)
# with a one-line warning rather than blocking the operator's commit — the
# hook is opt-in quality gate, not a hard dependency. Operators who want
# fail-CLOSED semantics can set
# ITER157_COMMIT_MSG_HOOK_FAIL_MODE_ON_ADVISOR_NOT_FOUND=closed.
#
# Industry-standard bypass: `git commit --no-verify` skips ALL git hooks
# including this one. Use sparingly per the cc-skills CLAUDE.md "never skip
# hooks unless explicitly asked" policy.
#
# Git contract: this script is invoked by git with ONE argument — the
# absolute path to the .git/COMMIT_EDITMSG file containing the proposed
# commit message. Exit 0 accepts, non-zero rejects.

set -euo pipefail

# ─── Step 1: locate the iter-153 advisor script via the precedence chain ────

iter157_locate_cc_skills_repo_path_via_env_override_or_git_config_or_canonical_default_location() {
    if [[ -n "${ITER157_COMMIT_MSG_HOOK_CC_SKILLS_REPO_PATH_OVERRIDE:-}" ]]; then
        printf '%s\n' "$ITER157_COMMIT_MSG_HOOK_CC_SKILLS_REPO_PATH_OVERRIDE"
        return 0
    fi
    local git_config_repo_path
    git_config_repo_path=$(git config --get cc-skills.repo-path 2>/dev/null || true)
    if [[ -n "$git_config_repo_path" ]]; then
        printf '%s\n' "$git_config_repo_path"
        return 0
    fi
    printf '%s\n' "$HOME/eon/cc-skills"
}

ITER157_CC_SKILLS_REPO_PATH=$(iter157_locate_cc_skills_repo_path_via_env_override_or_git_config_or_canonical_default_location)
ITER157_ITER153_ADVISOR_RELATIVE_PATH="scripts/iter153-operator-facing-pre-commit-dry-run-advisor-classifying-proposed-conventional-commit-subject-through-iter82-grammar-and-iter151-overlay-with-human-readable-verdict-default-and-json-output-mode-for-ai-agent-automation-pipeline-consumption.sh"
ITER157_ITER153_ADVISOR_ABSOLUTE_PATH="$ITER157_CC_SKILLS_REPO_PATH/$ITER157_ITER153_ADVISOR_RELATIVE_PATH"

if [[ ! -x "$ITER157_ITER153_ADVISOR_ABSOLUTE_PATH" ]]; then
    if [[ "${ITER157_COMMIT_MSG_HOOK_FAIL_MODE_ON_ADVISOR_NOT_FOUND:-open}" == "closed" ]]; then
        printf '[iter-157 commit-msg hook] ERROR: iter-153 advisor not found at %s — fail-CLOSED mode rejects commit.\n' \
            "$ITER157_ITER153_ADVISOR_ABSOLUTE_PATH" >&2
        exit 1
    fi
    printf '[iter-157 commit-msg hook] WARNING: iter-153 advisor not found at %s — fail-OPEN, allowing commit.\n' \
        "$ITER157_ITER153_ADVISOR_ABSOLUTE_PATH" >&2
    exit 0
fi

# ─── Step 2: extract the subject line from the commit message file ──────────
#
# Git passes the absolute path to .git/COMMIT_EDITMSG (or equivalent for merge,
# squash, etc.) as $1. The subject is the first non-blank line that doesn't
# start with '#' (git's comment-line convention).

ITER157_COMMIT_MSG_FILE_ABSOLUTE_PATH="${1:-}"
if [[ -z "$ITER157_COMMIT_MSG_FILE_ABSOLUTE_PATH" ]] || [[ ! -f "$ITER157_COMMIT_MSG_FILE_ABSOLUTE_PATH" ]]; then
    printf '[iter-157 commit-msg hook] ERROR: missing or unreadable commit-msg file argument: %s\n' \
        "$ITER157_COMMIT_MSG_FILE_ABSOLUTE_PATH" >&2
    exit 1
fi

iter157_extract_first_non_comment_non_blank_subject_line_from_commit_editmsg_file() {
    local commit_msg_file_absolute_path="$1"
    awk '
        /^[[:space:]]*$/ { next }
        /^[[:space:]]*#/ { next }
        { print; exit }
    ' "$commit_msg_file_absolute_path"
}

ITER157_PROPOSED_SUBJECT_LINE=$(
    iter157_extract_first_non_comment_non_blank_subject_line_from_commit_editmsg_file "$ITER157_COMMIT_MSG_FILE_ABSOLUTE_PATH"
)

# Empty commit message: git itself will abort the commit. Don't double-error.
if [[ -z "$ITER157_PROPOSED_SUBJECT_LINE" ]]; then
    exit 0
fi

# ─── Step 3: skip merge / squash / fixup / revert commits ──────────────────
#
# These commits are auto-generated by git itself and follow their own format
# conventions ("Merge branch ...", "fixup! ...", "Revert ...", etc.). Subjecting
# them to conventional-commits validation would be hostile to normal git flow.

case "$ITER157_PROPOSED_SUBJECT_LINE" in
    "Merge "* | "Revert "* | "fixup! "* | "squash! "* | "amend! "*)
        exit 0
        ;;
esac

# ─── Step 4: delegate classification to iter-153 advisor --strict ───────────
#
# --strict makes iter-153 exit non-zero on silent-fail-class violations
# (COMPOUND-PREFIX, MISSING-TYPE) while keeping the iter-150 50/72 long-subject
# overlay informational. The advisor prints its own verdict + remediation
# banner to stderr; we just forward the exit code.
#
# Iter-162: switched from `-- "$SUBJECT_LINE"` to `--message-file
# "$COMMIT_MSG_FILE"` so the advisor can see the FULL multi-line commit
# message (subject + body) and apply iter-162 BREAKING-CHANGE footer-
# token detection per Conventional Commits §13. The advisor still
# extracts the subject internally via the same first-non-comment-non-
# blank-line convention this hook used.

exec "$ITER157_ITER153_ADVISOR_ABSOLUTE_PATH" --strict --message-file "$ITER157_COMMIT_MSG_FILE_ABSOLUTE_PATH"
