#!/usr/bin/env bash
# misc_repl_located_verdict — a LOCATED verdict that is only reachable AFTER a
# reply continuation must surface in the final envelope (with its file:line:
# error: detail), not be lost to a collapse/bail nor mis-deduced elsewhere.
#
# `on t.tick(): { bool x = r.get(); if (x) illegal; }` reaches the `illegal;`
# statement only once the external reply `r.true` resumes the RTC. Under
# `simulate --repl --output json` the daemon feeds the stimulus + the reply as
# SEPARATE ops, OMITTING the produced requires out-call `r.get`. Only if the fed
# reply rebases onto the produced out-call does the handler resume, take the
# `x`-true branch, and hit `illegal` — surfacing `<illegal>` located at that
# statement (`error: illegal`) in the envelope.
#
# The trail-collapse regression (13fc94d8) dropped the omitted out-call: the
# reply was "not reachable", the trail collapsed to `[t.tick]`, and the engine
# mis-deduced a spurious `non-compliance` at the behavior block instead of the
# real located `illegal`. The corpus's trail-basis traces never drove this
# verify->simulate located-verdict handoff.
set -euo pipefail

work="$NUCTEST_TMPDIR/located_verdict"
rm -rf "$work"; mkdir -p "$work"; cd "$work"

cat > located_verdict.dzn <<'DZN'
interface ibool {
    in bool get();
    behavior {
        on get: reply(true);
        on get: reply(false);
    }
}
interface itick {
    in void tick();
    behavior {
        on tick: {}
    }
}
component located_verdict {
    provides itick t;
    requires ibool r;
    behavior {
        on t.tick(): {
            bool x = r.get();
            if (x) illegal;
        }
    }
}
DZN

# Daemon-style op stream: stimulus + external reply. Produced r.get OMITTED.
ops=$(printf '%s\n' \
  '{"op":"event","event":"t.tick"}' \
  '{"op":"event","event":"r.true"}' \
  '{"op":"quit"}')

read -r trail verdict < <(printf '%s\n' "$ops" \
  | "$NUCLEOS" --output json simulate --repl -m located_verdict located_verdict.dzn 2>/dev/null \
  | python3 -c '
import sys, json
last = None; err = None
for line in sys.stdin:
    line = line.strip()
    if not line: continue
    d = json.loads(line)
    if d.get("status") == "error":
        last = "BAIL"; err = str(d.get("error")); break
    tr = d.get("trace")
    if tr is not None:
        last = tr.get("trail")
    e = d.get("error")
    if e and d.get("status") in ("verdict", "done"):
        err = e
# single-line: JSON trail (no spaces) + TAB + first error line
trail_s = json.dumps(last, separators=(",", ":")) if last != "BAIL" else "BAIL"
err_line = (err or "").splitlines()[0] if err else ""
print(trail_s + "\t" + err_line)')

expected_trail='["t.tick","r.get","r.true","<illegal>"]'
if [[ "$trail" != "$expected_trail" ]]; then
  echo "FAIL: daemon-style located verdict trail collapsed/bailed"
  echo "--- expected trail ---"; printf '%s\n' "$expected_trail"
  echo "--- got trail ---";      printf '%s\n' "$trail"
  echo "--- verdict line ---";   printf '%s\n' "$verdict"
  exit 1
fi
# The located verdict must be the real `illegal` (not a spurious non-compliance).
if [[ "$verdict" != *": error: illegal"* ]]; then
  echo "FAIL: located <illegal> verdict did not surface in the envelope"
  echo "--- got verdict line ---"; printf '%s\n' "$verdict"
  exit 1
fi
