//! Shared `#[cfg(test)]` fixtures for the SMA-cross signal-quality harness,
//! consumed by the `blueprint` and `sweep` unit-test modules. Kept in one place
//! so the harness topology cannot silently diverge between them (see #53).
//! Gated test-only at the declaration site in `lib.rs`
//! (`#[cfg(test)] mod test_fixtures;`), matching the `reexport_tests` precedent.

use crate::{BlueprintNode, Composite, Edge, OutField, Role, Target};
use aura_core::{Firing, Scalar, ScalarKind, Timestamp};
use aura_std::{Exposure, Recorder, SimBroker, Sma, Sub};
use std::sync::mpsc;

/// Seven synthetic F64 ticks driving the harness; deterministic input fixture.
pub(crate) fn synthetic_prices() -> Vec<(Timestamp, Scalar)> {
    [
        (1_i64, 1.0000_f64),
        (2, 1.0010),
        (3, 1.0030),
        (4, 1.0060),
        (5, 1.0040),
        (6, 1.0010),
        (7, 0.9990),
    ]
    .iter()
    .map(|&(t, p)| (Timestamp(t), Scalar::f64(p)))
    .collect()
}

/// The SMA-cross signal as a reusable value-empty composite: one input role
/// (price), one output (fast-minus-slow spread). Lengths injected at compile.
pub(crate) fn sma_cross() -> Composite {
    Composite::new(
        "sma_cross",
        vec![
            Sma::builder().named("fast").into(),
            Sma::builder().named("slow").into(),
            Sub::builder().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 }],
            source: None,
        }],
        vec![OutField { node: 2, field: 0, name: "out".into() }],
    )
}

/// The signal-quality harness as a value-empty composite blueprint with two
/// recording sinks (equity, exposure).
#[allow(clippy::type_complexity)]
pub(crate) fn composite_sma_cross_harness() -> (
    Composite,
    mpsc::Receiver<(Timestamp, Vec<Scalar>)>,
    mpsc::Receiver<(Timestamp, Vec<Scalar>)>,
) {
    let (tx_eq, rx_eq) = mpsc::channel();
    let (tx_ex, rx_ex) = mpsc::channel();
    let bp = Composite::new(
        "root",
        vec![
            BlueprintNode::Composite(sma_cross()),
            Exposure::builder().into(),
            SimBroker::builder(0.0001).into(),
            Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_eq).into(),
            Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_ex).into(),
        ],
        vec![
            Edge { from: 0, to: 1, slot: 0, from_field: 0 }, // composite out -> Exposure
            Edge { from: 1, to: 2, slot: 0, from_field: 0 }, // exposure -> broker slot 0
            Edge { from: 2, to: 3, slot: 0, from_field: 0 }, // equity -> sink
            Edge { from: 1, to: 4, slot: 0, from_field: 0 }, // exposure -> sink
        ],
        vec![Role {
            name: "src".into(),
            targets: vec![
                Target { node: 0, slot: 0 }, // price -> sma_cross role 0
                Target { node: 2, slot: 1 }, // price -> SimBroker price slot
            ],
            source: Some(ScalarKind::F64),
        }],
        vec![], // output: the root ends in sinks
    );
    (bp, rx_eq, rx_ex)
}
