#!/usr/bin/env bash
# tests/validate/run-tests.sh -- orchestrator tests with MOCK checks.
# Verifies: profile resolution, explicit --checks, verdict aggregation across the
# full contract (PASS/FAIL/HOLD/MANUAL/SKIP), worst-wins precedence
# (FAIL>HOLD>MANUAL>PASS), --stop-on-fail, --include-disruptive pass-through,
# missing-check -> HOLD, and a check that emits NO RESULT line (rc trusted).
set -uo pipefail
SD="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"; VAL="$SD/../../scripts/validate.sh"
P=0; F=0; ok(){ echo "PASS: $1"; P=$((P+1)); }; no(){ echo "FAIL: $1"; F=$((F+1)); }
chk(){ [ "$2" = "$3" ] && ok "$1" || no "$1 (got '$2' want '$3')"; }

# build a mock checkdir where each check's exit code is encoded in its name via env
W="$(mktemp -d)"; trap 'rm -rf "$W"' EXIT; mkdir -p "$W/checks"
mkcheck(){ # mkcheck <id> <exit> [emit?]
  local id="$1" ex="$2" em="${3:-yes}"
  { echo '#!/usr/bin/env bash'
    echo ". \"\$(dirname \"\$0\")/../../../scripts/lib-validate.sh\" 2>/dev/null || true"
    echo "echo \"(human output for $id)\""
    [ "$em" = yes ] && echo "echo \"RESULT $id WORD $ex - mock message\""
    echo "exit $ex"
  } > "$W/checks/$id.sh"; chmod +x "$W/checks/$id.sh"
}
# disruptive-aware mock: SKIP(4) unless VR_DISRUPTIVE=1 then PASS(0)
mkcheck_disr(){ local id="$1"
  { echo '#!/usr/bin/env bash'
    echo "if [ \"\${VR_DISRUPTIVE:-0}\" = 1 ]; then echo \"RESULT $id WORD 0 - ran disruptive\"; exit 0;"
    echo "else echo \"RESULT $id WORD 4 - skipped (disruptive not requested)\"; exit 4; fi"
  } > "$W/checks/$id.sh"; chmod +x "$W/checks/$id.sh"
}
run_val(){ VR_CHECKDIR="$W/checks" bash "$VAL" "$@" 2>&1; }

# 1. all-pass profile -> exit 0
mkcheck a 0; mkcheck b 0
OUT="$(run_val --checks a,b)"; chk "all-pass verdict" "$?" 0
grep -q 'OVERALL: PASS (exit 0)' <<<"$OUT" && ok "all-pass report" || no "all-pass report"

# 2. one FAIL dominates -> exit 1
mkcheck c 1; OUT="$(run_val --checks a,c,b)"; chk "fail-dominates" "$?" 1
grep -q 'FAIL=1' <<<"$OUT" && ok "fail counted" || no "fail counted"

# 3. HOLD (no fail) -> exit 2
mkcheck h 2; OUT="$(run_val --checks a,h)"; chk "hold verdict" "$?" 2

# 4. MANUAL (no fail/hold) -> exit 3
mkcheck m 3; OUT="$(run_val --checks a,m)"; chk "manual verdict" "$?" 3

# 5. precedence: FAIL beats HOLD beats MANUAL
OUT="$(run_val --checks m,h,c)"; chk "precedence FAIL>HOLD>MANUAL" "$?" 1
OUT="$(run_val --checks m,h)";   chk "precedence HOLD>MANUAL" "$?" 2

# 6. SKIP doesn't lower verdict (all pass/skip -> 0)
mkcheck s 4; OUT="$(run_val --checks a,s)"; chk "skip stays pass" "$?" 0
grep -q 'SKIP=1' <<<"$OUT" && ok "skip counted" || no "skip counted"

# 7. --stop-on-fail halts
mkcheck z 0; OUT="$(run_val --stop-on-fail --checks c,z)"
grep -q 'halting after c' <<<"$OUT" && ok "stop-on-fail halts" || no "stop-on-fail halts"
grep -q 'human output for z' <<<"$OUT" && no "stop-on-fail ran z (should not)" || ok "stop-on-fail skipped z"

# 8. missing check -> HOLD
OUT="$(run_val --checks a,doesnotexist)"; chk "missing-check verdict" "$?" 2
grep -q 'not found' <<<"$OUT" && ok "missing-check reported" || no "missing-check reported"

# 9. check emits NO RESULT line -> rc trusted, synthesized line
mkcheck silent 1 no; OUT="$(run_val --checks silent)"; chk "no-result-line rc-trusted" "$?" 1
grep -q 'no RESULT line emitted' <<<"$OUT" && ok "no-result-line synthesized" || no "no-result-line synthesized"

# 10. --include-disruptive pass-through
#     (SKIP does not lower the OVERALL verdict -- a lone skipped check => OVERALL PASS(0)
#      with SKIP=1 in totals; that is the contract. Assert the check's OWN result, not
#      just the overall, by reading its RESULT line.)
mkcheck_disr d
OUT="$(run_val --checks d)"; chk "disruptive-default overall" "$?" 0
grep -qE '^RESULT d WORD 4 ' <<<"$OUT" && ok "disruptive default emitted SKIP(4)" || no "disruptive default emitted SKIP(4)"
grep -q 'SKIP=1' <<<"$OUT" && ok "disruptive default counted as skip" || no "disruptive default counted as skip"
OUT="$(run_val --include-disruptive --checks d)"; chk "disruptive included overall" "$?" 0
grep -qE '^RESULT d WORD 0 .*ran disruptive' <<<"$OUT" && ok "disruptive included ran" || no "disruptive included ran"

# 11. profile resolution
mkcheck d011-01-charms 0; mkcheck d011-02-vip-jumphost 0; mkcheck d011-03-vip-tenant 0
OUT="$(run_val --profile post-restart)"; chk "profile post-restart" "$?" 0
grep -q 'd011-03-vip-tenant' <<<"$OUT" && ok "profile expanded" || no "profile expanded"

# 12. unknown profile -> HOLD(2)
OUT="$(run_val --profile nope 2>&1)"; chk "unknown-profile" "$?" 2

# 12b. NON-CONTRACT exit (127 tool-missing / 141 SIGPIPE class) -> OVERALL HOLD(2)
#      with the (nonstd) annotation VISIBLE in the report (pre-fix: counted n_hold
#      but note_worst matched nothing -> OVERALL PASS 0; annotation was dead code).
mkcheck crash 127 no
OUT="$(run_val --checks a,crash)"; chk "nonstd-exit overall HOLD" "$?" 2
grep -q '127(nonstd)' <<<"$OUT" && ok "nonstd annotation in report" || no "nonstd annotation in report"
grep -q 'HOLD=1' <<<"$OUT" && ok "nonstd counted as hold" || no "nonstd counted as hold"

# 13. --list exits 0 and shows profiles
OUT="$(run_val --list)"; chk "list rc" "$?" 0
grep -q 'full-d011' <<<"$OUT" && ok "list shows profile" || no "list shows profile"

echo; [ "$F" = 0 ] && { echo "ALL PASS ($P checks)"; exit 0; } || { echo "FAILURES: $F"; exit 1; }
