// Run with: cargo test -- --test-threads=1 --nocapture
// NEMWeb rate-limits concurrent requests, so tests must run sequentially.

use nemweb::Region;

const ALL_REGIONS: &[Region] = &[
    Region::NSW1,
    Region::QLD1,
    Region::SA1,
    Region::TAS1,
    Region::VIC1,
];

// ── Dispatch prices ────────────────────────────────────────────────

#[tokio::test]
async fn dispatch_realtime() {
    let prices = nemweb::dispatch().fetch().await.unwrap();
    assert_eq!(prices.len(), 5, "Expected 5 regions, got {}", prices.len());

    let regions: Vec<Region> = prices.iter().map(|p| p.region).collect();
    for r in ALL_REGIONS {
        assert!(regions.contains(r), "Missing region: {r}");
    }

    for p in &prices {
        assert!((-1000.0..=17500.0).contains(&p.price));
    }

    println!("\n=== Dispatch Prices ===");
    for p in &prices {
        println!(
            "  {} | ${:.2}/MWh | {}",
            p.region, p.price, p.settlement_date
        );
    }
}

#[tokio::test]
async fn dispatch_filtered() {
    let prices = nemweb::dispatch()
        .region(Region::NSW1)
        .fetch()
        .await
        .unwrap();
    assert_eq!(prices.len(), 1);
    assert_eq!(prices[0].region, Region::NSW1);
    println!("\n=== NSW1 Dispatch ===");
    println!(
        "  ${:.2}/MWh | {}",
        prices[0].price, prices[0].settlement_date
    );
}

#[tokio::test]
async fn dispatch_historical() {
    let prices = nemweb::dispatch()
        .from("2024-01-01").unwrap()
        .to("2024-01-31").unwrap()
        .fetch()
        .await
        .unwrap();

    assert!(
        prices.len() > 10000,
        "Expected >10k records for Jan 2024, got {}",
        prices.len()
    );

    let regions: std::collections::HashSet<Region> = prices.iter().map(|p| p.region).collect();
    for r in ALL_REGIONS {
        assert!(regions.contains(r), "Missing region: {r}");
    }

    // All intervals should fall within the Jan 2024 query range.
    // Note: AEMO uses interval-ending timestamps, so the last interval of
    // Jan 31 has SETTLEMENTDATE = 2024-02-01 00:00:00. It legitimately
    // belongs to January's dispatch activity.
    let jan_start = chrono::NaiveDate::from_ymd_opt(2024, 1, 1).unwrap();
    let feb_start = chrono::NaiveDate::from_ymd_opt(2024, 2, 1).unwrap();
    for p in &prices {
        assert!(
            p.settlement_date.date() >= jan_start && p.settlement_date.date() <= feb_start,
            "unexpected settlement_date: {}",
            p.settlement_date
        );
    }

    println!(
        "\n=== Historical Dispatch Jan 2024: {} records ===",
        prices.len()
    );
}

#[tokio::test]
async fn dispatch_historical_filtered() {
    let prices = nemweb::dispatch()
        .region(Region::VIC1)
        .from("2024-01-01").unwrap()
        .to("2024-01-07").unwrap()
        .fetch()
        .await
        .unwrap();

    assert!(!prices.is_empty());
    assert!(prices.iter().all(|p| p.region == Region::VIC1));
    println!(
        "\n=== VIC1 first week Jan 2024: {} records ===",
        prices.len()
    );
}

// ── Trading prices ─────────────────────────────────────────────────

#[tokio::test]
async fn trading_realtime() {
    let prices = nemweb::trading().fetch().await.unwrap();
    assert!(!prices.is_empty(), "Expected trading prices");

    let regions: std::collections::HashSet<Region> = prices.iter().map(|p| p.region).collect();
    for r in ALL_REGIONS {
        assert!(regions.contains(r), "Missing region: {r}");
    }

    println!("\n=== Trading Prices ({} records) ===", prices.len());
    for p in prices.iter().take(5) {
        println!(
            "  {} | ${:.2}/MWh | {}",
            p.region, p.price, p.settlement_date
        );
    }
}

// ── Region demand ──────────────────────────────────────────────────

#[tokio::test]
async fn demand_realtime() {
    let demand = nemweb::demand().fetch().await.unwrap();
    assert_eq!(demand.len(), 5, "Expected 5 regions, got {}", demand.len());

    for d in &demand {
        assert!(d.total_demand > 0.0, "Demand should be positive");
        assert!(
            d.available_generation > 0.0,
            "Generation should be positive"
        );
    }

    println!("\n=== Region Demand ===");
    for d in &demand {
        println!(
            "  {} | demand={:.0}MW gen={:.0}MW net_ix={:.0}MW | {}",
            d.region, d.total_demand, d.available_generation, d.net_interchange, d.settlement_date
        );
    }
}

// ── Interconnector flows ───────────────────────────────────────────

#[tokio::test]
async fn interconnector_realtime() {
    let flows = nemweb::interconnector().fetch().await.unwrap();
    assert!(!flows.is_empty(), "Expected interconnector flows");

    println!("\n=== Interconnector Flows ({} records) ===", flows.len());
    for f in &flows {
        println!(
            "  {} | {:.0}MW (losses={:.1}MW) | {}",
            f.interconnector_id, f.mw_flow, f.mw_losses, f.settlement_date
        );
    }
}

