#!/bin/bash
#
# state_test.sh — The @@@state system command and *name* substitution.
#
# Added 2026-08-22 by the error-gallery coverage review, which found that no
# suite covered @@@state at all: its two error messages had never been
# printed by any test.  What is pinned here, each by outcome:
#
#   * :value sets a variable and *name* substitutes it in a klammer BODY
#     (bodies are processed at application time).  A top-level *name* of a
#     document-declared variable does NOT substitute — the same read-time
#     asymmetry @cond had before 2026-08-15; if that is ever changed, case 2
#     documents today's behaviour and should change with it.
#   * :replace replaces an existing value.
#   * Redefining WITHOUT :replace is an error that names the remedy.
#   * An undefined variable's error lists the frames it searched.
#   * A shell environment variable substitutes like any state variable.
#
# NOT to be confused with "state_test", the C++ diagnostic program built from
# state_test.cpp in this directory: that one constructs engine objects and
# prints what it gets, for a person to read, and asserts nothing (see the
# `smoke` target in tst/Makefile).  This is the regression suite.  The ".sh"
# is the only thing distinguishing them — the same collision as
# eval_test/eval_test.sh, documented there first.
#
# Usage: ./state_test.sh   (needs ktext on PATH)
# Exit code: 0 if all tests pass, 1 otherwise.

PASS=0
FAIL=0
KTEXT=ktext
export KT_STATE_PROBE="probe-value"   # for the environment-variable case

red=$'\033[31m'
green=$'\033[32m'
bold=$'\033[1m'
reset=$'\033[0m'

# check_eq NAME EXPECTED KTEXT_ARGS... — trimmed stdout equals EXPECTED.
check_eq() {
    local name="$1" expected="$2"
    shift 2
    local out status
    out=$("$KTEXT" "$@" 2>/dev/null)
    status=$?
    out=$(printf '%s' "$out" | sed -e 's/[ \t]*$//' | grep -v '^$')
    if [ $status -eq 0 ] && [ "$out" = "$expected" ]; then
        echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
    else
        echo "${red}FAIL${reset} $name"
        echo "       expected: [$expected]"; echo "       got:      [$out] (exit $status)"
        FAIL=$((FAIL+1))
    fi
}

# check_error NAME PATTERN KTEXT_ARGS... — nonzero exit, PATTERN in the
# message.  Wrap-insensitive: error prose is justified to 80 columns, so a
# phrase may wrap anywhere.
check_error() {
    local name="$1" pattern="$2"
    shift 2
    local out status
    out=$("$KTEXT" "$@" 2>&1)
    status=$?
    if [ $status -eq 0 ]; then
        echo "${red}FAIL${reset} $name — expected an error but ktext succeeded"
        FAIL=$((FAIL+1)); return
    fi
    if printf '%s' "$out" | tr '\n' ' ' | tr -s ' ' | grep -qF "$pattern"; then
        echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
    else
        echo "${red}FAIL${reset} $name — expected error to contain [$pattern]"
        echo "       got: $(printf '%s' "$out" | sed 's/\x1b\[[0-9;]*m//g' | tr '\n' ' ' | head -c 200)"
        FAIL=$((FAIL+1))
    fi
}

echo "${bold}@@@state tests${reset}"
echo "=============="
echo

echo "-- setting and substituting --"
check_eq " 1. :value sets; *name* substitutes in a body" \
    "[hello]" \
    --klammersets none -s '@@@state V :value hello @@@ @@f : [*V*] @@ @f@' -d

# Documents TODAY'S behaviour: a top-level *name* of a document-declared
# variable is left as written (bodies substitute; the top level does not).
check_eq " 2. a top-level *name* does not substitute (current behaviour)" \
    "[*V*]" \
    --klammersets none -s '@@@state V :value hello @@@ [*V*]' -d

check_eq " 3. an environment variable substitutes" \
    "[probe-value]" \
    --klammersets none -s '@@f : [*KT_STATE_PROBE*] @@ @f@' -d

echo
echo "-- replacing --"
check_eq " 4. :replace replaces an existing value" \
    "[b]" \
    --klammersets none \
    -s '@@@state V :value a @@@ @@@state V :replace b @@@ @@f : [*V*] @@ @f@' -d

check_error " 5. redefining without :replace is an error naming the remedy" \
    "Use ':replace <new-value>' to replace the current value" \
    --klammersets none -s '@@@state V :value a @@@ @@@state V :value b @@@' -d

echo
echo "-- the undefined variable --"
check_error " 6. an undefined *name* in a body is a located error" \
    'Variable "Missing" not defined' \
    --klammersets none -s '@@f : [*Missing*] @@ @f@' -d

check_error " 7. ... that lists the frames it searched" \
    "(searched:" \
    --klammersets none -s '@@f : [*Missing*] @@ @f@' -d

echo
echo "=============="
echo "Results: ${green}$PASS passed${reset}, ${red}$FAIL failed${reset}"
[ $FAIL -eq 0 ]
