#!/usr/bin/env bash
# Fixture test for deckctl bootstrap: ordered per-module provisioning, cached green steps,
# stop-at-failing-module with earlier modules intact, resumable re-run after a fix.
set -euo pipefail
ROOT=$(cd "$(dirname "$0")/../.." && pwd)
FIX=$(mktemp -d "${TMPDIR:-/tmp}/deckctl-bootstrap-test-XXXXXX")
trap 'rm -rf "$FIX"' EXIT
pass=0; fail=0
ok()  { echo "  PASS: $1"; pass=$((pass+1)); }
bad() { echo "  FAIL: $1"; fail=$((fail+1)); }

mkdir -p "$FIX/bin"
cat >"$FIX/bin/systemctl" <<'STUB'
#!/usr/bin/env bash
set -euo pipefail
log="${DECKCTL_TEST_SYSTEMCTL_LOG:?}"
state="${DECKCTL_TEST_SYSTEMCTL_STATE:?}"
args=()
for a in "$@"; do
  [[ "$a" == "--user" ]] && continue
  args+=("$a")
done
verb="${args[0]:-}"
unit="${args[1]:-}"
printf '%s %s\n' "$verb" "$unit" >>"$log"
mkdir -p "$state"
case "$verb" in
  daemon-reload) ;;
  is-enabled) [[ -f "$state/$unit" ]] || exit 1 ;;
  enable) : >"$state/$unit" ;;
esac
exit 0
STUB
chmod +x "$FIX/bin/systemctl"
export PATH="$FIX/bin:$PATH"

write_beta_bootstrap() { # $1=sandbox $2=exit code
  cat >"$1/modules/beta/bootstrap.sh" <<EOF
#!/usr/bin/env bash
printf 'run\n' >>"\${BETA_COUNTER:?}"
exit $2
EOF
  chmod +x "$1/modules/beta/bootstrap.sh"
}

