#!/usr/bin/env bash

# The machine role: the one interview answer Panama keeps.
#
# What is pinned, and why each half matters:
#
#   * The interview asks the role first and a server is never asked the
#     desktop's questions -- hardware and extras feed stages the server path
#     does not run, and a question nothing consumes is a control that lies.
#   * `--server` presets the answer without a prompt, because the fresh-VPS
#     path runs from a curl with nobody to interview twice.
#   * setup/lib/machine-role reads env over file over default, and records
#     only values it can read back -- an unreadable role file must degrade to
#     desktop, never to an error, because every pre-role machine has none.
#   * install runs different stages per role, and the server list must never
#     silently reacquire a desktop stage (or the reverse).

set -uo pipefail

repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
interview="$repo_dir/setup/scripts/interview"
install_script="$repo_dir/install"
machine_role="$repo_dir/setup/lib/machine-role"
boot="$repo_dir/boot"

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

# ── The helper, sandboxed ────────────────────────────────────────────────────

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

role_of() {
    env -u PANAMA_ROLE XDG_STATE_HOME="$work/state" ${1:+PANAMA_ROLE="$1"} \
        bash -c "source '$machine_role'; panama_role"
}

[[ "$(role_of "")" == desktop ]] \
    || note 'with no file and no env, the role is not desktop'

env XDG_STATE_HOME="$work/state" \
    bash -c "source '$machine_role'; panama_role_record server"
[[ "$(role_of "")" == server ]] \
    || note 'a recorded server role does not read back'

[[ "$(role_of desktop)" == desktop ]] \
    || note 'an exported PANAMA_ROLE does not win over the recorded file'

printf 'gibberish\n' >"$work/state/panama/role"
[[ "$(role_of "")" == desktop ]] \
    || note 'a role file with an unknown value does not degrade to desktop'

env XDG_STATE_HOME="$work/state" \
    bash -c "source '$machine_role'; panama_role_record nonsense"
[[ "$(role_of "")" == desktop ]] \
    || note 'recording an unknown value does not degrade to desktop'

# ── The interview, per role ──────────────────────────────────────────────────
#
# The same stub the interview contract stands up, plus `choose` so the role
# question is answerable. GUM_STUB_CHOOSE also feeds the extras checklist on
# a desktop run, which is why the desktop case leaves it empty.

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

answers="$work/answers"

ask_interview() {
    : >"$answers"
    GUM_STUB_INPUT="x" GUM_STUB_CONFIRM=yes GUM_STUB_CHOOSE="${2:-}" \
        PANAMA_ANSWERS="$answers" PANAMA_ROLE_PRESET="${1:-}" \
        PATH="$stub_dir:$PATH" bash "$interview" >/dev/null 2>&1
}

answered() {
    ( # shellcheck source=/dev/null
      source "$answers"; printf '%s' "${!1:-}" )
}

ask_interview "" server \
    || note 'the interview fails when the role question answers server'
[[ "$(answered PANAMA_ROLE)" == server ]] \
    || note 'choosing server does not record PANAMA_ROLE=server'
[[ "$(answered PANAMA_EXTRAS)" == "" ]] \
    || note 'a server was asked the extras question'
[[ "$(answered PANAMA_NVIDIA)" == no ]] \
    || note 'a server run does not record the hardware defaults'

ask_interview "" "" \
    || note 'the interview fails when the role question is escaped'
[[ "$(answered PANAMA_ROLE)" == desktop ]] \
    || note 'an escaped role question does not default to desktop'

ask_interview server desktop \
    || note 'the interview fails under a --server preset'
[[ "$(answered PANAMA_ROLE)" == server ]] \
    || note 'PANAMA_ROLE_PRESET=server does not win: the prompt was asked anyway'

# ── install runs the right stages ────────────────────────────────────────────
#
# Read from the source rather than run: the stage lists are data, and what
# matters is which names each role's list carries.

# Only the two literal lists: the upgrade filter reassigns STAGES from a
# variable, which is not a role's stage list.
server_stages="$(sed -n 's/^  STAGES=(\(.*\))$/\1/p' "$install_script" | grep '^install-packages' | head -1)"
desktop_stages="$(sed -n 's/^  STAGES=(\(.*\))$/\1/p' "$install_script" | grep '^install-packages' | tail -1)"

for stage in install-packages link-dotfiles link-user setup-server link-server setup-identity; do
    grep -qw "$stage" <<<"$server_stages" \
        || note "the server stage list is missing $stage"
done
for stage in install-hardware change-settings link-skills link-vicinae-scripts; do
    grep -qw "$stage" <<<"$server_stages" \
        && note "the server stage list acquired the desktop stage $stage"
done
for stage in install-packages link-dotfiles link-skills link-user change-settings link-vicinae-scripts setup-identity install-hardware; do
    grep -qw "$stage" <<<"$desktop_stages" \
        || note "the desktop stage list lost $stage"
done

grep -q -- '--server)' "$install_script" \
    || note 'install does not accept --server'
grep -q 'panama_role_record' "$install_script" \
    || note 'install never records the role, so panama update cannot know it'

# ── boot's front door ────────────────────────────────────────────────────────

grep -q -- '--server) SERVER=1' "$boot" \
    || note 'boot does not accept --server'
grep -q 'not root' "$boot" \
    || note 'boot no longer refuses a root run without --server'
grep -q 'runuser' "$boot" \
    || note 'the root bootstrap does not hand off to the created user'

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

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

printf 'role contract: PASS\n'
