//! Gated integration test: the GER40 archive timestamps are **genuine UTC**, so
//! the Frankfurt session aligns **DST-correctly end-to-end** (raw archive →
//! `unix_ms_to_epoch_ns` → the `Session` node's Berlin conversion).
//!
//! Why this needs pinning: `data-server` applies **no timezone math** — its
//! `delphi_to_unix_ms` is pure arithmetic (Delphi `TDateTime` → Unix-ms, no
//! offset, no DST), so the archive carries whatever the raw Pepperstone files
//! contain, *labelled* as Unix-ms-UTC. The `Session` node's UTC→Berlin DST math
//! is unit-tested in isolation, but the assumption that the raw timestamps are
//! UTC was otherwise unverified — a broker-local (e.g. EET) archive would shift
//! the whole session and the error would jump across the DST boundary.
//!
//! Decisive market signal (the DAX cash open at 09:00 Frankfurt is the morning's
//! sharp **volume** spike): in genuine-UTC data that spike renders at **Berlin
//! 09:00 in BOTH seasons**, and the same instant is **07:00 UTC** (summer, CEST,
//! UTC+2) / **08:00 UTC** (winter, CET, UTC+1) — a clean +1h DST shift. A
//! fixed-offset / broker-local archive would put the spike at the wrong Berlin
//! hour or fail to shift across DST.
//!
//! Mirrors `real_bars.rs`: skips with a note where the local Pepperstone archive
//! is absent, so `cargo test --workspace` stays green anywhere.

use std::sync::Arc;

use aura_ingest::load_m1_window;
use chrono::{TimeZone, Timelike};
use chrono_tz::Europe::Berlin;
use data_server::{DataServer, DEFAULT_DATA_PATH};

/// For one UTC calendar day of GER40, return `(morning-peak Berlin hour over
/// 06..=11, the UTC hour that the Berlin-09:00 hour maps to)`. The morning volume
/// peak is the DAX cash open; in genuine-UTC data it sits at Berlin 09:00 and the
/// same instant is 07:00 UTC (CEST) / 08:00 UTC (CET).
fn morning_open(server: &Arc<DataServer>, y: i32, m: u32, d: u32) -> (u32, u32) {
    let from = chrono::Utc.with_ymd_and_hms(y, m, d, 0, 0, 0).unwrap().timestamp_millis();
    let to = from + 24 * 3600 * 1000 - 1;
    let cols = load_m1_window(server, "GER40", Some(from), Some(to))
        .expect("GER40 has data on this day");

    let mut vol_by_berlin_hour = [0i64; 24];
    let mut utc_hour_of_berlin_9: Option<u32> = None;
    for i in 0..cols.ts.len() {
        let ns = cols.ts[i].0;
        let berlin_hour = Berlin.timestamp_nanos(ns).hour();
        vol_by_berlin_hour[berlin_hour as usize] += cols.volume[i];
        if berlin_hour == 9 && utc_hour_of_berlin_9.is_none() {
            utc_hour_of_berlin_9 = Some(chrono::Utc.timestamp_nanos(ns).hour());
        }
    }
    let morning_peak = (6u32..=11)
        .max_by_key(|&h| vol_by_berlin_hour[h as usize])
        .unwrap();
    (morning_peak, utc_hour_of_berlin_9.expect("bars exist in the Berlin 09:00 hour"))
}

#[test]
fn ger40_archive_is_utc_and_session_is_dst_correct() {
    let server = Arc::new(DataServer::new(DEFAULT_DATA_PATH));
    if !server.has_symbol("GER40") {
        eprintln!("skip: no local data at {DEFAULT_DATA_PATH} (symbol GER40 absent)");
        return; // hermetic elsewhere; exercises the real archive where files exist
    }

    // Two normal trading Mondays, one each side of the EU DST boundary.
    let (summer_peak, summer_utc) = morning_open(&server, 2024, 7, 15);
    let (winter_peak, winter_utc) = morning_open(&server, 2024, 1, 15);

    // The morning volume peak is the DAX cash open, at Berlin 09:00 in BOTH
    // seasons — the data, rendered local, places the open at the right wall-clock.
    assert_eq!(summer_peak, 9, "summer: morning volume must peak at the Berlin 09:00 cash open");
    assert_eq!(winter_peak, 9, "winter: morning volume must peak at the Berlin 09:00 cash open");

    // ...and Berlin 09:00 is the DST-correct UTC instant: 07:00 (CEST, UTC+2) in
    // summer, 08:00 (CET, UTC+1) in winter. The shift is the genuine-UTC
    // signature (a fixed-offset / broker-local archive would not shift).
    assert_eq!(summer_utc, 7, "summer: Berlin 09:00 must be 07:00 UTC (CEST, UTC+2)");
    assert_eq!(winter_utc, 8, "winter: Berlin 09:00 must be 08:00 UTC (CET, UTC+1)");
    assert_ne!(
        summer_utc, winter_utc,
        "the open's UTC instant must shift across DST — proof the archive is genuine UTC",
    );
}