mk_sandbox() { # $1=dest
  local dest="$1"
  mkdir -p "$dest/spec" "$dest/bin" "$dest/lib/deckctl" "$dest/modules/alpha/systemd" "$dest/modules/beta"
  cp "$ROOT/spec/deck-module.schema.json" "$dest/spec/"
  cp "$ROOT/bin/deckctl" "$dest/bin/"
  # bootstrap dispatches to whatever subcommands it needs, so the sandbox carries every lib
  # file. An enumerated list silently omits each new subcommand as it lands.
  cp "$ROOT"/lib/deckctl/*.sh "$dest/lib/deckctl/"

  cat >"$dest/modules/alpha/deck.module.json" <<'EOF'
{
  "schema_version": 1,
  "name": "alpha",
  "kind": "service",
  "services": ["systemd/alpha.service"],
  "bootstrap": "./bootstrap.sh",
  "health": "./health.sh",
  "install": { "mode": "in-place", "needs_git": false }
}
EOF
  cat >"$dest/modules/alpha/bootstrap.sh" <<'EOF'
#!/usr/bin/env bash
printf 'run\n' >>"${ALPHA_COUNTER:?}"
EOF
  printf '#!/usr/bin/env bash\nexit 0\n' >"$dest/modules/alpha/health.sh"
  printf '[Unit]\nDescription=alpha\n[Service]\nExecStart=/bin/true\n' >"$dest/modules/alpha/systemd/alpha.service"
  chmod +x "$dest/modules/alpha/bootstrap.sh" "$dest/modules/alpha/health.sh"

  cat >"$dest/modules/beta/deck.module.json" <<'EOF'
{
  "schema_version": 1,
  "name": "beta",
  "kind": "cli",
  "bootstrap": "./bootstrap.sh",
  "health": "./health.sh",
  "install": { "mode": "in-place", "needs_git": false }
}
EOF
  printf '#!/usr/bin/env bash\nexit 0\n' >"$dest/modules/beta/health.sh"
  chmod +x "$dest/modules/beta/health.sh"
  write_beta_bootstrap "$dest" 0
}

run_deckctl() { # $1=sandbox $2+=args
  local sb="$1"; shift
  HOME="$TEST_HOME" XDG_CONFIG_HOME="$TEST_HOME/.config" "$sb/bin/deckctl" "$@"
}

count_lines() { # $1=file
  [[ -f "$1" ]] || { printf '0'; return 0; }
  wc -l <"$1" | tr -d ' '
}

row_for() { # $1=output $2=module
  printf '%s\n' "$1" | grep -E "^$2[[:space:]]" || true
}

echo "=== prereq: bootstrap.sh exists ==="
if [[ -f "$ROOT/lib/deckctl/bootstrap.sh" ]]; then ok "bootstrap.sh exists"; else bad "missing lib/deckctl/bootstrap.sh"; fi

echo "=== bash -n clean ==="
if bash -n "$ROOT/lib/deckctl/bootstrap.sh" 2>/dev/null; then ok "bash -n bootstrap.sh"; else bad "bash -n bootstrap.sh"; fi

echo "=== cold bootstrap: all green ==="
SB1="$FIX/sb-green"
TEST_HOME="$FIX/home-green"
mkdir -p "$TEST_HOME/.local/state/overdeck" "$TEST_HOME/.local/opt/overdeck"
export DECKCTL_TEST_SYSTEMCTL_LOG="$FIX/systemctl-green.log"
export DECKCTL_TEST_SYSTEMCTL_STATE="$FIX/systemctl-green-state"
: >"$DECKCTL_TEST_SYSTEMCTL_LOG"
export ALPHA_COUNTER="$FIX/alpha-green.count"
export BETA_COUNTER="$FIX/beta-green.count"
mk_sandbox "$SB1"
set +e
out=$(run_deckctl "$SB1" bootstrap 2>&1)
rc=$?
set -e
if [[ $rc -eq 0 ]]; then ok "cold bootstrap exits 0"; else bad "cold bootstrap rc=$rc: $out"; fi
if printf '%s\n' "$out" | grep -q '^MODULE .*HEALTH'; then ok "summary table printed"; else bad "no summary table: $out"; fi
if [[ "$(row_for "$out" alpha)" == alpha*ok* ]]; then ok "alpha row green"; else bad "alpha row: $(row_for "$out" alpha)"; fi
if [[ "$(row_for "$out" beta)" == beta*ok* ]]; then ok "beta row green"; else bad "beta row: $(row_for "$out" beta)"; fi
if printf '%s\n' "$out" | grep -q 'FAIL'; then bad "cold bootstrap reported FAIL: $out"; else ok "no FAIL in summary"; fi
link="$TEST_HOME/.config/systemd/user/alpha.service"
if [[ -L "$link" && "$(readlink "$link")" == "$SB1/modules/alpha/systemd/alpha.service" ]]; then
  ok "alpha.service linked into sandbox unit dir"
else
  bad "alpha unit link wrong: $(readlink "$link" 2>&1)"
fi
if grep -qx 'enable alpha.service' "$DECKCTL_TEST_SYSTEMCTL_LOG"; then ok "alpha.service enabled"; else bad "enable not called: $(cat "$DECKCTL_TEST_SYSTEMCTL_LOG")"; fi
if [[ "$(count_lines "$ALPHA_COUNTER")" == 1 && "$(count_lines "$BETA_COUNTER")" == 1 ]]; then
  ok "each module bootstrap ran exactly once"
else
  bad "counters alpha=$(count_lines "$ALPHA_COUNTER") beta=$(count_lines "$BETA_COUNTER")"
fi

echo "=== re-run does not repeat green steps ==="
set +e
out=$(run_deckctl "$SB1" bootstrap 2>&1)
rc=$?
set -e
if [[ $rc -eq 0 ]]; then ok "re-run exits 0"; else bad "re-run rc=$rc: $out"; fi
if [[ "$(count_lines "$ALPHA_COUNTER")" == 1 && "$(count_lines "$BETA_COUNTER")" == 1 ]]; then
  ok "bootstrap scripts not re-run"
else
  bad "re-run repeated scripts: alpha=$(count_lines "$ALPHA_COUNTER") beta=$(count_lines "$BETA_COUNTER")"
fi
if [[ "$(row_for "$out" alpha)" == alpha*cached* ]]; then ok "alpha bootstrap reported cached"; else bad "alpha row: $(row_for "$out" alpha)"; fi
enable_count=$(grep -cx 'enable alpha.service' "$DECKCTL_TEST_SYSTEMCTL_LOG" || true)
if [[ "$enable_count" -eq 1 ]]; then ok "unit enabled once across two runs"; else bad "enable called $enable_count times"; fi

echo "=== --module scopes to one module ==="
set +e
out=$(run_deckctl "$SB1" bootstrap --module beta 2>&1)
rc=$?
set -e
if [[ $rc -eq 0 ]]; then ok "--module beta exits 0"; else bad "--module beta rc=$rc: $out"; fi
if [[ -n "$(row_for "$out" beta)" && -z "$(row_for "$out" alpha)" ]]; then
  ok "--module beta reports beta only"
else
  bad "--module scoping wrong: $out"
fi
set +e
out=$(run_deckctl "$SB1" bootstrap --module nosuch 2>&1)
rc=$?
set -e
if [[ $rc -ne 0 ]] && printf '%s\n' "$out" | grep -q 'nosuch'; then ok "unknown --module rejected"; else bad "unknown --module rc=$rc: $out"; fi

echo "=== failing module 2: stops, module 1 intact, exit 1 ==="
SB2="$FIX/sb-fail"
TEST_HOME="$FIX/home-fail"
mkdir -p "$TEST_HOME/.local/state/overdeck" "$TEST_HOME/.local/opt/overdeck"
export DECKCTL_TEST_SYSTEMCTL_LOG="$FIX/systemctl-fail.log"
export DECKCTL_TEST_SYSTEMCTL_STATE="$FIX/systemctl-fail-state"
: >"$DECKCTL_TEST_SYSTEMCTL_LOG"
export ALPHA_COUNTER="$FIX/alpha-fail.count"
export BETA_COUNTER="$FIX/beta-fail.count"
mk_sandbox "$SB2"
write_beta_bootstrap "$SB2" 1
set +e
out=$(run_deckctl "$SB2" bootstrap 2>&1)
rc=$?
set -e
if [[ $rc -eq 1 ]]; then ok "failing module exits 1"; else bad "failing module rc=$rc: $out"; fi
if printf '%s\n' "$out" | grep -q 'beta'; then ok "failure names the module"; else bad "failure does not name beta: $out"; fi
if [[ "$(row_for "$out" beta)" == *FAIL* ]]; then ok "beta row marked FAIL"; else bad "beta row: $(row_for "$out" beta)"; fi
if [[ "$(row_for "$out" alpha)" == alpha*ok* ]]; then ok "alpha row still green"; else bad "alpha row: $(row_for "$out" alpha)"; fi
if [[ -f "$TEST_HOME/.local/state/overdeck/bootstrap/alpha/bootstrap.sha256" ]]; then
  ok "alpha bootstrap state recorded"
else
  bad "alpha bootstrap state missing"
fi
if [[ -L "$TEST_HOME/.config/systemd/user/alpha.service" ]]; then ok "alpha unit still linked"; else bad "alpha unit link lost"; fi
if [[ "$(count_lines "$BETA_COUNTER")" == 1 ]]; then ok "beta bootstrap attempted once"; else bad "beta count=$(count_lines "$BETA_COUNTER")"; fi

echo "=== re-run after fix completes ==="
write_beta_bootstrap "$SB2" 0
set +e
out=$(run_deckctl "$SB2" bootstrap 2>&1)
rc=$?
set -e
if [[ $rc -eq 0 ]]; then ok "re-run after fix exits 0"; else bad "re-run after fix rc=$rc: $out"; fi
if [[ "$(count_lines "$ALPHA_COUNTER")" == 1 ]]; then ok "alpha not re-bootstrapped"; else bad "alpha count=$(count_lines "$ALPHA_COUNTER")"; fi
if [[ "$(count_lines "$BETA_COUNTER")" == 2 ]]; then ok "beta re-bootstrapped after fix"; else bad "beta count=$(count_lines "$BETA_COUNTER")"; fi
if printf '%s\n' "$out" | grep -q 'FAIL'; then bad "re-run still reports FAIL: $out"; else ok "re-run summary clean"; fi

echo "=== failure after a green step keeps that step green ==="
printf '#!/usr/bin/env bash\nexit 1\n' >"$SB2/modules/beta/health.sh"
chmod +x "$SB2/modules/beta/health.sh"
set +e
out=$(run_deckctl "$SB2" bootstrap 2>&1)
rc=$?
set -e
if [[ $rc -eq 1 ]]; then ok "late-step failure exits 1"; else bad "late-step failure rc=$rc: $out"; fi
if [[ "$(row_for "$out" beta)" == beta*cached*FAIL* ]]; then
  ok "beta bootstrap stays cached while health FAILs"
else
  bad "beta row: $(row_for "$out" beta)"
fi
if [[ "$(count_lines "$BETA_COUNTER")" == 2 ]]; then ok "green bootstrap step not re-run on late failure"; else bad "beta count=$(count_lines "$BETA_COUNTER")"; fi

printf '#!/usr/bin/env bash\nexit 0\n' >"$SB2/modules/beta/health.sh"
chmod +x "$SB2/modules/beta/health.sh"
set +e
out=$(run_deckctl "$SB2" bootstrap 2>&1)
rc=$?
set -e
if [[ $rc -eq 0 ]]; then ok "re-run after late fix exits 0"; else bad "re-run after late fix rc=$rc: $out"; fi
if [[ "$(count_lines "$BETA_COUNTER")" == 2 && "$(count_lines "$ALPHA_COUNTER")" == 1 ]]; then
  ok "no bootstrap script re-run after late fix"
else
  bad "counters alpha=$(count_lines "$ALPHA_COUNTER") beta=$(count_lines "$BETA_COUNTER")"
fi

echo "=== doctor gate blocks bootstrap ==="
SB3="$FIX/sb-doctor"
TEST_HOME="$FIX/home-doctor"
mkdir -p "$TEST_HOME/.local/state/overdeck" "$TEST_HOME/.local/opt/overdeck"
export DECKCTL_TEST_SYSTEMCTL_LOG="$FIX/systemctl-doctor.log"
export DECKCTL_TEST_SYSTEMCTL_STATE="$FIX/systemctl-doctor-state"
: >"$DECKCTL_TEST_SYSTEMCTL_LOG"
export ALPHA_COUNTER="$FIX/alpha-doctor.count"
export BETA_COUNTER="$FIX/beta-doctor.count"
mk_sandbox "$SB3"
rm -f "$SB3/modules/beta/health.sh"
set +e
out=$(run_deckctl "$SB3" bootstrap 2>&1)
rc=$?
set -e
if [[ $rc -eq 1 ]]; then ok "doctor failure exits 1"; else bad "doctor gate rc=$rc: $out"; fi
if [[ "$(count_lines "$ALPHA_COUNTER")" == 0 ]]; then ok "no module ran after doctor failure"; else bad "module ran despite doctor failure"; fi

echo "----"
echo "PASS=$pass FAIL=$fail"
[[ $fail -eq 0 ]]
