//! Symbol-fan swarm harness (spec §3 symbol-fan / Testing §2). Run
//! as a subprocess by `tests/swarm.rs` under `AILANG_RC_STATS=1` so
//! the C-runtime atexit `ailang_rc_stats:` line is observable.
//!
//! argv[1] = data dir (defaults to `data_server::DEFAULT_DATA_PATH`).
//! Enumerates up to `N_SYMBOLS` symbols that have tick files
//! (requires >= 2 for a meaningful swarm), spawns one thread per
//! symbol — each thread owns its `Kernel` (`Ctx: !Send` forces
//! one ctx per thread) — folds at most `MAX_TICKS` mids, and prints
//! one `RESULT <symbol> <acc_bits:016x> <count>` line per symbol to
//! stdout. Exits 0 on success, non-zero (with a message) otherwise.

use std::sync::Arc;

use ail_embed::adapter::fold_symbol;
use data_server::records::DataFormat;
use data_server::{DataServer, DEFAULT_DATA_PATH};

/// Deterministic bounded prefix per symbol (chunk-aligned via
/// `Iterator::take`): enough real volume to be a genuine proof,
/// bounded so the E2E stays ~seconds. m5.3 owns the friction
/// timing; here it is only a runtime bound.
const MAX_TICKS: usize = 2_000_000;
/// Max threads/symbols fanned. Real data has >= 2 tick symbols
/// (EURUSD, XAUUSD); take min(N_SYMBOLS, available).
const N_SYMBOLS: usize = 4;

fn main() {
    let data_dir = std::env::args()
        .nth(1)
        .unwrap_or_else(|| DEFAULT_DATA_PATH.to_string());

    let server = Arc::new(DataServer::new(&data_dir));

    let mut tick_syms: Vec<String> = server
        .symbols()
        .into_iter()
        .filter(|s| {
            server.file_count(s, DataFormat::Tick).unwrap_or(0) > 0
        })
        .map(|s| s.to_string())
        .collect();
    tick_syms.truncate(N_SYMBOLS);

    if tick_syms.len() < 2 {
        eprintln!(
            "swarm_runner: need >= 2 tick symbols under {data_dir}, found {}",
            tick_syms.len()
        );
        std::process::exit(2);
    }

    let handles: Vec<_> = tick_syms
        .into_iter()
        .map(|sym| {
            let srv = Arc::clone(&server);
            // Each thread builds its own Kernel inside the closure.
            // `Kernel`/`Ctx` are `!Send`, so this is the ONLY way
            // this compiles — one ctx per thread, enforced by the
            // type system, not by convention.
            std::thread::spawn(move || {
                let (acc, n) = fold_symbol(&srv, &sym, MAX_TICKS)
                    .expect("symbol has a tick stream");
                (sym, acc, n)
            })
        })
        .collect();

    for h in handles {
        let (sym, acc, n) = h.join().expect("worker thread panicked");
        println!("RESULT {sym} {:016x} {n}", acc.to_bits());
    }
}
