#!/usr/bin/env bash
# test_transpiler_clamping.sh — Tests transpiler validation of output register values.
# Verifies that:
#   - negative literal outputs for ci, anchorShare, anchorWidth, discoveryDepth produce
#     a transpiler-level error with a clear message (not a silent Solidity compile failure).
#   - an anchorWidth literal outside [0, 16777215] produces a transpiler-level error.
#   - a valid anchorWidth literal within range is accepted and still uses % (2**24).
# Exit: 0 if all tests pass, 1 if any fail.
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"

PASS=0
FAIL=0

assert_contains() {
  local name="$1"
  local output="$2"
  local expected="$3"
  if echo "$output" | grep -qF "$expected"; then
    echo "  PASS: $name"
    PASS=$((PASS + 1))
  else
    echo "  FAIL: $name"
    echo "    expected to contain: $expected"
    echo "    actual output:"
    echo "$output" | sed 's/^/      /'
    FAIL=$((FAIL + 1))
  fi
}

assert_not_contains() {
  local name="$1"
  local output="$2"
  local pattern="$3"
  if echo "$output" | grep -qF "$pattern"; then
    echo "  FAIL: $name"
    echo "    expected NOT to contain: $pattern"
    echo "    actual output:"
    echo "$output" | sed 's/^/      /'
    FAIL=$((FAIL + 1))
  else
    echo "  PASS: $name"
    PASS=$((PASS + 1))
  fi
}

assert_exits_nonzero() {
  local name="$1"
  local stderr_output="$2"
  local expected_fragment="$3"
  # stderr_output is non-empty only if the command failed (captured separately)
  if echo "$stderr_output" | grep -qF "$expected_fragment"; then
    echo "  PASS: $name"
    PASS=$((PASS + 1))
  else
    echo "  FAIL: $name"
    echo "    expected stderr to contain: $expected_fragment"
    echo "    actual stderr:"
    echo "$stderr_output" | sed 's/^/      /'
    FAIL=$((FAIL + 1))
  fi
}

# ── Ensure node_modules exist ────────────────────────────────────────────────
if [ ! -d "$SCRIPT_DIR/node_modules" ]; then
  (cd "$SCRIPT_DIR" && npm install --silent)
fi

TMPDIR_T=$(mktemp -d)
trap 'rm -rf "$TMPDIR_T"' EXIT

run_transpiler() {
  local push3_content="$1"
  local input_file="$TMPDIR_T/test_input.push3"
  local output_file="$TMPDIR_T/test_output.sol"
  printf '%s\n' "$push3_content" > "$input_file"
  rm -f "$output_file"
  local stderr_out
  # Capture stderr; allow non-zero exit without aborting
  if stderr_out=$(cd "$SCRIPT_DIR" && npx tsx src/index.ts "$input_file" "$output_file" 2>&1 >/dev/null); then
    echo "OK"
  else
    printf '%s' "$stderr_out"
  fi
}

# ── Test 6: anchorWidth literal > uint24 max → transpiler error ──────────────
echo "Test 6: anchorWidth = 1e18 (literal) → transpiler error, not silent Solidity failure"

# Push3 program: push 4 values (discoveryDepth, anchorWidth=1e18, anchorShare, ci).
# The transpiler pops top-4 from DYADIC stack; these literals sit on top of the
# primed input slots, so they become the 4 output vars.
STDERR=$(run_transpiler "(
  300000000000000000
  1000000000000000000
  300000000000000000
  0
)")

assert_contains \
  "anchorWidth 1e18: transpiler emits range error message" \
  "$STDERR" \
  "anchorWidth literal (1000000000000000000) is outside uint24 range [0, 16777215]"

# ── Test 7: valid anchorWidth literal (in range) → accepted, wraps with % (2**24) ─
echo "Test 7: anchorWidth = 100 (valid literal) → accepted, output uses % (2**24)"

INPUT_6="$TMPDIR_T/valid_aw.push3"
OUTPUT_6="$TMPDIR_T/valid_aw.sol"
cat > "$INPUT_6" <<'PUSH3EOF'
(
  300000000000000000
  100
  300000000000000000
  0
)
PUSH3EOF

(cd "$SCRIPT_DIR" && npx tsx src/index.ts "$INPUT_6" "$OUTPUT_6" 2>/dev/null)

SOL_6=$(cat "$OUTPUT_6")
assert_contains \
  "anchorWidth 100: output uses % (2**24)" \
  "$SOL_6" \
  "uint24(100 % (2**24))"

# ── Test 8: negative ci literal → transpiler error ───────────────────────────
echo "Test 8: ci = -1 (negative literal) → transpiler error"

