//! Fieldtest c0006 #4 — recorded output is byte-identical across two runs (C1).
//!
//! Axis: "Then verify determinism: same input => byte-identical recorded output
//! across two runs."
//!
//! Two fresh harnesses (each its own channel), identical input. Drain both
//! recorded streams and assert bit-identical. A recorded side effect cannot feed
//! back into the graph, so determinism must hold (the cycle's own acceptance
//! claim). To make "byte-identical" concrete we compare the bit patterns of the
//! recorded f64s, not just `==` (which treats NaN specially), and the timestamps.

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::{Sma, Sub};

struct Recorder {
    tx: Sender<(Timestamp, f64)>,
}
impl Recorder {
    fn new(tx: Sender<(Timestamp, f64)>) -> Self {
        Self { tx }
    }
}
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 build_and_run(tx: Sender<(Timestamp, f64)>) {
    let mut h = Harness::bootstrap(
        vec![
            Box::new(Sma::new(2)),
            Box::new(Sma::new(4)),
            Box::new(Sub::new()),
            Box::new(Recorder::new(tx)),
        ],
        vec![SourceSpec {
            kind: ScalarKind::F64,
            targets: vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }],
        }],
        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 },
        ],
    )
    .expect("valid DAG");

    h.run(vec![f64_stream(&[
        (1, 10.5),
        (2, 12.25),
        (3, 9.75),
        (4, 16.0),
        (5, 18.125),
        (6, 7.0),
        (7, 21.5),
    ])]);
}

fn main() {
    let (tx1, rx1) = mpsc::channel();
    build_and_run(tx1);
    let run1: Vec<(Timestamp, f64)> = rx1.try_iter().collect();

    let (tx2, rx2) = mpsc::channel();
    build_and_run(tx2);
    let run2: Vec<(Timestamp, f64)> = rx2.try_iter().collect();

    println!("run1 = {run1:?}");
    println!("run2 = {run2:?}");

    // bit-exact comparison (f64::to_bits) — true byte-identity, not float ==.
    assert_eq!(run1.len(), run2.len(), "same number of recorded events");
    for (a, b) in run1.iter().zip(run2.iter()) {
        assert_eq!(a.0, b.0, "timestamps byte-identical");
        assert_eq!(a.1.to_bits(), b.1.to_bits(), "values byte-identical");
    }
    assert!(!run1.is_empty(), "the run actually recorded something");
    println!("c0006_4 OK: recorded output byte-identical across two runs (C1)");
}
