// Milestone fieldtest — axis (c): nest a composite INSIDE another composite,
// build a Blueprint, compile() + bootstrap() + run(). The milestone promises
// composites "nest arbitrarily" and that the compiled/flat view handles
// nesting (#26 notes the CLUSTERED blueprint render has an `unimplemented!` on
// nested composites — but that path is only reachable through `aura graph` on
// the built-in sample, which a downstream consumer cannot point at their own
// graph; see the spec finding).
//
// Here we verify the run-path promise from the consumer side: a composite that
// CONTAINS a composite lowers (inlines, recursively) to a flat runnable
// instance that produces a populated trace.
//
// Public interface only.

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};

/// Inner composite: the SMA(2)/SMA(4) cross (one price role -> spread output).
fn inner_cross() -> Composite {
    Composite::new(
        "sma_cross",
        vec![
            Sma::factory().into(),
            Sma::factory().into(),
            Sub::factory().into(),
        ],
        vec![
            Edge { from: 0, to: 2, slot: 0, from_field: 0 },
            Edge { from: 1, to: 2, slot: 1, from_field: 0 },
        ],
        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() }],
    )
}

/// Outer composite: wraps inner_cross then maps it through Exposure(0.5).
/// Interior: 0 = inner_cross (Composite), 1 = Exposure(0.5).
/// One price role fans into the inner composite's role 0.
/// Output: Exposure's output field.
fn strategy() -> Composite {
    Composite::new(
        "strategy",
        vec![
            BlueprintNode::Composite(inner_cross()),
            Exposure::factory().into(),
        ],
        vec![Edge { from: 0, to: 1, slot: 0, from_field: 0 }], // cross -> Exposure
        // price -> inner role 0
        vec![Role { name: "price".into(), targets: vec![Target { node: 0, slot: 0 }] }],
        vec![],
        vec![OutField { node: 1, field: 0, name: "out".into() }],
    )
}

fn main() {
    let (tx, rx) = mpsc::channel::<(Timestamp, Vec<Scalar>)>();
    // Top-level: 0 = strategy (nested Composite), 1 = SimBroker, 2 = Recorder.
    let blueprint = Blueprint::new(
        vec![
            BlueprintNode::Composite(strategy()),
            SimBroker::factory(1e-4).into(),
            Recorder::factory(vec![ScalarKind::F64], Firing::Any, tx).into(),
        ],
        vec![SourceSpec {
            kind: ScalarKind::F64,
            targets: vec![
                Target { node: 0, slot: 0 }, // strategy price role
                Target { node: 1, slot: 1 }, // broker price leg
            ],
        }],
        vec![
            Edge { from: 0, to: 1, slot: 0, from_field: 0 }, // strategy -> broker.exposure
            Edge { from: 1, to: 2, slot: 0, from_field: 0 }, // broker -> Recorder
        ],
    );

    // Param vector (depth-first, C12): strategy { inner_cross's two SMA lengths
    // (I64), Exposure 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("nested-composite blueprint should bootstrap");

    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);

    let rows: Vec<(Timestamp, Vec<Scalar>)> = rx.iter().collect();
    println!("nested-composite recorded rows: {}", rows.len());
    assert!(
        !rows.is_empty(),
        "a composite-in-composite must inline to a flat runnable instance"
    );
    println!("OK: composite-inside-composite inlined, bootstrapped, ran, recorded.");
}
