#!/usr/bin/env bash
# misc_repl_nested_reply — a handler that makes SEQUENTIAL requires out-calls,
# each continued by its own external reply, whose values flow into the provides
# reply (the Alarm/AutomaticLights shape). Fed daemon-style, the full multi-level
# continuation must render, not collapse.
#
# `on p.get(): { bool a = r.get(); bool b = s.get(); reply(a && b); }` suspends
# after `r.get()`, resumes on the `r` reply, suspends after `s.get()`, resumes on
# the `s` reply, then replies `a && b` to the provides port. Under `simulate
# --repl --output json` the daemon feeds only the stimulus + the two external
# replies (`r.*`, `s.*`) as SEPARATE ops; the produced requires out-calls
# (`r.get`, `s.get`) and the final provides reply are OMITTED (engine-produced).
# Each fed reply must rebase onto its produced out-call and resume the paused RTC.
#
# The trail-collapse regression (13fc94d8) dropped the omitted out-calls, so the
# first fed reply was "not reachable" and the trail collapsed to `[p.get]` plus a
# spurious non-compliance. The corpus's trail-basis traces never exercised this
# reply-direct path.
set -euo pipefail

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

cat > nested_reply.dzn <<'DZN'
interface ibool {
    in bool get();
    behavior {
        on get: reply(true);
        on get: reply(false);
    }
}
component nested_reply {
    provides ibool p;
    requires ibool r;
    requires ibool s;
    behavior {
        on p.get(): {
            bool a = r.get();
            bool b = s.get();
            reply(a && b);
        }
    }
}
DZN

# Daemon-style op stream: stimulus + the two external replies only.
# The produced out-calls r.get / s.get and the final provides reply are OMITTED.
ops=$(printf '%s\n' \
  '{"op":"event","event":"p.get"}' \
  '{"op":"event","event":"r.true"}' \
  '{"op":"event","event":"s.false"}' \
  '{"op":"quit"}')

final_trail=$(printf '%s\n' "$ops" \
  | "$NUCLEOS" --output json simulate --repl -m nested_reply nested_reply.dzn 2>/dev/null \
  | python3 -c '
import sys, json
last = None
for line in sys.stdin:
    line = line.strip()
    if not line: continue
    d = json.loads(line)
    if d.get("status") == "error":
        print("BAIL:" + str(d.get("error")))
        break
    tr = d.get("trace")
    if tr is not None:
        last = tr.get("trail")
else:
    print(json.dumps(last))')

# a=true, b=false -> provides reply p.false, after both produced out-calls.
expected='["p.get", "r.get", "r.true", "s.get", "s.false", "p.false"]'
if [[ "$final_trail" != "$expected" ]]; then
  echo "FAIL: daemon-style nested reply continuation collapsed/bailed"
  echo "--- expected ---"; printf '%s\n' "$expected"
  echo "--- got ---";      printf '%s\n' "$final_trail"
  exit 1
fi
