//! Fieldtest c0007 #3 — sim-optimal pip integration with a non-unit pip_size and
//! a SHORT exposure on a falling market.
//!
//! Axis: "sim-optimal pip-equity integration via SimBroker" + "a run on a
//! different pip_size / exposure scale".
//!
//! Two things a researcher checks about the sim-optimal broker:
//!  1. A short signal on a falling price makes POSITIVE pips
//!     (negative exposure * negative return > 0) — the signal is "good".
//!  2. pip_size is a divisor: doubling pip_size halves the pip count for the
//!     same price move (rustdoc struct.SimBroker; ledger C10 Realization:
//!     prev_exposure * (price - prev_price) / pip_size).
//!
//!   price --+--> SMA(2) --\
//!           |             Sub(fast - slow) --> Exposure(4) --\
//!           +--> SMA(4) --/                                  |
//!           +------------------------------> SimBroker(pip_size) --> Recorder
//!
//! We run the SAME falling-price stream through two harnesses, pip_size 1.0 vs
//! 2.0, and assert the pip-2.0 curve is exactly half the pip-1.0 curve.

use std::sync::mpsc::{self, Sender};

use aura_core::{Ctx, Firing, InputSpec, Node, NodeSchema, Scalar, ScalarKind, Timestamp};
use aura_engine::{Edge, Harness, SourceSpec, Target};
use aura_std::{Exposure, SimBroker, Sma, Sub};

struct Recorder {
    tx: Sender<(Timestamp, f64)>,
}
impl Node for Recorder {
    fn schema(&self) -> NodeSchema {
        NodeSchema {
            inputs: vec![InputSpec { kind: ScalarKind::F64, lookback: 1, firing: Firing::Any }],
            output: vec![],
        }
    }
    fn eval(&mut self, ctx: Ctx<'_>) -> Option<&[Scalar]> {
        let w = ctx.f64_in(0);
        if w.is_empty() {
            return None;
        }
        let _ = self.tx.send((ctx.now(), w[0]));
        None
    }
}

fn f64_stream(pairs: &[(i64, f64)]) -> Vec<(Timestamp, Scalar)> {
    pairs.iter().map(|&(t, v)| (Timestamp(t), Scalar::F64(v))).collect()
}

fn run_curve(pip_size: f64, prices: &[(i64, f64)]) -> Vec<(Timestamp, f64)> {
    let (tx, rx) = mpsc::channel();
    let mut h = Harness::bootstrap(
        vec![
            Box::new(Sma::new(2)),
            Box::new(Sma::new(4)),
            Box::new(Sub::new()),
            Box::new(Exposure::new(4.0)),
            Box::new(SimBroker::new(pip_size)),
            Box::new(Recorder { tx }),
        ],
        vec![SourceSpec {
            kind: ScalarKind::F64,
            targets: vec![
                Target { node: 0, slot: 0 },
                Target { node: 1, slot: 0 },
                Target { node: 4, slot: 1 },
            ],
        }],
        vec![
            Edge { from: 0, to: 2, slot: 0, from_field: 0 },
            Edge { from: 1, to: 2, slot: 1, from_field: 0 },
            Edge { from: 2, to: 3, slot: 0, from_field: 0 },
            Edge { from: 3, to: 4, slot: 0, from_field: 0 },
            Edge { from: 4, to: 5, slot: 0, from_field: 0 },
        ],
    )
    .expect("valid DAG");
    h.run(vec![f64_stream(prices)]);
    rx.try_iter().collect()
}

fn main() {
    // Steadily FALLING price: SMA(2) below SMA(4) => negative spread => short
    // exposure; the falling price pays that short positive pips.
    let prices: &[(i64, f64)] = &[
        (1, 112.0),
        (2, 110.0),
        (3, 108.0),
        (4, 106.0),
        (5, 104.0),
        (6, 102.0),
        (7, 100.0),
    ];

    let c1 = run_curve(1.0, prices);
    let c2 = run_curve(2.0, prices);
    println!("pip_size=1.0 curve = {c1:?}");
    println!("pip_size=2.0 curve = {c2:?}");

    // Short on a falling market => non-negative, eventually positive pips.
    let final_pips_1 = c1.last().map(|&(_, v)| v).unwrap_or(f64::NAN);
    println!("final pips (pip_size=1.0) = {final_pips_1}");
    assert!(final_pips_1 > 0.0, "short signal on falling price makes positive pips");

    // pip_size is a divisor: c2 must be exactly half c1, timestamp-for-timestamp.
    assert_eq!(c1.len(), c2.len(), "same firing pattern regardless of pip_size");
    for (&(t1, v1), &(t2, v2)) in c1.iter().zip(c2.iter()) {
        assert_eq!(t1, t2, "same timestamps");
        assert!(
            (v2 - v1 / 2.0).abs() < 1e-12,
            "pip_size 2.0 pip {v2} == half of pip_size 1.0 pip {v1} at {t1:?}"
        );
    }
    println!("c0007_3 OK: short pays positive pips; pip_size halves the pip count");
}
