// Milestone fieldtest — axis (a): author a small signal as a reusable NAMED
// Composite, build a Blueprint that uses it, compile() + bootstrap() + run(),
// and confirm the recorded trace is non-empty.
//
// Public interface only: Blueprint / Composite / BlueprintNode / Edge / Target /
// OutField / SourceSpec from aura-engine; Sma / Sub / Exposure / SimBroker /
// Recorder from aura-std; ScalarKind / Scalar / Firing / Timestamp from
// aura-core. No crates/*/src was read.
//
// The downstream task: package an SMA(2)/SMA(4)-cross into a reusable
// `sma_cross` composite exposing one output (the cross spread), then wire that
// composite into a harness blueprint (source -> composite -> Exposure ->
// SimBroker -> Recorder), bootstrap it, and run it over a tiny synthetic price
// ramp. This is the milestone's headline authoring story: build a graph
// fractally from a Rust builder via a named composite.

use std::sync::mpsc;

use aura_core::{Firing, Scalar, ScalarKind, Timestamp};
use aura_engine::{Blueprint, BlueprintNode, Composite, Edge, OutField, Role, SourceSpec, Target};
use aura_std::{Exposure, Recorder, SimBroker, Sma, Sub};

fn main() {
    // --- The reusable composite: a 2/4 SMA cross. ----------------------------
    // Interior layout (local indices):
    //   0: Sma(2)  (fast)   1: Sma(4)  (slow)   2: Sub  (fast - slow)
    // One input role (the price) fans into BOTH SMAs' slot 0.
    // The exposed output port is Sub's single output field.
    // Value-empty recipes (factories); the two SMA lengths are injected at
    // bootstrap (param vector below), not baked here.
    let sma_cross = Composite::new(
        "sma_cross",
        vec![
            Sma::factory().into(),
            Sma::factory().into(),
            Sub::factory().into(),
        ],
        // interior edges (local indices): fast -> Sub.slot0, slow -> Sub.slot1
        vec![
            Edge { from: 0, to: 2, slot: 0, from_field: 0 },
            Edge { from: 1, to: 2, slot: 1, from_field: 0 },
        ],
        // input role 0 (price) fans into Sma(2).slot0 AND Sma(4).slot0
        vec![Role {
            name: "price".into(),
            targets: vec![
                Target { node: 0, slot: 0 },
                Target { node: 1, slot: 0 },
            ],
        }],
        // no param aliases
        vec![],
        // single exposed output: Sub's output field 0
        vec![OutField { node: 2, field: 0, name: "out".into() }],
    );

    // --- The harness blueprint that USES the composite. ----------------------
    // Top-level items:
    //   0: sma_cross (Composite)   1: Exposure(0.5)   2: SimBroker(1e-4)
    //   3: Recorder over [F64]  (taps the broker equity)
    let (tx, rx) = mpsc::channel::<(Timestamp, Vec<Scalar>)>();
    let blueprint = Blueprint::new(
        vec![
            BlueprintNode::Composite(sma_cross),
            Exposure::factory().into(),
            SimBroker::factory(1e-4).into(),
            Recorder::factory(vec![ScalarKind::F64], Firing::Any, tx).into(),
        ],
        // one f64 price source: feeds the composite (item 0, role 0) AND the
        // broker's price leg (item 2, slot 1).
        vec![SourceSpec {
            kind: ScalarKind::F64,
            targets: vec![
                Target { node: 0, slot: 0 }, // composite price role
                Target { node: 2, slot: 1 }, // broker price leg
            ],
        }],
        // top-level edges: composite-out -> Exposure -> broker.exposure(slot0)
        // -> Recorder
        vec![
            Edge { from: 0, to: 1, slot: 0, from_field: 0 }, // sma_cross -> Exposure
            Edge { from: 1, to: 2, slot: 0, from_field: 0 }, // Exposure -> SimBroker.exposure
            Edge { from: 2, to: 3, slot: 0, from_field: 0 }, // SimBroker -> Recorder
        ],
    );

    // --- compile() + bootstrap() + run() -------------------------------------
    // Param vector (depth-first item order, C12): sma_cross's two SMA lengths
    // (I64), then Exposure's scale (F64); Sub/SimBroker/Recorder declare none.
    let mut harness = blueprint
        .bootstrap_with_params(vec![Scalar::I64(2), Scalar::I64(4), Scalar::F64(0.5)])
        .expect("composite-authored blueprint should bootstrap");

    // a synthetic upward price ramp then a dip, enough to warm SMA(4) and flip
    let prices: Vec<f64> = vec![
        1.00, 1.01, 1.02, 1.03, 1.05, 1.08, 1.06, 1.04, 1.02, 1.01, 1.03, 1.07,
    ];
    let stream: Vec<(Timestamp, Scalar)> = prices
        .iter()
        .enumerate()
        .map(|(i, &p)| (Timestamp(60_000_000_000 * i as i64), Scalar::F64(p)))
        .collect();
    harness.run(vec![stream]);
    drop(harness); // drop the recorder's tx clone held inside

    let rows: Vec<(Timestamp, Vec<Scalar>)> = rx.iter().collect();
    println!("recorded equity rows: {}", rows.len());
    for (ts, row) in &rows {
        let v = match row[0] {
            Scalar::F64(x) => x,
            other => panic!("expected f64 equity, got {other:?}"),
        };
        println!("  ts={:>14} equity_pips={:.4}", ts.0, v);
    }
    assert!(
        !rows.is_empty(),
        "the composite-authored harness must record a populated equity trace"
    );
    println!("OK: composite authored, compiled, bootstrapped, ran, recorded.");
}
