//! Smoke test against a real (private) IB FLEX statement.
//!
//! This test is **skipped unless** the `IB_FLEX_REAL_XML` environment variable
//! points at a real Activity FLEX XML file. Real statements contain account
//! data and are never committed, so CI simply skips this test. To run it
//! locally against your own data:
//!
//! ```sh
//! IB_FLEX_REAL_XML=tmp/backfill-to-2026-01-13.xml cargo test --test real_data_smoke -- --nocapture
//! ```
//!
//! It verifies the parser handles a large, real, multi-statement document
//! without error and that the attributes fixed/added in the coverage work
//! actually populate from real output (not just synthetic fixtures).

use ib_flex::parse_activity_flex_all;

#[test]
fn real_statement_parses_and_populates() {
    let Ok(path) = std::env::var("IB_FLEX_REAL_XML") else {
        eprintln!("skipping: set IB_FLEX_REAL_XML to a real Activity FLEX file to run");
        return;
    };
    let xml = std::fs::read_to_string(&path).expect("read real XML file");

    let statements = parse_activity_flex_all(&xml)
        .unwrap_or_else(|e| panic!("real statement failed to parse: {e}"));
    assert!(
        !statements.is_empty(),
        "expected at least one FlexStatement"
    );

    // Tallies across every statement.
    let mut trades = 0usize;
    let mut lots = 0usize;
    let mut orders = 0usize;
    let mut symbol_summaries = 0usize;
    let mut trades_with_price = 0usize;
    let mut trades_with_ib_order_id = 0usize;
    let mut trades_with_net_cash_in_base = 0usize;
    let mut positions = 0usize;
    let mut conversion_rates = 0usize;
    let mut corporate_actions = 0usize;

    for s in &statements {
        trades += s.trades.items.len();
        lots += s.trades.lots.len();
        orders += s.trades.orders.len();
        symbol_summaries += s.trades.symbol_summaries.len();
        positions += s.positions.items.len();
        conversion_rates += s.conversion_rates.items.len();
        corporate_actions += s.corporate_actions.items.len();
        for t in &s.trades.items {
            if t.price.is_some() {
                trades_with_price += 1;
            }
            if t.ib_order_id.is_some() {
                trades_with_ib_order_id += 1;
            }
            if t.net_cash_in_base.is_some() {
                trades_with_net_cash_in_base += 1;
            }
        }
    }

    eprintln!(
        "real-data smoke: {} statements | {trades} trades ({trades_with_price} w/price, \
         {trades_with_ib_order_id} w/ibOrderID, {trades_with_net_cash_in_base} w/netCashInBase) | \
         {lots} lots | {orders} orders | {symbol_summaries} symbolSummaries | \
         {positions} positions | {conversion_rates} convRates | {corporate_actions} corpActions",
        statements.len()
    );

    // The whole point of the coverage work: real trades carry a price, and the
    // ibOrderID / netCashInBase rename+additions actually populate on real data.
    if trades > 0 {
        assert!(
            trades_with_price * 100 >= trades * 90,
            "expected >=90% of trades to carry a tradePrice, got {trades_with_price}/{trades}"
        );
        assert!(
            trades_with_ib_order_id > 0,
            "expected ibOrderID to populate on at least some real trades"
        );
    }
}
