#![allow(clippy::expect_used, clippy::panic)]

use super::*;

fn valid() -> AircraftProfile {
    serde_json::from_str(
        r#"{ "cruise_tas_kt": 450.0, "climb_rate_fpm": 2500.0, "climb_tas_kt": 280.0,
             "descent_rate_fpm": 1800.0, "descent_tas_kt": 300.0, "cruise_burn_per_hour": 200.0,
             "taxi_fuel": 30.0, "fuel_capacity": 2000.0, "reserve_minutes": 45.0 }"#,
    )
    .expect("profile parses")
}

#[test]
fn a_sensible_profile_validates() {
    assert!(valid().validate().is_ok());
}

#[test]
fn zero_fuel_and_reserve_figures_are_allowed() {
    // Non-negative fuel figures: taxi, burn, and reserve may be zero.
    let mut p = valid();
    p.taxi_fuel = 0.0;
    p.cruise_burn_per_hour = 0.0;
    p.reserve_minutes = 0.0;
    assert!(p.validate().is_ok());
}

#[test]
fn non_finite_fields_are_rejected() {
    for bad in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
        let mut p = valid();
        p.cruise_tas_kt = bad;
        assert!(matches!(
            p.validate(),
            Err(ProfileError::NotFinite {
                field: "cruise_tas_kt",
                ..
            })
        ));
    }
}

#[test]
fn nonpositive_speeds_and_rates_are_rejected() {
    for value in [0.0, -1.0] {
        let mut p = valid();
        p.climb_tas_kt = value;
        assert!(matches!(
            p.validate(),
            Err(ProfileError::NotPositive {
                field: "climb_tas_kt",
                ..
            })
        ));

        let mut p = valid();
        p.descent_rate_fpm = value;
        assert!(matches!(
            p.validate(),
            Err(ProfileError::NotPositive {
                field: "descent_rate_fpm",
                ..
            })
        ));

        let mut p = valid();
        p.fuel_capacity = value;
        assert!(matches!(
            p.validate(),
            Err(ProfileError::NotPositive {
                field: "fuel_capacity",
                ..
            })
        ));
    }
}

#[test]
fn negative_fuel_is_rejected() {
    let mut p = valid();
    p.taxi_fuel = -10.0;
    assert!(matches!(
        p.validate(),
        Err(ProfileError::Negative {
            field: "taxi_fuel",
            ..
        })
    ));
}

#[test]
fn an_out_of_range_reserve_is_rejected() {
    let mut p = valid();
    p.reserve_minutes = 100.0 * 60.0; // 100 hours
    assert!(matches!(
        p.validate(),
        Err(ProfileError::TooLarge {
            field: "reserve_minutes",
            ..
        })
    ));
}

#[test]
fn a_hypersonic_or_reentry_speed_is_accepted() {
    // No arbitrary speed/rate ceiling: an X-43- or reentry-class profile is a
    // valid (if exotic) input. Finiteness, not a speed limit, prevents panics.
    let mut p = valid();
    p.cruise_tas_kt = 17_000.0; // ~Mach 25, reentry
    p.climb_tas_kt = 6_600.0; // ~Mach 10, X-43
    p.descent_rate_fpm = 200_000.0;
    assert!(p.validate().is_ok());
}
