//! Example 1 — the north-star "combine one signal with another" move (C10),
//! expressed with the SHIPPED `Add` node (no hand-authored combinator).
//!
//! This is the demonstration that the cycle-0007 fieldtest boilerplate (a
//! project-local `Add2`) is retired: aura-std now ships `Add`, so two MA-cross
//! spreads can be summed into one exposure from shipped blocks alone.
//!
//!   price ─┬─> Sma(2) ─┐
//!          ├─> Sma(4) ─┴─> Sub (fast spread) ─┐
//!          ├─> Sma(3) ─┐                       ├─> Add ─> Exposure ─> SimBroker ─> Recorder
//!          ├─> Sma(6) ─┴─> Sub (slow spread) ─┘                          ^
//!          └────────────────────────────────────────────────────────────┘ (price)

mod recorder;

use std::cell::RefCell;
use std::rc::Rc;

use aura_core::{Scalar, Timestamp};
use aura_engine::{Edge, Harness, SourceSpec, Target};
use aura_core::ScalarKind;
use aura_std::{Add, Exposure, SimBroker, Sma, Sub};

use recorder::{Recorder, Tape};

fn main() {
    // Node indices.
    const SMA2: usize = 0;
    const SMA4: usize = 1;
    const SMA3: usize = 2;
    const SMA6: usize = 3;
    const SUB_FAST: usize = 4;
    const SUB_SLOW: usize = 5;
    const ADD: usize = 6;
    const EXPOSURE: usize = 7;
    const BROKER: usize = 8;
    const REC: usize = 9;

    let tape: Tape = Rc::new(RefCell::new(Vec::new()));

    let nodes: Vec<Box<dyn aura_core::Node>> = vec![
        Box::new(Sma::new(2)),
        Box::new(Sma::new(4)),
        Box::new(Sma::new(3)),
        Box::new(Sma::new(6)),
        Box::new(Sub::new()),
        Box::new(Sub::new()),
        Box::new(Add::new()),
        Box::new(Exposure::new(0.5)),
        Box::new(SimBroker::new(1.0)),
        Box::new(Recorder::new(tape.clone())),
    ];

    // One price source, fed into all four SMAs (slot 0) and the broker price (slot 1).
    let sources = vec![SourceSpec {
        kind: ScalarKind::F64,
        targets: vec![
            Target { node: SMA2, slot: 0 },
            Target { node: SMA4, slot: 0 },
            Target { node: SMA3, slot: 0 },
            Target { node: SMA6, slot: 0 },
            Target { node: BROKER, slot: 1 },
        ],
    }];

    let edges = vec![
        // fast spread = Sma(2) - Sma(4)
        Edge { from: SMA2, to: SUB_FAST, slot: 0, from_field: 0 },
        Edge { from: SMA4, to: SUB_FAST, slot: 1, from_field: 0 },
        // slow spread = Sma(3) - Sma(6)
        Edge { from: SMA3, to: SUB_SLOW, slot: 0, from_field: 0 },
        Edge { from: SMA6, to: SUB_SLOW, slot: 1, from_field: 0 },
        // combine the two spreads with the SHIPPED Add
        Edge { from: SUB_FAST, to: ADD, slot: 0, from_field: 0 },
        Edge { from: SUB_SLOW, to: ADD, slot: 1, from_field: 0 },
        // sum -> exposure -> broker exposure slot (0)
        Edge { from: ADD, to: EXPOSURE, slot: 0, from_field: 0 },
        Edge { from: EXPOSURE, to: BROKER, slot: 0, from_field: 0 },
        // broker equity -> recorder
        Edge { from: BROKER, to: REC, slot: 0, from_field: 0 },
    ];

    let mut harness = Harness::bootstrap(nodes, sources, edges)
        .expect("bootstrap should succeed for an acyclic, kind-consistent graph");

    // A simple rising-then-falling price path, 1 tick per ns.
    let prices: &[f64] = &[
        100.0, 101.0, 102.0, 103.0, 104.0, 105.0, 106.0, 107.0, // rising
        106.0, 104.0, 102.0, 100.0, 98.0, 96.0, 94.0, 92.0, // falling
    ];
    let stream: Vec<(Timestamp, Scalar)> = prices
        .iter()
        .enumerate()
        .map(|(i, &p)| (Timestamp(i as i64), Scalar::F64(p)))
        .collect();

    harness.run(vec![stream]);

    let tape = tape.borrow();
    println!("recorded {} pip-equity rows (one per price cycle)", tape.len());
    for (ts, eq) in tape.iter() {
        println!("  t={:>3}  equity={:+.4}", ts.0, eq);
    }

    // Determinism check (C1): same input -> identical run.
    let tape2: Tape = Rc::new(RefCell::new(Vec::new()));
    let nodes2: Vec<Box<dyn aura_core::Node>> = vec![
        Box::new(Sma::new(2)),
        Box::new(Sma::new(4)),
        Box::new(Sma::new(3)),
        Box::new(Sma::new(6)),
        Box::new(Sub::new()),
        Box::new(Sub::new()),
        Box::new(Add::new()),
        Box::new(Exposure::new(0.5)),
        Box::new(SimBroker::new(1.0)),
        Box::new(Recorder::new(tape2.clone())),
    ];
    let sources2 = vec![SourceSpec {
        kind: ScalarKind::F64,
        targets: vec![
            Target { node: SMA2, slot: 0 },
            Target { node: SMA4, slot: 0 },
            Target { node: SMA3, slot: 0 },
            Target { node: SMA6, slot: 0 },
            Target { node: BROKER, slot: 1 },
        ],
    }];
    let edges2 = vec![
        Edge { from: SMA2, to: SUB_FAST, slot: 0, from_field: 0 },
        Edge { from: SMA4, to: SUB_FAST, slot: 1, from_field: 0 },
        Edge { from: SMA3, to: SUB_SLOW, slot: 0, from_field: 0 },
        Edge { from: SMA6, to: SUB_SLOW, slot: 1, from_field: 0 },
        Edge { from: SUB_FAST, to: ADD, slot: 0, from_field: 0 },
        Edge { from: SUB_SLOW, to: ADD, slot: 1, from_field: 0 },
        Edge { from: ADD, to: EXPOSURE, slot: 0, from_field: 0 },
        Edge { from: EXPOSURE, to: BROKER, slot: 0, from_field: 0 },
        Edge { from: BROKER, to: REC, slot: 0, from_field: 0 },
    ];
    let stream2: Vec<(Timestamp, Scalar)> = prices
        .iter()
        .enumerate()
        .map(|(i, &p)| (Timestamp(i as i64), Scalar::F64(p)))
        .collect();
    let mut harness2 = Harness::bootstrap(nodes2, sources2, edges2).unwrap();
    harness2.run(vec![stream2]);
    assert_eq!(*tape, *tape2.borrow(), "C1: two runs must be bit-identical");
    println!("determinism: two runs bit-identical = OK");
}
