#!/usr/bin/env bash
# misc_repl_defer_continuation — a DEFERRED block that makes a requires
# out-call must CONTINUE across REPL ops when its external reply is fed
# daemon-style, not collapse/bail.
#
# The handler defers a block containing a requires out-call `r.get()`. Under
# `simulate --repl --output json` the daemon feeds the reply-direct op stream:
# the provides-in stimulus, the void return, the `<defer>` flush action, then
# the external reply `r.true` — each a SEPARATE op, with the engine-produced
# requires out-call `r.get` OMITTED (the daemon never feeds produced out-calls).
# When `<defer>` flushes it runs `r.get()` and the RTC suspends mid-way ("end of
# trail; stopping here"); feeding the reply must rebase onto the produced
# out-call and resume, so the deferred RTC completes.
#
# The trail-collapse regression (13fc94d8) dropped the omitted out-call, so the
# fed reply was "not reachable in this RTC" and the daemon path bailed. The
# corpus's trace.N are trail-basis (out-call included) so they never hit this;
# this pins the reply-direct defer continuation.
set -euo pipefail

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

cat > defer_cont.dzn <<'DZN'
interface ibool {
    in bool get();
    behavior {
        on get: reply(true);
        on get: reply(false);
    }
}
interface ihello {
    in void hello();
    behavior {
        on hello: {}
    }
}
component defer_cont {
    provides ihello h;
    requires ibool r;
    behavior {
        on h.hello(): defer { bool x = r.get(); }
    }
}
DZN

# Daemon-style op stream: stimulus, return, defer flush, external reply.
# The produced requires out-call `r.get` is intentionally OMITTED.
ops=$(printf '%s\n' \
  '{"op":"event","event":"h.hello"}' \
  '{"op":"event","event":"h.return"}' \
  '{"op":"event","event":"<defer>"}' \
  '{"op":"event","event":"r.true"}' \
  '{"op":"quit"}')

final_trail=$(printf '%s\n' "$ops" \
  | "$NUCLEOS" --output json simulate --repl -m defer_cont defer_cont.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))')

expected='["h.hello", "h.return", "<defer>", "r.get", "r.true"]'
if [[ "$final_trail" != "$expected" ]]; then
  echo "FAIL: daemon-style defer continuation collapsed/bailed"
  echo "--- expected ---"; printf '%s\n' "$expected"
  echo "--- got ---";      printf '%s\n' "$final_trail"
  exit 1
fi
