#!/usr/bin/env bash

# The interview asks, the stages consume, and nothing survives the run.
#
# Three properties matter enough to pin:
#
#   1. Every question maps to a stage that reads its answer. A prompt whose
#      answer nothing consumes is a control that lies -- the same defect this
#      repository refused to ship on the SSH Keys page -- and it is an easy one
#      to introduce, because asking is cheap and wiring up is not.
#   2. Every answer a stage reads is one the interview asks. The reverse gap is
#      quieter and worse: the stage silently takes its fallback forever.
#   3. The answers file is deleted on every exit path. It carries an email
#      address, and it is transient by design -- there is deliberately no
#      remembered state between runs.

set -uo pipefail

repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
interview="$repo_dir/setup/scripts/interview"
install_script="$repo_dir/install"

findings=()
note() { findings+=("$1"); }

# ── 1 & 2. Questions and consumers agree ─────────────────────────────────────

asked="$(grep -oE '^record [A-Z_]+' "$interview" | awk '{print $2}' | sort -u)"

# Stages read answers as ${PANAMA_FOO:-default}; install re-exports them.
consumed="$(grep -rhoE '\$\{PANAMA_[A-Z_]+' "$repo_dir"/setup/scripts/* "$install_script" 2>/dev/null \
    | sed 's/^\${//' | sort -u)"

# Not answers: paths the installer sets up for itself, plus PANAMA_MOK_CERT,
# which is the akmods certificate path with an override on it. Nothing sets that
# override outside the hardware contract, which needs a certificate it is allowed
# to create -- enrolling the real one to find out what happens is not a test.
INFRASTRUCTURE='^(PANAMA_PATH|PANAMA_ANSWERS|PANAMA_BASH|PANAMA_DOT|PANAMA_OLD|PANAMA_APPLICATION_DIR|PANAMA_ICON_DIR|PANAMA_UNIT_DIR|PANAMA_CURSOR_DIR|PANAMA_WALLPAPER_DIR|PANAMA_MOK_CERT)$'

while read -r key; do
    [[ -n "$key" ]] || continue
    grep -qx "$key" <<<"$consumed" \
        || note "the interview asks for $key, but no stage ever reads it"
done <<<"$asked"

while read -r key; do
    [[ -n "$key" ]] || continue
    [[ "$key" =~ $INFRASTRUCTURE ]] && continue
    grep -qx "$key" <<<"$asked" \
        || note "a stage reads $key, but the interview never asks for it"
done <<<"$consumed"

# ── 3. Nothing is left behind ────────────────────────────────────────────────

# Cleanup rides the EXIT trap; INT and TERM must exit explicitly, because a
# trap handler that merely cleans up lets bash carry on with the remaining
# stages after a Ctrl-C -- MOK enrollment and firmware included.
grep -q 'trap cleanup EXIT' "$install_script" \
    || note 'install does not arm a cleanup trap on EXIT'
grep -qE "trap 'exit [0-9]+' INT" "$install_script" \
    || note 'install does not exit on SIGINT, so Ctrl-C would keep installing'
grep -qE "trap 'exit [0-9]+' TERM" "$install_script" \
    || note 'install does not exit on SIGTERM, so a kill would keep installing'
grep -q 'rm -f "$PANAMA_ANSWERS"' "$install_script" \
    || note 'the cleanup trap does not delete the answers file'
grep -qE 'mktemp' "$install_script" \
    || note 'install does not create the answers file with mktemp'

# Declining must stop the run rather than count as one failed stage among five.
grep -qE 'if ! "\$PANAMA_PATH/setup/scripts/interview"; then' "$install_script" \
    || note 'install does not treat a declined interview as fatal'

# ── 4. A real run, with gum stubbed ──────────────────────────────────────────
#
# The interview is built on gum, which needs a terminal. Standing in a stub on
# PATH exercises the actual script -- its ordering, its quoting, and the file it
# writes -- rather than asserting things about its source text.

stub_dir="$(mktemp -d)"
answers_file="$(mktemp)"
trap 'rm -rf "$stub_dir" "$answers_file"' EXIT

cat >"$stub_dir/gum" <<'STUB'
#!/usr/bin/env bash
case "$1" in
    input)   printf '%s\n' "$GUM_STUB_INPUT" ;;
    confirm) [[ "$GUM_STUB_CONFIRM" == yes ]] ;;
    style)   shift; printf '%s\n' "${@: -1}" ;;
    *)       exit 0 ;;
esac
STUB
chmod +x "$stub_dir/gum"

# A value containing a space and a quote, to prove %q survives being sourced.
GUM_STUB_INPUT="O'Brien Test" GUM_STUB_CONFIRM=yes \
    PANAMA_ANSWERS="$answers_file" PATH="$stub_dir:$PATH" \
    bash "$interview" >/dev/null 2>&1
interview_status=$?

(( interview_status == 0 )) || note "the interview exited $interview_status on a run that answered everything"

# Sourcing it back must reproduce the value exactly, not a mangled fragment.
(
    # shellcheck source=/dev/null
    source "$answers_file"
    [[ "${PANAMA_GIT_NAME:-}" == "O'Brien Test" ]]
) || note 'an answer containing a quote and a space does not survive being sourced'

# Declining at the confirmation must fail, so install stops.
GUM_STUB_INPUT="x" GUM_STUB_CONFIRM=no \
    PANAMA_ANSWERS="$answers_file" PATH="$stub_dir:$PATH" \
    bash "$interview" >/dev/null 2>&1 \
    && note 'declining the final confirmation still exits zero, so install would proceed'

# Refusing to invent an answers path keeps the file where the caller can delete it.
PATH="$stub_dir:$PATH" bash "$interview" >/dev/null 2>&1 \
    && note 'the interview runs without PANAMA_ANSWERS instead of refusing'

# ── Report ───────────────────────────────────────────────────────────────────

if (( ${#findings[@]} > 0 )); then
    mapfile -t findings < <(printf '%s\n' "${findings[@]}" | sort -u)
    printf 'interview contract: %d finding(s)\n' "${#findings[@]}" >&2
    printf '  - %s\n' "${findings[@]}" >&2
    exit 1
fi

printf 'interview contract: PASS\n'
