#!/bin/sh
# scripts/gates — unified runner for the four green gates.
#
# This is the canonical entrypoint. EXECUTION.md, the pre-push hook,
# and the Forgejo workflow all delegate here so the gate definition
# lives in exactly one place; drift between docs and reality is
# impossible by construction.
#
# Usage:
#   scripts/gates              # all four gates, stop on first failure
#   scripts/gates --fast       # skip the Zig gate (slow path); pre-push hook uses this
#   scripts/gates --keep-going # run every gate even if an earlier one failed
#
# Exit codes: 0 all green; 1 some gate failed; 2 usage error.
#
# Reusable note: every gate is a POSIX-shell run-and-report call.
# Federation siblings copy this verbatim; only the gate command
# bodies need adjusting (and most of them are the same anyway).

set -eu

REPO_ROOT="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
cd "$REPO_ROOT"

PY="${PY:-.venv/bin/python}"
ZIG="${ZIG:-.venv/lib/python3.13/site-packages/ziglang/zig}"

fast=0
keep_going=0
for arg in "$@"; do
    case "$arg" in
        --fast) fast=1 ;;
        --keep-going) keep_going=1 ;;
        -h|--help)
            sed -n '1,/^set/p' "$0" | sed 's/^# \{0,1\}//'
            exit 0
            ;;
        *)
            printf 'scripts/gates: unknown flag: %s\n' "$arg" >&2
            exit 2
            ;;
    esac
done

# Colour helpers — disabled when stdout isn't a tty (CI logs stay clean).
if [ -t 1 ]; then
    BOLD="$(printf '\033[1m')"
    GREEN="$(printf '\033[32m')"
    RED="$(printf '\033[31m')"
    RESET="$(printf '\033[0m')"
else
    BOLD=""; GREEN=""; RED=""; RESET=""
fi

failed_gates=""

run_gate() {
    name="$1"; shift
    printf '%s==> %s%s\n' "$BOLD" "$name" "$RESET"
    if "$@"; then
        printf '%s    PASS%s  %s\n' "$GREEN" "$RESET" "$name"
    else
        printf '%s    FAIL%s  %s\n' "$RED" "$RESET" "$name"
        failed_gates="$failed_gates $name"
        if [ "$keep_going" -eq 0 ]; then
            printf '\n%sFAILED:%s%s\n' "$RED" "$RESET" "$failed_gates"
            printf '(run with --keep-going to see every gate)\n'
            exit 1
        fi
    fi
}

# Gate 1 — lint
run_gate "ruff (lint)" "$PY" -m ruff check src tests

# Gate 2 — types
run_gate "mypy --strict" "$PY" -m mypy --strict src

# Gate 3 — Python tests + coverage. `--cov` (no source arg) honours
# `[tool.coverage.run].source = ["mgf.hrb"]` in pyproject.toml.
run_gate "pytest (default suite + coverage)" "$PY" -m pytest -q --cov --cov-fail-under=80

# Gate 3.5 — import-linter. Verifies the SDK's layering posture
# (PR-01: SDK forbids UI frameworks; PR-06: fmt is stricter still).
# A5 (presentation-layer plan) added these contracts; this gate
# wires them into the canonical runner so a future contributor
# accidentally importing PySide6 from `mgf.hrb.diagnostics` fails
# CI immediately. Fast (~1s); no reason to skip on --fast.
run_gate "import-linter (layering)" .venv/bin/lint-imports --config pyproject.toml

# Gate 3.6 — transitive-dep audit (PR-01 supply-chain). import-linter
# only sees the SDK's own source; it can't catch a UI framework
# arriving via a transitive dep of `mgf-common` (or any future
# production dep). This walks the actual installed dep closure from
# `mgf-hrb` and fails on any forbidden framework. Hardening unit S7
# of the presentation-layer hardening plan (2026-05-26).
run_gate "audit transitive deps (PR-01 supply-chain)" "$PY" scripts/audit_transitive_deps.py

# Gate 4 — Zig tests. Slow (~10s on a warm cache, ~60s cold). Pre-push
# hook skips this; CI runs it; local `scripts/gates` runs it by default.
if [ "$fast" -eq 1 ]; then
    printf '%s    SKIP%s  zig build test (--fast)\n' "$BOLD" "$RESET"
else
    run_gate "zig build test" "$ZIG" build --build-file hrb-code/build.zig --cache-dir hrb-code/.zig-cache test --summary all

    # Gate 4.5 — Zig coverage (informational). The script self-skips
    # (exit 0) when kcov is absent, so this never blocks contributors
    # without the tool; where kcov IS installed (CI), it prints the
    # broker's line-coverage number. No threshold here — measurement,
    # not enforcement, until a baseline is agreed.
    run_gate "zig coverage (informational)" scripts/zig-coverage
fi

if [ -n "$failed_gates" ]; then
    printf '\n%sFAILED:%s%s\n' "$RED" "$RESET" "$failed_gates"
    exit 1
fi

printf '\n%sAll gates green.%s\n' "$GREEN" "$RESET"
