#!/usr/bin/env bash
# next/tests/http_server_tcp.sh — Step 9a-tcp live TCP smoke test.
#
# Boots sx_server in the background with a script that loads
# http_server.erl and calls http_server:start/1 on a high port,
# then drives the running server with curl from this shell to
# verify the request → marshaling → route → marshaling → HTTP
# response chain end-to-end.
#
# Boot timing: ~10s for all `lib/erlang/*.sx` loads + module
# compile + spawn + Unix.bind. We hold the server's stdin open
# via `(cat file; sleep 60) | sx_server` so EOF doesn't trigger
# exit(0) before the listener finishes binding.

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

PORT=51820
VERBOSE="${1:-}"
PASS=0; FAIL=0; ERRORS=""

EPOCH_FILE=$(mktemp)
LOG_FILE=$(mktemp)
cleanup() {
  if [ -n "${SXPID:-}" ]; then
    kill -KILL "$SXPID" 2>/dev/null || true
    wait "$SXPID" 2>/dev/null || true
  fi
  if [ -n "${HOLDPID:-}" ]; then
    kill -KILL "$HOLDPID" 2>/dev/null || true
    wait "$HOLDPID" 2>/dev/null || true
  fi
  rm -f "$EPOCH_FILE" "$LOG_FILE"
}
trap cleanup EXIT

cat > "$EPOCH_FILE" <<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)")
(epoch 3)
(eval "(erlang-eval-ast \"http_server:start($PORT)\")")
EPOCHS

# Run sx_server with stdin held open via a long-running background
# `sleep` so EOF doesn't trigger exit(0) before the listener binds
# and the test finishes curling. Use a FIFO so we can capture both
# the holder process's PID and sx_server's PID explicitly — bash
# only captures the rightmost pipe stage with $!.
FIFO=$(mktemp -u)
mkfifo "$FIFO"
( cat "$EPOCH_FILE"; sleep 120 ) > "$FIFO" &
HOLDPID=$!
"$SX_SERVER" < "$FIFO" > "$LOG_FILE" 2>&1 &
SXPID=$!
rm -f "$FIFO"  # both ends still hold open via the running procs

# Wait for the listener to bind (up to ~30s — boot takes ~10s).
BOUND=""
for i in $(seq 1 60); do
  if (exec 3<>/dev/tcp/127.0.0.1/$PORT) 2>/dev/null; then
    exec 3<&-; exec 3>&-
    BOUND="yes"
    break
  fi
  sleep 0.5
done

if [ -z "$BOUND" ]; then
  echo "FAIL: listener never bound on port $PORT"
  if [ "$VERBOSE" = "-v" ]; then
    echo "--- sx_server output ---"
    cat "$LOG_FILE"
    echo "---"
  fi
  exit 1
fi

check_http() {
  local desc="$1" method="$2" path="$3" auth="$4" expected_status="$5" expected_body_substr="$6"
  local args=()
  args+=(-s -o /tmp/http_body.out -w "%{http_code}")
  args+=(-X "$method")
  if [ -n "$auth" ]; then
    args+=(-H "Authorization: $auth")
  fi
  if [ "$method" = "POST" ]; then
    args+=(-d "")
  fi
  args+=("http://127.0.0.1:$PORT$path")
  local code
  code=$(curl "${args[@]}" 2>/dev/null || echo "000")
  local body
  body=$(cat /tmp/http_body.out 2>/dev/null || echo "")
  local pass=1
  if [ "$code" != "$expected_status" ]; then pass=0; fi
  if [ -n "$expected_body_substr" ] && ! echo "$body" | grep -qF -- "$expected_body_substr"; then pass=0; fi
  if [ $pass -eq 1 ]; then
    PASS=$((PASS+1))
    [ "$VERBOSE" = "-v" ] && echo "  ok $desc ($code)"
  else
    FAIL=$((FAIL+1))
    ERRORS+="  FAIL [$desc] code=$code body=$body
"
  fi
}

check_http "GET / -> 200" GET / "" 200 ""
check_http "GET capabilities -> 200" GET /.well-known/sx-capabilities "" 200 "kernel:"
check_http "GET unknown -> 404" GET /no-such-path "" 404 ""
check_http "POST /activity no bearer -> 401" POST /activity "" 401 ""
check_http "POST /activity bad bearer -> 401" POST /activity "Bearer wrong" 401 ""

TOTAL=$((PASS+FAIL))
if [ $FAIL -eq 0 ]; then
  echo "ok $PASS/$TOTAL http_server_tcp tests passed (port $PORT)"
else
  echo "FAIL $PASS/$TOTAL passed, $FAIL failed:"
  echo "$ERRORS"
  if [ "$VERBOSE" = "-v" ]; then
    echo "--- sx_server output (last 30 lines) ---"
    tail -30 "$LOG_FILE"
    echo "---"
  fi
fi
[ $FAIL -eq 0 ]