STDERR=$(run_transpiler "(
  300000000000000000
  100
  300000000000000000
  -1
)")

assert_contains \
  "negative ci: transpiler emits error message" \
  "$STDERR" \
  "ci is a negative literal (-1)"

# ── Test 9: negative anchorShare literal → transpiler error ──────────────────
echo "Test 9: anchorShare = -5 (negative literal) → transpiler error"

STDERR=$(run_transpiler "(
  300000000000000000
  100
  -5
  0
)")

assert_contains \
  "negative anchorShare: transpiler emits error message" \
  "$STDERR" \
  "anchorShare is a negative literal (-5)"

# ── Test 10: negative discoveryDepth literal → transpiler error ───────────────
echo "Test 10: discoveryDepth = -99 (negative literal) → transpiler error"

STDERR=$(run_transpiler "(
  -99
  100
  300000000000000000
  0
)")

assert_contains \
  "negative discoveryDepth: transpiler emits error message" \
  "$STDERR" \
  "discoveryDepth is a negative literal (-99)"

# ── Test 11: negative anchorWidth literal → transpiler error ─────────────────
echo "Test 11: anchorWidth = -1 (negative literal) → transpiler error"

STDERR=$(run_transpiler "(
  300000000000000000
  -1
  300000000000000000
  0
)")

assert_contains \
  "negative anchorWidth: transpiler emits range error message" \
  "$STDERR" \
  "anchorWidth literal (-1) is outside uint24 range [0, 16777215]"

# ── Test 12: forge build — transpiler output compiles (regression for #900) ──
echo "Test 12: forge build on valid transpiler output (regression for #900)"

REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
FORGE_BIN="$(command -v forge 2>/dev/null || true)"
if [ -z "$FORGE_BIN" ] && [ -x /root/.foundry/bin/forge ]; then
  FORGE_BIN=/root/.foundry/bin/forge
fi

if [ -z "$FORGE_BIN" ]; then
  echo "  SKIP: forge not found — add foundry bin to PATH to enable compile verification"
else
  FORGE_DIR="$TMPDIR_T/forge_verify"
  mkdir -p "$FORGE_DIR/src"
  printf '[profile.default]\nsrc = "src"\nout = "out"\nlibs = []\nvia_ir = true\n' > "$FORGE_DIR/foundry.toml"
  cp "$REPO_ROOT/onchain/src/IOptimizer.sol" "$FORGE_DIR/src/"
  cp "$OUTPUT_6" "$FORGE_DIR/src/OptimizerV3Push3.sol"

  FORGE_OUT=""
  FORGE_RC=0
  FORGE_OUT=$(cd "$FORGE_DIR" && "$FORGE_BIN" build 2>&1) || FORGE_RC=$?

  if [ "$FORGE_RC" -eq 0 ]; then
    echo "  PASS: forge build succeeded on transpiled contract"
    PASS=$((PASS + 1))
  else
    echo "  FAIL: forge build failed — transpiled contract does not compile"
    echo "$FORGE_OUT" | sed 's/^/      /'
    FAIL=$((FAIL + 1))
  fi
fi

# ── Test 13: ci literal > 1e18 → transpiler error ────────────────────────────
echo "Test 13: ci = 2e18 (literal) → transpiler error"

STDERR=$(run_transpiler "(
  300000000000000000
  100
  300000000000000000
  2000000000000000000
)")

assert_contains \
  "ci 2e18: transpiler emits max1e18 error message" \
  "$STDERR" \
  "ci literal (2000000000000000000) exceeds 1e18 (max allowed)"

# ── Test 14: anchorShare literal > 1e18 → transpiler error ───────────────────
echo "Test 14: anchorShare = 2e18 (literal) → transpiler error"

STDERR=$(run_transpiler "(
  300000000000000000
  100
  2000000000000000000
  0
)")

assert_contains \
  "anchorShare 2e18: transpiler emits max1e18 error message" \
  "$STDERR" \
  "anchorShare literal (2000000000000000000) exceeds 1e18 (max allowed)"

# ── Test 15: discoveryDepth literal > 1e18 → transpiler error ────────────────
echo "Test 15: discoveryDepth = 2e18 (literal) → transpiler error"

STDERR=$(run_transpiler "(
  2000000000000000000
  100
  300000000000000000
  0
)")

assert_contains \
  "discoveryDepth 2e18: transpiler emits max1e18 error message" \
  "$STDERR" \
  "discoveryDepth literal (2000000000000000000) exceeds 1e18 (max allowed)"

# ── Summary ──────────────────────────────────────────────────────────────────
echo ""
echo "Results: $PASS passed, $FAIL failed"
[ "$FAIL" -eq 0 ]
