#!/usr/bin/env bash
# Cherry-picked test262 conformance runner for JS-on-SX.
# Walks lib/js/test262-slice/**/*.js, evaluates each via js-eval,
# and compares against the sibling .expected file (substring match).
#
# Usage:
#   bash lib/js/conformance.sh          # summary only
#   bash lib/js/conformance.sh -v       # per-test pass/fail

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

SX_SERVER="hosts/ocaml/_build/default/bin/sx_server.exe"
if [ ! -x "$SX_SERVER" ]; then
  echo "ERROR: $SX_SERVER not found. Run: cd hosts/ocaml && dune build"
  exit 1
fi

VERBOSE="${1:-}"
SLICE_DIR="lib/js/test262-slice"
PASS=0
FAIL=0
ERRORS=""

# Find all .js fixtures (sorted for stable output).
# Skip README.md and similar.
mapfile -t FIXTURES < <(find "$SLICE_DIR" -type f -name '*.js' | sort)

if [ ${#FIXTURES[@]} -eq 0 ]; then
  echo "No fixtures found in $SLICE_DIR"
  exit 1
fi

# Build one big batch script: load everything once, then one epoch per
# fixture. Avoids the ~200ms boot cost of starting the server for each
# test.

TMPFILE=$(mktemp)
trap "rm -f $TMPFILE" EXIT

{
  echo '(epoch 1)'
  echo '(load "lib/r7rs.sx")'
  echo '(epoch 2)'
  echo '(load "lib/js/lexer.sx")'
  echo '(epoch 3)'
  echo '(load "lib/js/parser.sx")'
  echo '(epoch 4)'
  echo '(load "lib/js/transpile.sx")'
  echo '(epoch 5)'
  echo '(load "lib/js/runtime.sx")'
  echo '(epoch 6)'
  echo '(load "lib/js/regex.sx")'

  epoch=100
  for f in "${FIXTURES[@]}"; do
    # Read source, strip trailing newline, then escape for *two*
    # nested SX string literals: the outer epoch `(eval "…")` and
    # the inner `(js-eval "…")` that it wraps.
    #
    # Source char → final stream char
    #   \  →  \\\\    (outer: becomes \\ ; inner: becomes \)
    #   "  →  \\\"    (outer: becomes \" ; inner: becomes ")
    #   nl →  \\n     (SX newline escape, survives both levels)
    src=$(python3 -c '
import sys
s = open(sys.argv[1], "r", encoding="utf-8").read().rstrip("\n")
# Two nested SX string literals: outer eval wraps inner js-eval.
# Escape once for inner (JS source → SX inner string literal):
inner = s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n")
# Escape the result again for the outer SX string literal:
outer = inner.replace("\\", "\\\\").replace("\"", "\\\"")
sys.stdout.write(outer)
' "$f")
    echo "(epoch $epoch)"
    echo "(eval \"(js-eval \\\"$src\\\")\")"
    epoch=$((epoch + 1))
  done
} > "$TMPFILE"

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

# Iterate fixtures with the same epoch sequence and check each.
epoch=100
for f in "${FIXTURES[@]}"; do
  expected=$(cat "${f%.js}.expected" | sed -e 's/[[:space:]]*$//' | head -n 1)
  name="${f#${SLICE_DIR}/}"
  name="${name%.js}"

  # Actual output lives on the line after "(ok-len $epoch N)" or on
  # "(ok $epoch VAL)" for short values. Errors surface as "(error …)".
  actual=$(echo "$OUTPUT" | awk -v e="$epoch" '
    $0 ~ ("^\\(ok-len "e" ") { getline; print; exit }
    $0 ~ ("^\\(ok "e" ") { sub("^\\(ok "e" ", ""); sub(")$", ""); print; exit }
    $0 ~ ("^\\(error "e" ") { print; exit }
  ')
  [ -z "$actual" ] && actual="<no output>"

  if echo "$actual" | grep -qF -- "$expected"; then
    PASS=$((PASS + 1))
    [ "$VERBOSE" = "-v" ] && echo "  ✓ $name"
  else
    FAIL=$((FAIL + 1))
    ERRORS+="  ✗ $name
    expected: $expected
    actual:   $actual
"
    [ "$VERBOSE" = "-v" ] && echo "  ✗ $name (expected: $expected, actual: $actual)"
  fi

  epoch=$((epoch + 1))
done

TOTAL=$((PASS + FAIL))
PCT=$(awk "BEGIN{printf \"%.1f\", ($PASS/$TOTAL)*100}")

echo
if [ $FAIL -eq 0 ]; then
  echo "✓ $PASS/$TOTAL test262-slice tests passed ($PCT%)"
else
  echo "✗ $PASS/$TOTAL passed, $FAIL failed ($PCT%):"
  [ "$VERBOSE" != "-v" ] && echo "$ERRORS"
fi

# Phase 5 target: ≥50% pass.
TARGET=50
if (( $(echo "$PCT >= $TARGET" | bc -l 2>/dev/null || python3 -c "print($PCT >= $TARGET)") )); then
  exit 0
else
  echo "(below target of ${TARGET}%)"
  exit 1
fi
