//! Gated integration test: the GER40 session-breakout shipped as a
//! World-consumable `Composite` blueprint. Proves the blueprint
//! reproduces the shipped hand-wired `FlatGraph` EXACTLY (C23) and that its
//! `param_space()` is the two genuine tuning knobs only — no `delay.lag`, no
//! bar-period (the desync trap, #96).
//!
//! Mirrors `tests/ger40_breakout_real.rs` / `real_bars.rs`: the data-gated
//! checks skip with a note where the local Pepperstone archive is absent, so
//! `cargo test --workspace` stays green anywhere. The `param_space()` check is
//! PURE (no data) and runs everywhere.
//!
//! The breakout wiring — both the shipped hand-wired `FlatGraph`
//! (`build_harness()`) and the new blueprint factory
//! (`ger40_breakout_blueprint()`) — lives once in the shared module
//! (`shared/breakout_real.rs`) and is reused by the example and the tests.

use std::sync::Arc;

use aura_core::{Scalar, Timestamp};
use chrono_tz::Europe::Berlin;
use aura_ingest::{default_data_server, open_ohlc, DataServer, DEFAULT_DATA_PATH};

#[path = "../examples/shared/breakout_real.rs"]
mod breakout_real;
use breakout_real::*;

// September 2024 (inclusive), the same window the example demonstrates and the
// hand-wired determinism test pins.
const YEAR: i32 = 2024;
const MONTH: u32 = 9;

/// (a) PURE — `param_space()` of the blueprint is EXACTLY the two genuine tuning
/// knobs `entry_bar.target` and `exit_bar.target`, and contains NEITHER
/// `delay.lag` NOR any bar-period param. This is the #96 resolution made
/// executable: the bar-period is bound structurally (both Resample and Session
/// at construction, locked equal) and `delay.lag` is bound out, so neither can
/// be swept into a desync. No data needed — runs everywhere.
#[test]
fn param_space_is_exactly_entry_and_exit_bar_targets() {
    // pure-structural: the pip is a throwaway sizing arg, never asserted here, so a
    // literal keeps this archive-independent (the topology assertion is the point).
    let (bp, _taps) = ger40_breakout_blueprint(1.0, 15, 9, 0, Berlin);
    let space = bp.param_space();

    let names: Vec<&str> = space.iter().map(|p| p.name.as_str()).collect();
    assert_eq!(
        names,
        vec!["entry_bar.target", "exit_bar.target"],
        "param_space() must be exactly the two EqConst targets",
    );

    // Belt-and-braces on the two specific leaks #96 named, independent of order.
    assert!(
        !names.iter().any(|n| n.contains("lag")),
        "delay.lag must be bound out of the space (a structural constant, not a knob)",
    );
    assert!(
        !names.iter().any(|n| n.contains("period")),
        "no bar-period param may leak (it is a structural construction arg, locked equal)",
    );
}

/// (c) determinism (PURE) — two blueprints constructed with the same args expose
/// the identical `param_space()`; the construction is a deterministic function of
/// its args. (The data-gated run-level determinism is asserted in
/// `composite_matches_flatgraph_bit_identical`.)
#[test]
fn blueprint_param_space_is_construction_deterministic() {
    let a = ger40_breakout_blueprint(1.0, 15, 9, 0, Berlin).0.param_space();
    let b = ger40_breakout_blueprint(1.0, 15, 9, 0, Berlin).0.param_space();
    assert_eq!(a, b, "blueprint param_space() is a deterministic function of its args");
}

/// Run the blueprint-bootstrapped harness over the epoch-ns [`Timestamp`] window
/// `[from, to]` and drain the recorded per-bar `(held, equity)` series. Fresh
/// build per call (fresh nodes + channels) keeps two runs disjoint for the C1
/// determinism assertion.
fn run_blueprint(server: &Arc<DataServer>, from: Timestamp, to: Timestamp) -> Vec<(f64, f64)> {
    let (bp, taps) = ger40_breakout_blueprint(
        pip_size_of(SYMBOL),
        BAR_MINUTES,
        SESSION_HOUR,
        SESSION_MINUTE,
        Berlin,
    );
    let mut h = bp
        .with("entry_bar.target", Scalar::i64(3))
        .with("exit_bar.target", Scalar::i64(5))
        .bootstrap()
        .expect("blueprint bootstraps with entry=3, exit=5");
    let sources =
        open_ohlc(server, SYMBOL, from, to).expect("GER40 has data in the Sept-2024 window");
    h.run(sources);
    let trace = drain_trace(&taps);
    trace.iter().map(|b| (b.held, b.equity)).collect()
}

/// Run the shipped hand-wired `FlatGraph` over the same window — the C23
/// reference series.
fn run_flatgraph(server: &Arc<DataServer>, from: Timestamp, to: Timestamp) -> Vec<(f64, f64)> {
    let (mut h, taps) = build_harness();
    let sources =
        open_ohlc(server, SYMBOL, from, to).expect("GER40 has data in the Sept-2024 window");
    h.run(sources);
    let trace = drain_trace(&taps);
    trace.iter().map(|b| (b.held, b.equity)).collect()
}

/// (b) C23 fidelity (gated) — over the GER40 2024-09 window, bootstrapping the
/// blueprint (entry=3, exit=5, bar_period=15) and running it produces a recorded
/// `(held, equity)` series BIT-IDENTICAL to the existing hand-wired `FlatGraph`
/// `build_harness()` over the same window. This is the behaviour-preserving
/// guarantee (C23): the Composite is just the authoring form of the same flat
/// graph, so the numbers must match exactly — not approximately.
///
/// (c) determinism (gated) — the blueprint path is bit-identical on a fresh
/// disjoint rerun (C1).
#[test]
fn composite_matches_flatgraph_bit_identical() {
    let server = default_data_server();
    if !server.has_symbol(SYMBOL) {
        eprintln!("skip: no local data at {DEFAULT_DATA_PATH} (symbol {SYMBOL} absent)");
        return; // hermetic elsewhere; exercises the real path where files exist
    }

    let (from, to) = utc_month_window(YEAR, MONTH);

    let blueprint_series = run_blueprint(&server, from, to);
    assert!(!blueprint_series.is_empty(), "window resolved to zero bars");

    // C23: the Composite blueprint reproduces the hand-wired FlatGraph EXACTLY.
    let flatgraph_series = run_flatgraph(&server, from, to);
    assert_eq!(
        blueprint_series, flatgraph_series,
        "blueprint must reproduce the hand-wired FlatGraph bit-identically (C23)",
    );

    // C1: a fresh disjoint blueprint run over the identical window is bit-identical.
    let blueprint_rerun = run_blueprint(&server, from, to);
    assert_eq!(
        blueprint_series, blueprint_rerun,
        "blueprint recorded (held, equity) series bit-identical across runs (C1)",
    );
}
