#!/usr/bin/env bash
# next/tests/http_server_start.sh — Step 8b-start structural test.
#
# `http_server:start/1,2` spawn an Erlang process that blocks in
# `http:listen/2` forever. In this port's cooperative scheduler,
# any in-process `erlang-eval-ast` that triggers that spawn hangs
# the runtime — `er-sched-run-all!` waits for every spawned
# process to leave the runnable queue before returning to the
# caller, and the listener never does. So this test verifies the
# code SHAPE without actually invoking start/1:
#   * Module loads.
#   * `start/1` and `start/2` are bound in the module env.
#   * The dict↔proplist marshaling bridge (the BIF-wrapper hook)
#     is bound in the runtime env.
# The live TCP behaviour lands in `next/tests/http_server_tcp.sh`
# (Step 9a-tcp) via a shell-side curl probe.

set -uo pipefail
cd "$(git rev-parse --show-toplevel)"

SX_SERVER="${SX_SERVER:-hosts/ocaml/_build/default/bin/sx_server.exe}"
if [ ! -x "$SX_SERVER" ]; then
  SX_SERVER="/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe"
fi
if [ ! -x "$SX_SERVER" ]; then
  echo "ERROR: sx_server.exe not found." >&2
  exit 1
fi

VERBOSE="${1:-}"
PASS=0; FAIL=0; ERRORS=""
TMPFILE=$(mktemp); trap "rm -f $TMPFILE" EXIT

cat > "$TMPFILE" <<'EPOCHS'
(epoch 1)
(load "lib/erlang/tokenizer.sx")
(load "lib/erlang/parser.sx")
(load "lib/erlang/parser-core.sx")
(load "lib/erlang/parser-expr.sx")
(load "lib/erlang/parser-module.sx")
(load "lib/erlang/transpile.sx")
(load "lib/erlang/runtime.sx")
(load "lib/erlang/vm/dispatcher.sx")

(epoch 2)
(eval "(get (erlang-load-module (file-read \"next/kernel/http_server.erl\")) :name)")

;; --- module is registered ---
(epoch 10)
(eval "(let ((m (get (er-modules-get) \"http_server\"))) (cond (= m nil) 'absent :else 'present))")

;; --- start/1 + start/2 are bound (multi-arity stored as a single binding) ---
(epoch 11)
(eval "(let ((env (get (get (er-modules-get) \"http_server\") \"current\"))) (cond (= (get env \"start\") nil) 'missing :else 'present))")

;; --- request->proplist marshaler exists in runtime env ---
(epoch 12)
(eval "(if (= (type-of er-request-dict-to-proplist) \"lambda\") 'present 'missing)")

;; --- proplist->dict marshaler exists in runtime env ---
(epoch 13)
(eval "(if (= (type-of er-proplist-to-dict) \"lambda\") 'present 'missing)")

;; --- http:listen BIF wrapper now routes through the marshalers ---
;; Probe by registration only (calling listen would block forever).
(epoch 14)
(eval "(not (= (er-lookup-bif \"http\" \"listen\" 2) nil))")
EPOCHS

OUTPUT=$(timeout 30 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)

check() {
  local epoch="$1" desc="$2" expected="$3"
  local actual
  actual=$(echo "$OUTPUT" | awk -v e="$epoch" '
    $0 ~ "^\\(ok-len " e " " { getline; print; exit }
    $0 ~ "^\\(ok " e " "     { print; exit }
    $0 ~ "^\\(error " e " "  { print; exit }
  ')
  [ -z "$actual" ] && actual="<no output for epoch $epoch>"
  if echo "$actual" | grep -qF -- "$expected"; then
    PASS=$((PASS+1))
    [ "$VERBOSE" = "-v" ] && echo "  ok $desc"
  else
    FAIL=$((FAIL+1))
    ERRORS+="  FAIL [$desc] (epoch $epoch) expected: $expected | actual: $actual
"
  fi
}

check 2  "http_server module loaded"        "http_server"
check 10 "module registered"                "present"
check 11 "start bound in module env"        "present"
check 12 "request marshaler defined"        "present"
check 13 "response marshaler defined"       "present"
check 14 "http:listen BIF registered"       "true"

TOTAL=$((PASS+FAIL))
if [ $FAIL -eq 0 ]; then
  echo "ok $PASS/$TOTAL http_server_start tests passed"
else
  echo "FAIL $PASS/$TOTAL passed, $FAIL failed:"
  echo "$ERRORS"
fi
[ $FAIL -eq 0 ]
