#!/usr/bin/env bash
# misc_lts_diagnose_chain — `nucleos lts-diagnose` on a target whose
# trigger edge ends a COLLAPSED MULTI-EDGE PREFIX CHAIN (spontaneous
# requires group: r.inevitable, r.done, r.<flush>, tau). The in-edge
# resolved from pass 1 then has a MID-CHAIN source, not the dispatch
# frontier the arm sites compare against — without the backward
# candidate walk the diagnostic streams nothing for the handler.
#
# Same fixture as test-models/lts_diagnose_chain (which carries the
# dzn goldens); inlined here so the misc test is self-contained.
set -euo pipefail

F="$NUCTEST_TMPDIR/chain.dzn"
cat > "$F" <<'EOF'
interface Ireq {
  in void start();
  out void done();
  behavior {
    enum S { Idle, Busy };
    S s = S.Idle;
    [s.Idle] on start: s = S.Busy;
    [s.Busy] on inevitable: { done; s = S.Idle; }
  }
}
interface Isync {
  in bool poll();
  behavior {
    on poll: reply(true);
    on poll: reply(false);
  }
}
interface Iprov {
  in void go();
  out void ready();
  behavior {
    enum P { Off, On };
    P p = P.Off;
    [p.Off] on go: p = P.On;
    [p.On] on inevitable: { ready; p = P.Off; }
  }
}
component lts_diagnose_chain {
  provides Iprov p;
  requires Ireq r;
  requires Isync q;
  behavior {
    enum W { Free, Wait };
    W w = W.Free;
    [w.Free] on p.go(): { r.start(); w = W.Wait; }
    on r.done(): { w = W.Free; bool b = q.poll(); if (b) b = q.poll(); p.ready(); }
  }
}
EOF

"$NUCLEOS" lts -m lts_diagnose_chain "$F" > "$NUCTEST_TMPDIR/chain.aut"

# Discover the handler-entry state at the END of the spontaneous
# prefix chain: r.done --> d, d --r.<flush>--> f, f --tau--> state.
# No hardcoded ids — each hop is looked up in the generated .aut.
edge_target() { # $1=source $2=label
  grep -oE "^\($1,\"$2\",[0-9]+\)" "$NUCTEST_TMPDIR/chain.aut" \
    | head -1 | sed -E 's/.*,([0-9]+)\)$/\1/'
}
d=$(grep -oE '^\([0-9]+,"r\.done",[0-9]+\)' "$NUCTEST_TMPDIR/chain.aut" \
    | head -1 | sed -E 's/.*,([0-9]+)\)$/\1/')
[[ -n "$d" ]] || { echo "FAIL: no r.done edge" >&2; cat "$NUCTEST_TMPDIR/chain.aut" >&2; exit 1; }
f=$(edge_target "$d" 'r\.<flush>')
[[ -n "$f" ]] || { echo "FAIL: no r.<flush> after r.done" >&2; cat "$NUCTEST_TMPDIR/chain.aut" >&2; exit 1; }
state=$(edge_target "$f" 'tau')
[[ -n "$state" ]] || { echo "FAIL: no tau after r.<flush>" >&2; cat "$NUCTEST_TMPDIR/chain.aut" >&2; exit 1; }
echo "discovered chain handler-entry state = $state (r.done->$d -><flush>->$f -tau-> $state)"

out=$("$NUCLEOS" lts-diagnose -m lts_diagnose_chain --state "$state" "$F" 2>&1)

fail() { echo "MISSING: $1" >&2; echo "--- output ---" >&2; echo "$out" >&2; exit 1; }

# The header must fire even though the in-edge source is mid-chain.
echo "$out" | grep -q "enumerate start: post-trigger state $state"      || fail "enumerate-start header"
echo "$out" | grep -qE '\[dbg\] +emit  [0-9]+ --q\.poll--> '            || fail "emit edge (q.poll)"
echo "$out" | grep -q 'commit_requires_call q.poll external=false'      || fail "commit_requires_call line"
echo "$out" | grep -qE 'record (NEW|MERGE)'                             || fail "dedup record line"
echo "$out" | grep -q -- "--- outgoing edges of state $state ---"       || fail "outgoing-edges dump"

# Negative control: a non-existent state arms nothing.
out2=$("$NUCLEOS" lts-diagnose -m lts_diagnose_chain --state 999999 "$F" 2>&1)
if echo "$out2" | grep -q 'enumerate start'; then
  echo "FAIL: gating leaked — enumerate trace fired for a non-target state" >&2
  exit 1
fi

echo "ok: lts-diagnose streamed the handler trace for mid-chain target $state"
