//! Cross-source consistency tests.
//!
//! These tests don't check that the parser returns what we *expect* —
//! they check that multiple independent API calls give data that is
//! physically / economically consistent with each other. That means
//! they can catch semantic bugs (e.g. sign conventions flipped, columns
//! swapped) that no single-source test ever could.
//!
//! Run with: `cargo test --test consistency -- --test-threads=1 --nocapture`

use nemweb::Region;

// ── Energy conservation ─────────────────────────────────────────────
//
// `net_interchange` is per-region net export (positive = flowing out of
// the region). In a closed system the five regional values must sum to
// approximately zero — the power exported from one region has to arrive
// in another. The small residual is from losses being accounted
// differently on each side of a boundary; empirically it's under ±500 MW
// on a total NEM demand of ~25 GW (< 2%).
#[tokio::test]
async fn net_interchange_sums_to_zero() {
    let demand = nemweb::demand().fetch().await.unwrap();
    let total: f64 = demand.iter().map(|d| d.net_interchange).sum();

    assert!(
        total.abs() < 1000.0,
        "Sum of net_interchange across 5 regions should be ≈0 (energy \
         conservation), got {total:.0} MW. Either the parser is doing \
         something wrong, or AEMO's accounting has drifted."
    );

    println!("net_interchange sum = {total:+.0} MW (within ±1000 tolerance)");
}

// ── Basslink / TAS1 direction consistency ──────────────────────────
//
// Tasmania is an island with exactly one interconnector to the mainland:
// `T-V-MNSP1` (Basslink), from TAS to VIC. Positive `mw_flow` on
// Basslink means power flowing TAS → VIC — i.e. TAS is exporting.
//
// Under our "positive = net export" convention for `RegionDemand`, the
// sign of `TAS1.net_interchange` must match the sign of Basslink's
// `mw_flow` (whenever the flow is large enough that rounding can't flip
// the sign).
//
// This test independently verifies the two most error-prone pieces of
// our model at once:
//   1. the sign convention of `net_interchange` (positive = export)
//   2. the direction convention of `mw_flow` (from → to, per ID)
#[tokio::test]
async fn tas1_net_interchange_matches_basslink_direction() {
    let demand = nemweb::demand().fetch().await.unwrap();
    let flows = nemweb::interconnector().fetch().await.unwrap();

    let tas1 = demand
        .iter()
        .find(|d| d.region == Region::TAS1)
        .expect("TAS1 should be in demand data");

    let basslink = flows
        .iter()
        .find(|f| f.interconnector_id == "T-V-MNSP1")
        .expect("Basslink T-V-MNSP1 should be in interconnector data");

    // Skip check if flow is near zero — sign could flip on rounding.
    if basslink.mw_flow.abs() > 50.0 {
        assert_eq!(
            tas1.net_interchange.signum(),
            basslink.mw_flow.signum(),
            "TAS1 net_interchange ({:+.0} MW) and Basslink mw_flow \
             ({:+.0} MW) should have the same sign — Tasmania has only \
             one interconnector.",
            tas1.net_interchange,
            basslink.mw_flow
        );

        println!(
            "TAS1 net_ix = {:+.0} MW, Basslink flow = {:+.0} MW — signs match",
            tas1.net_interchange, basslink.mw_flow
        );
    } else {
        println!(
            "Basslink flow is {:+.1} MW (below ±50 threshold), skipping sign check",
            basslink.mw_flow
        );
    }
}

// ── Dispatch price market range ────────────────────────────────────
//
// The NEM has a regulated price floor of −$1,000/MWh and a price cap
// (as of 2024) of $17,500/MWh. Any dispatch price outside this band is
// a parser error (e.g. column index off by one picking up a demand value
// by mistake). A dedicated test keeps this invariant visible.
#[tokio::test]
async fn dispatch_prices_within_market_range() {
    let prices = nemweb::dispatch().fetch().await.unwrap();
    assert!(!prices.is_empty());

    for p in &prices {
        assert!(
            p.price.is_finite(),
            "{} price is not finite: {}",
            p.region,
            p.price
        );
        assert!(
            (-1000.0..=17500.0).contains(&p.price),
            "{} price {} out of NEM range [-1000, 17500]",
            p.region,
            p.price
        );
    }

    println!("All {} dispatch prices within market range", prices.len());
}
