//! Golden-fixture regression test for the Rust scorer.
//!
//! Reads `tests/scores.bincode` (produced by `mix rust.golden` from the
//! Elixir scorer) and asserts every (conditions, band) → score tuple
//! matches. Regenerate after any scorer or band_config change:
//!
//!     mix rust.golden
//!     cp priv/rust_golden/scores.bincode rust/prop_grid_rs/tests/
//!
//! The format is documented in `lib/mix/tasks/rust.golden.ex`.

use std::io::{Cursor, Read};

use byteorder::{LittleEndian, ReadBytesExt};
use prop_grid_rs::{band_config, scorer};

fn opt(v: f64) -> Option<f64> {
    if v.is_nan() {
        None
    } else {
        Some(v)
    }
}

fn read_f64(r: &mut Cursor<&[u8]>) -> f64 {
    r.read_f64::<LittleEndian>().expect("f64")
}

fn read_u8(r: &mut Cursor<&[u8]>) -> u8 {
    let mut buf = [0u8; 1];
    r.read_exact(&mut buf).expect("u8");
    buf[0]
}

#[test]
fn matches_elixir_golden_fixture() {
    let bytes = std::fs::read("tests/scores.bincode")
        .expect("tests/scores.bincode — run `mix rust.golden` first");
    let mut r = Cursor::new(bytes.as_slice());

    let n = r.read_u32::<LittleEndian>().expect("n_samples");
    assert!(n > 0, "empty fixture");

    let mut mismatches: Vec<String> = Vec::new();
    for i in 0..n {
        let c = scorer::Conditions {
            abs_humidity: read_f64(&mut r),
            temp_f: read_f64(&mut r),
            dewpoint_f: read_f64(&mut r),
            wind_speed_kts: opt(read_f64(&mut r)),
            sky_cover_pct: opt(read_f64(&mut r)),
            utc_hour: read_u8(&mut r),
            utc_minute: read_u8(&mut r),
            month: read_u8(&mut r),
            longitude: read_f64(&mut r),
            latitude: opt(read_f64(&mut r)),
            pressure_mb: opt(read_f64(&mut r)),
            prev_pressure_mb: opt(read_f64(&mut r)),
            rain_rate_mmhr: opt(read_f64(&mut r)),
            min_refractivity_gradient: opt(read_f64(&mut r)),
            bl_depth_m: opt(read_f64(&mut r)),
            pwat_mm: opt(read_f64(&mut r)),
            best_duct_band_ghz: opt(read_f64(&mut r)),
            bulk_richardson: opt(read_f64(&mut r)),
        };
        let band_mhz = r.read_u32::<LittleEndian>().expect("band_mhz");
        let expected = r.read_i32::<LittleEndian>().expect("expected_score");

        let band = band_config::get(band_mhz).expect("band in config");
        let got = scorer::composite_score(&c, band).score;
        if got != expected {
            mismatches.push(format!(
                "sample {i} band {band_mhz}: elixir={expected} rust={got}"
            ));
        }
    }

    assert!(
        mismatches.is_empty(),
        "{n} samples checked, {} mismatches:\n{}",
        mismatches.len(),
        mismatches.join("\n")
    );
}
