// Task 1 (deep dive) — WHERE does the memory go? The docs (C12 realization
// cycle 0041; the streaming_seam.rs residency test) claim a M1FieldSource keeps
// O(one chunk) resident, "independent of window length". Yet the 7-year breakout
// run grew RSS to ~127 MiB. Is that the SOURCES (which the docs promise is
// bounded), or the recorder SINKS accumulating every bar into unbounded mpsc
// channels + the drain_trace Vec?
//
// This probe isolates the two by driving the SAME sources two ways over the SAME
// 7-year window:
//   (A) full breakout harness with all 4 recorder taps + drain_trace (what the
//       shipped example does), then
//   (B) the four M1FieldSource streams driven to exhaustion with NO harness, NO
//       sinks — peek/next only, the exact loop streaming_seam.rs calls bounded.
// VmHWM (peak RSS, /proc/self/status) is read after each. PUBLIC SURFACE ONLY:
// Source::{peek,next,resident_records} are the public trait methods the docs use.

use std::sync::Arc;

use chrono::TimeZone;
use data_server::{DataServer, DEFAULT_DATA_PATH};

use aura_engine::Source;
use aura_ingest::{M1Field, M1FieldSource};

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

fn vmhwm_mib() -> u64 {
    let status = std::fs::read_to_string("/proc/self/status").unwrap_or_default();
    for line in status.lines() {
        if let Some(rest) = line.strip_prefix("VmHWM:") {
            let kib: u64 = rest.split_whitespace().next().and_then(|s| s.parse().ok()).unwrap_or(0);
            return kib / 1024;
        }
    }
    0
}

fn utc_span_ms(y0: i32, m0: u32, y1: i32, m1: u32) -> (i64, i64) {
    let from = chrono::Utc.with_ymd_and_hms(y0, m0, 1, 0, 0, 0).unwrap().timestamp_millis();
    let (ny, nm) = if m1 == 12 { (y1 + 1, 1) } else { (y1, m1 + 1) };
    let next = chrono::Utc.with_ymd_and_hms(ny, nm, 1, 0, 0, 0).unwrap().timestamp_millis();
    (from, next - 1)
}

fn main() {
    let server = Arc::new(DataServer::new(DEFAULT_DATA_PATH));
    if !server.has_symbol(SYMBOL) {
        println!("skip: no local {SYMBOL} data");
        return;
    }
    let (from_ms, to_ms) = utc_span_ms(2018, 1, 2024, 12);

    println!("=== Task 1 deep dive: residency attribution (7-year GER40 window) ===");
    println!("VmHWM at start = {} MiB\n", vmhwm_mib());

    // (B) FIRST (so its peak is measured before the harness allocates): drive the
    // four field sources to exhaustion with no harness and no sinks. Per the docs
    // each keeps O(one chunk) resident — peak resident_records should stay tiny.
    {
        // resident_records() is now on the public Source trait (#95 follow-up): a
        // consumer driving boxed `dyn Source`s can read residency without a
        // downcast. We use the concrete type here, per field, exactly as the docs'
        // residency test does; the value is Option (None = a source that does not
        // report, e.g. the eager VecSource), unwrapped to 0 for the max.
        let fields = [M1Field::Open, M1Field::High, M1Field::Low, M1Field::Close];
        let mut peak_resident = 0usize;
        let mut total = 0u64;
        for f in fields {
            let mut s = M1FieldSource::open(&server, SYMBOL, Some(from_ms), Some(to_ms), f).unwrap();
            while s.peek().is_some() {
                peak_resident = peak_resident.max(s.resident_records().unwrap_or(0));
                Source::next(&mut s);
                total += 1;
            }
        }
        println!("(B) 4 raw sources, NO harness/sinks:");
        println!("    streamed {total} records, peak resident_records/source = {peak_resident}");
        println!("    VmHWM after = {} MiB", vmhwm_mib());
    }

    // (A) the full shipped breakout harness with 4 recorder taps + drain_trace.
    {
        let sources = open_ohlc_sources(&server, from_ms, to_ms).unwrap();
        let (mut h, taps) = build_harness();
        h.run(sources);
        let trace = drain_trace(&taps);
        println!("\n(A) full breakout harness + 4 recorder taps + drain_trace:");
        println!("    trace rows = {}", trace.len());
        println!("    VmHWM after = {} MiB", vmhwm_mib());
    }

    println!("\nVmHWM final = {} MiB", vmhwm_mib());
}