// ── Pre-dispatch ───────────────────────────────────────────────────

#[tokio::test]
async fn predispatch_realtime() {
    let forecasts = nemweb::predispatch().fetch().await.unwrap();
    assert!(
        forecasts.len() >= 50,
        "Expected 50+ forecasts, got {}",
        forecasts.len()
    );

    let regions: std::collections::HashSet<Region> = forecasts.iter().map(|f| f.region).collect();
    for r in ALL_REGIONS {
        assert!(regions.contains(r), "Missing region: {r}");
    }

    println!("\n=== Pre-dispatch ({} records) ===", forecasts.len());
    for f in forecasts.iter().take(5) {
        println!("  {} | ${:.2}/MWh | {}", f.region, f.price, f.forecast_time);
    }
}

#[tokio::test]
async fn predispatch_filtered() {
    let forecasts = nemweb::predispatch()
        .region(Region::SA1)
        .fetch()
        .await
        .unwrap();
    assert!(!forecasts.is_empty());
    assert!(forecasts.iter().all(|f| f.region == Region::SA1));
    println!("\n=== SA1 Forecasts: {} records ===", forecasts.len());
}

// ── SCADA ──────────────────────────────────────────────────────────

#[tokio::test]
async fn scada_realtime() {
    let scada = nemweb::scada().fetch().await.unwrap();
    assert!(!scada.is_empty(), "Expected SCADA records");

    println!("\n=== Unit SCADA ({} records) ===", scada.len());
    for s in scada.iter().take(5) {
        println!(
            "  {} | {:.1}MW | {}",
            s.duid, s.scada_value, s.settlement_date
        );
    }
}

// ── Serde roundtrip ────────────────────────────────────────────────

#[tokio::test]
async fn serde_roundtrip() {
    let prices = nemweb::dispatch().fetch().await.unwrap();
    let json = serde_json::to_string_pretty(&prices).unwrap();
    let parsed: Vec<nemweb::DispatchPrice> = serde_json::from_str(&json).unwrap();
    assert_eq!(prices.len(), parsed.len());
    for (a, b) in prices.iter().zip(parsed.iter()) {
        assert_eq!(a.region, b.region);
        assert_eq!(a.price, b.price);
        assert_eq!(a.settlement_date, b.settlement_date);
    }
    println!("\n=== Serde roundtrip OK ===");
    println!("{json}");
}

// ── Historical tests for new data types ────────────────────────────

#[tokio::test]
async fn trading_historical() {
    let prices = nemweb::trading()
        .from("2024-01-01").unwrap()
        .to("2024-01-07").unwrap()
        .fetch()
        .await
        .unwrap();
    assert!(!prices.is_empty(), "Expected trading records, got 0");

    let regions: std::collections::HashSet<Region> = prices.iter().map(|p| p.region).collect();
    for r in ALL_REGIONS {
        assert!(regions.contains(r), "Missing region: {r}");
    }

    println!(
        "\n=== Historical Trading Jan 1-7 2024: {} records ===",
        prices.len()
    );
    if let Some(p) = prices.first() {
        println!(
            "  first: {} | ${:.2}/MWh | {}",
            p.region, p.price, p.settlement_date
        );
    }
}

#[tokio::test]
async fn demand_historical() {
    let demand = nemweb::demand()
        .region(Region::NSW1)
        .from("2024-01-01").unwrap()
        .to("2024-01-07").unwrap()
        .fetch()
        .await
        .unwrap();
    assert!(!demand.is_empty(), "Expected demand records, got 0");
    assert!(demand.iter().all(|d| d.region == Region::NSW1));

    for d in &demand {
        assert!(d.total_demand > 0.0);
    }

    println!(
        "\n=== Historical NSW1 Demand Jan 1-7 2024: {} records ===",
        demand.len()
    );
    if let Some(d) = demand.first() {
        println!(
            "  first: {} | demand={:.0}MW gen={:.0}MW | {}",
            d.region, d.total_demand, d.available_generation, d.settlement_date
        );
    }
}

#[tokio::test]
async fn interconnector_historical() {
    let flows = nemweb::interconnector()
        .from("2024-01-01").unwrap()
        .to("2024-01-07").unwrap()
        .fetch()
        .await
        .unwrap();
    assert!(!flows.is_empty(), "Expected interconnector records, got 0");

    println!(
        "\n=== Historical Interconnector Jan 1-7 2024: {} records ===",
        flows.len()
    );
    if let Some(f) = flows.first() {
        println!(
            "  first: {} | {:.0}MW | {}",
            f.interconnector_id, f.mw_flow, f.settlement_date
        );
    }
}

#[tokio::test]
async fn scada_historical() {
    let scada = nemweb::scada()
        .duid("BASTYAN")
        .from("2024-01-01").unwrap()
        .to("2024-01-01").unwrap()
        .fetch()
        .await
        .unwrap();
    assert!(!scada.is_empty(), "Expected SCADA records, got 0");
    assert!(scada.iter().all(|s| s.duid == "BASTYAN"));

    println!(
        "\n=== Historical BASTYAN SCADA Jan 1 2024: {} records ===",
        scada.len()
    );
    if let Some(s) = scada.first() {
        println!(
            "  first: {} | {:.1}MW | {}",
            s.duid, s.scada_value, s.settlement_date
        );
    }
}

