// Milestone fieldtest — axis (b): deliberately mis-wire a graph (swap the
// fast/slow SMA legs of the cross) and confirm the wiring reads back
// differently — the milestone's headline promise that a render visibly
// surfaces a mis-wiring.
//
// The headline render is the `aura graph` CLI (exercised separately, in the
// accompanying .sh-free flow: see the spec). HERE we test the property the
// render RELIES ON from the consumer side: that the labels + graph-as-data of a
// correctly-wired composite differ observably from a swapped one. If a swap
// produced an identical graph-as-data, no renderer could ever surface it.
//
// Public interface only: Composite / Blueprint accessors + Node::label() via
// the boxed node; ledger C8-refinement (label is the disambiguating symbol).

use aura_engine::{BlueprintNode, Composite, Edge, OutField, Role, Target};
// NB: `node.label()` on a `&Box<dyn Node>` leaf dispatches via the trait object
// without an explicit `use aura_core::Node` (the method is in scope through the
// boxed trait object). A consumer does not need to import the Node trait to read
// labels off a built graph.
use aura_std::{Sma, Sub};

/// Build an SMA-cross composite. `swap` flips which SMA feeds Sub's slot 0.
/// Correct: fast(SMA2) -> minuend(slot0), slow(SMA4) -> subtrahend(slot1).
/// Swapped: slow -> slot0, fast -> slot1 (the classic fast/slow mis-wire).
fn cross(swap: bool) -> Composite {
    let (e_fast, e_slow) = if swap {
        // slow(node 1) into slot0, fast(node 0) into slot1
        (
            Edge { from: 1, to: 2, slot: 0, from_field: 0 },
            Edge { from: 0, to: 2, slot: 1, from_field: 0 },
        )
    } else {
        (
            Edge { from: 0, to: 2, slot: 0, from_field: 0 },
            Edge { from: 1, to: 2, slot: 1, from_field: 0 },
        )
    };
    Composite::new(
        "sma_cross",
        vec![
            Sma::factory().into(),
            Sma::factory().into(),
            Sub::factory().into(),
        ],
        vec![e_fast, e_slow],
        vec![Role {
            name: "price".into(),
            targets: vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }],
        }],
        vec![],
        vec![OutField { node: 2, field: 0, name: "out".into() }],
    )
}

fn node_labels(c: &Composite) -> Vec<String> {
    c.nodes()
        .iter()
        .map(|n| match n {
            BlueprintNode::Leaf(node) => node.label(),
            BlueprintNode::Composite(inner) => format!("composite:{}", inner.name()),
        })
        .collect()
}

fn main() {
    let correct = cross(false);
    let swapped = cross(true);

    // The blueprint-view label is the bare value-empty type (SMA), not SMA(2)/
    // SMA(4): post cycle-0016 the lengths are injected params, so two same-type
    // leaves share a blueprint label and are disambiguated by slot/edge-table
    // (the compiled view labels them valued via Node::label, separately).
    println!("correct labels: {:?}", node_labels(&correct));
    println!("swapped labels: {:?}", node_labels(&swapped));

    // The mis-wire lives in the EDGE table; the graph-as-data must reflect it.
    let ce: Vec<Edge> = correct.edges().to_vec();
    let se: Vec<Edge> = swapped.edges().to_vec();
    println!("correct interior edges: {ce:?}");
    println!("swapped interior edges: {se:?}");

    // Headline property: the two graphs-as-data differ. A renderer reading the
    // edge table + labels can therefore make the swap visible.
    assert_ne!(
        ce, se,
        "a fast/slow swap MUST change the graph-as-data, else no render could surface it"
    );

    // The blueprint-view labels carry the value-empty node type (both SMAs read
    // as "SMA"); the swap's observability rests on the edge table above, not on a
    // per-leg label. The labels still type-distinguish SMA from Sub.
    let labels = node_labels(&correct);
    assert!(
        labels.iter().filter(|l| l.contains("SMA")).count() == 2 && labels.iter().any(|l| l.contains("Sub")),
        "labels must type-distinguish the two SMAs from the Sub: {labels:?}"
    );
    println!("OK: a fast/slow swap is observable in graph-as-data; labels are value-empty types.");
}
