#!/usr/bin/env bash
# ===========================================================================
# run-tests.sh — Run ALL test suites. Exit non-zero if any fail.
#
# Usage:
#   ./run-tests.sh           # run everything
#   ./run-tests.sh --quick   # skip Playwright (fast CI check)
#   ./run-tests.sh --sx-only # SX language tests only (JS + Python + OCaml)
# ===========================================================================
set -euo pipefail

cd "$(dirname "$0")"

QUICK="${QUICK:-false}"
SX_ONLY="${SX_ONLY:-false}"
for arg in "$@"; do
  case "$arg" in
    --quick)   QUICK=true ;;
    --sx-only) SX_ONLY=true ;;
  esac
done

FAILURES=()
PASSES=()

run_suite() {
  local name="$1"
  shift
  echo ""
  echo "============================================================"
  echo "  $name"
  echo "============================================================"
  if "$@"; then
    PASSES+=("$name")
  else
    FAILURES+=("$name")
  fi
}

# -------------------------------------------------------------------
# 1. Build SX bundles
# -------------------------------------------------------------------
echo "=== Building SX bundles ==="
python3 hosts/javascript/cli.py --output shared/static/scripts/sx-browser.js
python3 hosts/javascript/cli.py --extensions continuations --spec-modules types \
  --output shared/static/scripts/sx-full-test.js

# -------------------------------------------------------------------
# 2. JavaScript SX tests (standard + full)
# -------------------------------------------------------------------
run_suite "JS standard (spec tests)" \
  node hosts/javascript/run_tests.js

run_suite "JS full (spec + continuations + types + VM)" \
  node hosts/javascript/run_tests.js --full

# -------------------------------------------------------------------
# 3. OCaml SX tests
# -------------------------------------------------------------------
if [ -x hosts/ocaml/_build/default/bin/run_tests.exe ]; then
  run_suite "OCaml (spec tests)" \
    hosts/ocaml/_build/default/bin/run_tests.exe
else
  echo ""
  echo "[SKIP] OCaml tests — binary not built (run: cd hosts/ocaml && dune build)"
fi

# -------------------------------------------------------------------
# 4. OCaml bridge integration (custom special forms, web-forms.sx)
# -------------------------------------------------------------------
run_suite "OCaml bridge — custom special forms + web-forms" \
  python3 -c "
from shared.sx.ocaml_sync import OcamlSync
bridge = OcamlSync()
# Load exactly what the server does (no evaluator.sx!)
for f in ['spec/parser.sx', 'spec/render.sx', 'web/adapter-html.sx', 'web/adapter-sx.sx', 'web/web-forms.sx', 'lib/freeze.sx']:
    bridge.load(f)
ok = 0; fail = 0
def check(name, expr, expected=None):
    global ok, fail
    try:
        r = bridge.eval(expr)
        if expected is not None and r != expected:
            print(f'  FAIL: {name}: expected {expected!r}, got {r!r}'); fail += 1
        else:
            print(f'  PASS: {name}'); ok += 1
    except Exception as e:
        print(f'  FAIL: {name}: {e}'); fail += 1

# Custom special forms registered by web-forms.sx
for form in ['defhandler', 'defquery', 'defaction', 'defpage', 'defrelation', 'defstyle', 'deftype', 'defeffect']:
    check(f'{form} registered', f'(has-key? *custom-special-forms* \"{form}\")', 'true')

# Custom forms callable via eval (not just via load)
check('deftype via eval', '(deftype test-t number)', 'nil')
check('defeffect via eval', '(defeffect test-e)', 'nil')
check('defstyle via eval', '(defstyle my-s \"bold\")', 'bold')
check('defhandler via eval', '(has-key? (defhandler test-h (&key x) x) \"__type\")', 'true')

# Extension lists populated
check('definition-form-extensions populated', '(> (len *definition-form-extensions*) 0)', 'true')
check('RENDER_HTML_FORMS has defstyle', '(contains? RENDER_HTML_FORMS \"defstyle\")', 'true')

# Env-shadowing regression: custom forms survive evaluator.sx load
bridge2 = OcamlSync()
bridge2.eval('(register-special-form! \"shadow-test\" (fn (args env) 42))')
bridge2.load('spec/evaluator.sx')
check('custom form survives evaluator.sx load',
      bridge2.eval('(has-key? *custom-special-forms* \"shadow-test\")'), 'true')
bridge2.eval('(register-special-form! \"post-load\" (fn (args env) 99))')
check('custom form callable after evaluator.sx load',
      bridge2.eval('(post-load 1)'), '99')

print(f'\\nResults: {ok} passed, {fail} failed')
import sys; sys.exit(1 if fail > 0 else 0)
"

# -------------------------------------------------------------------
# 5. Python SX tests (post-removal regression, components, parser)
# -------------------------------------------------------------------
run_suite "Python — post-removal regression" \
  python3 -m pytest shared/sx/tests/test_post_removal_bugs.py -v --tb=short

run_suite "Python — component rendering" \
  python3 -m pytest shared/sx/tests/test_components.py -v --tb=short

run_suite "Python — parser" \
  python3 -m pytest shared/sx/tests/test_parser.py -v --tb=short

# -------------------------------------------------------------------
# 5. Playwright tests (browser)
# -------------------------------------------------------------------
if [ "$QUICK" = false ] && [ "$SX_ONLY" = false ]; then
  run_suite "Playwright — isomorphic SSR" \
    npx playwright test --reporter=list

  run_suite "Playwright — SX demos (98 tests)" \
    python3 -m pytest sx/tests/test_demos.py -v --tb=short
fi

# -------------------------------------------------------------------
# Summary
# -------------------------------------------------------------------
echo ""
echo "============================================================"
echo "  TEST SUMMARY"
echo "============================================================"
for p in "${PASSES[@]}"; do
  echo "  PASS: $p"
done
for f in "${FAILURES[@]}"; do
  echo "  FAIL: $f"
done
echo "============================================================"

if [ ${#FAILURES[@]} -gt 0 ]; then
  echo ""
  echo "  ${#FAILURES[@]} suite(s) FAILED — deploy blocked."
  echo ""
  exit 1
else
  echo ""
  echo "  All ${#PASSES[@]} suites passed."
  echo ""
  exit 0
fi
