use neptunes::atmosphere::climate::*;
use neptunes::atmosphere::heatwaves::*;
use neptunes::atmosphere::layers::*;
use neptunes::atmosphere::storms::*;
use neptunes::atmosphere::winds::*;

// ── Climate ──────────────────────────────────────────────────────────────

#[test]
fn climate_state_current_temperature() {
    let cs = ClimateState::current();
    assert!(cs.globalmeantempk > 50.0);
    assert!(cs.globalmeantempk < 100.0);
}

#[test]
fn climate_state_current_albedo() {
    let cs = ClimateState::current();
    assert!(cs.albedo > 0.0);
    assert!(cs.albedo < 1.0);
}

#[test]
fn climate_state_current_internal_heat() {
    let cs = ClimateState::current();
    assert!(cs.internalheatwm2 > 0.0);
}

#[test]
fn climate_state_current_ch4_fraction() {
    let cs = ClimateState::current();
    assert!(cs.ch4fraction > 0.0);
    assert!(cs.ch4fraction < 1.0);
}

#[test]
fn climate_outgoing_longwave_positive() {
    let cs = ClimateState::current();
    let olr = cs.outgoinglongwaveradiation();
    assert!(olr > 0.0);
}

#[test]
fn climate_absorbed_solar_positive() {
    let cs = ClimateState::current();
    let asr = cs.absorbedsolarradiation();
    assert!(asr > 0.0);
}

#[test]
fn climate_internal_plus_solar_flux() {
    let cs = ClimateState::current();
    let flux = cs.internalplussolarheatflux();
    assert!(flux > cs.absorbedsolarradiation());
}

#[test]
fn climate_energy_imbalance_finite() {
    let cs = ClimateState::current();
    let imb = cs.energyimbalance();
    assert!(imb.is_finite());
}

#[test]
fn climate_effective_emission_temperature() {
    let cs = ClimateState::current();
    let eet = cs.effectiveemissiontemperature();
    assert!(eet > 0.0);
}

#[test]
fn climate_methane_greenhouse_effect() {
    let cs = ClimateState::current();
    let mge = cs.methanegreenhouseeffectk();
    assert!(mge.is_finite());
}

#[test]
fn climate_ch4_radiative_forcing() {
    let cs = ClimateState::current();
    let rf = cs.ch4radiativeforcing();
    assert!(rf.is_finite());
}

#[test]
fn solar_zenith_at_equator_noon() {
    let sz = solarzenith(0.0, 0.0, 0.0);
    assert!(sz.is_finite());
    assert!(sz >= 0.0);
}

#[test]
fn solar_zenith_varies_with_latitude() {
    let sz0 = solarzenith(0.0, 10.0, 0.0);
    let sz60 = solarzenith(60.0, 10.0, 0.0);
    assert!((sz0 - sz60).abs() > 0.01);
}

#[test]
fn planck_spectral_radiance_positive() {
    let b = planck_spectral_radiance(10e-6, 72.0);
    assert!(b.is_finite());
    assert!(b > 0.0);
}

#[test]
fn planck_spectral_radiance_increases_with_temp() {
    let b1 = planck_spectral_radiance(10e-6, 72.0);
    let b2 = planck_spectral_radiance(10e-6, 200.0);
    assert!(b2 > b1);
}

#[test]
fn solar_flux_at_neptune_small() {
    let flux = solarfluxatneptune();
    assert!(flux > 0.0);
    assert!(flux < 10.0);
}

#[test]
fn co2_doubling_forcing_finite() {
    let f = co2doublingforcing();
    assert!(f.is_finite());
}

#[test]
fn climate_constants_valid() {
    const { assert!(neptunes::atmosphere::climate::NEPTUNEEQUILIBRIUMTEMPK > 0.0) };
    const {
        assert!(
            neptunes::atmosphere::climate::NEPTUNEEFFECTIVETEMPK
                > neptunes::atmosphere::climate::NEPTUNEEQUILIBRIUMTEMPK
        )
    };
    const { assert!(neptunes::atmosphere::climate::NEPTUNEALBEDO > 0.0) };
    const { assert!(neptunes::atmosphere::climate::NEPTUNEALBEDO < 1.0) };
}

// ── Heat Waves ───────────────────────────────────────────────────────────

#[test]
fn thermal_anomaly_warming_excess() {
    let ta = ThermalAnomaly {
        temperaturek: 80.0,
        durationdays: 10,
        altitudem: 20000.0,
    };
    let we = ta.warmingexcessk();
    assert!(we.is_finite());
}

#[test]
fn thermal_anomaly_cold_excess() {
    let ta = ThermalAnomaly {
        temperaturek: 50.0,
        durationdays: 5,
        altitudem: 30000.0,
    };
    let ce = ta.coldexcessk();
    assert!(ce.is_finite());
}

#[test]
fn thermal_anomaly_meth_evapor_temp() {
    let met = ThermalAnomaly::methevaportempk();
    assert!(met > 0.0);
}

#[test]
fn thermal_anomaly_danger_level_hot() {
    let ta = ThermalAnomaly {
        temperaturek: 100.0,
        durationdays: 30,
        altitudem: 5000.0,
    };
    let dl = ta.dangerlevel();
    assert!(!dl.is_empty());
}

#[test]
fn thermal_anomaly_danger_level_cold() {
    let ta = ThermalAnomaly {
        temperaturek: 40.0,
        durationdays: 2,
        altitudem: 50000.0,
    };
    let dl = ta.dangerlevel();
    assert!(!dl.is_empty());
}

#[test]
fn methane_condensation_pressure_at_72k() {
    let p = ThermalAnomaly::methanecondensationpressurepa(72.0);
    assert!(p.is_finite());
    assert!(p > 0.0);
}

#[test]
fn saturation_vapor_pressure_methane() {
    let svp = saturationvaporpressuremethanepa(90.0);
    assert!(svp.is_finite());
    assert!(svp > 0.0);
}

#[test]
fn saturation_vapor_pressure_increases_with_temp() {
    let svp1 = saturationvaporpressuremethanepa(70.0);
    let svp2 = saturationvaporpressuremethanepa(90.0);
    assert!(svp2 > svp1);
}

#[test]
fn dewpoint_methane_finite() {
    let dp = dewpointmethanek(1000.0);
    assert!(dp.is_finite());
    assert!(dp > 0.0);
}

#[test]
fn dewpoint_methane_increases_with_pressure() {
    let dp1 = dewpointmethanek(500.0);
    let dp2 = dewpointmethanek(5000.0);
    assert!(dp2 > dp1);
}

// ── Layers ───────────────────────────────────────────────────────────────

#[test]
fn troposphere_base_below_top() {
    let layer = troposphere();
    assert!(layer.topaltitudem > layer.basealtitudem);
}

#[test]
fn troposphere_temperature_at_base() {
    let layer = troposphere();
    let t = layer.temperatureat(layer.basealtitudem);
    assert!((t - layer.basetemperaturek).abs() < 1.0);
}

#[test]
fn troposphere_pressure_at_base() {
    let layer = troposphere();
    let p = layer.pressureat(layer.basealtitudem);
    assert!(p > 0.0);
}

#[test]
fn troposphere_density_at_base() {
    let layer = troposphere();
    let d = layer.densityat(layer.basealtitudem);
    assert!(d > 0.0);
}

#[test]
fn stratosphere_above_troposphere() {
    let tropo = troposphere();
    let strato = stratosphere();
    assert!(strato.basealtitudem >= tropo.topaltitudem - 1000.0);
}

#[test]
fn stratosphere_valid_lapse_rate() {
    let strato = stratosphere();
    assert!(strato.lapseratekperm.is_finite());
}

#[test]
fn thermosphere_above_stratosphere() {
    let strato = stratosphere();
    let thermo = thermosphere();
    assert!(thermo.basealtitudem >= strato.topaltitudem - 1000.0);
}

#[test]
fn barometric_pressure_surface() {
    let p = barometricpressure(0.0);
    assert!(p > 0.0);
}

#[test]
fn barometric_pressure_decreases() {
    let p0 = barometricpressure(0.0);
    let p1 = barometricpressure(10000.0);
    assert!(p1 < p0);
}

#[test]
fn scale_height_positive() {
    let h = scaleheight();
    assert!(h > 0.0);
}

#[test]
fn mean_solar_irradiance_small() {
    let s = meansolarirradiance();
    assert!(s > 0.0);
    assert!(s < 10.0);
}

#[test]
fn effective_temperature_finite() {
    let t = effectivetemperaturek();
    assert!(t.is_finite());
}

// ── Storms ───────────────────────────────────────────────────────────────

#[test]
fn great_dark_spot_wind_speed() {
    let gds = NeptuneStorm::greatdarkspot();
    assert!(gds.maxwindspeedms > 100.0);
}

#[test]
fn great_dark_spot_radius() {
    let gds = NeptuneStorm::greatdarkspot();
    assert!(gds.radiuskm > 1000.0);
}

#[test]
fn scooter_storm() {
    let s = NeptuneStorm::scooter();
    assert!(s.maxwindspeedms > 0.0);
    assert!(s.centralpressurehpa > 0.0);
}

#[test]
fn small_dark_spot_latitude() {
    let sds = NeptuneStorm::smalldarkspot();
    assert!(sds.latitudedeg.is_finite());
}

#[test]
fn storm_kinetic_energy_density() {
    let gds = NeptuneStorm::greatdarkspot();
    let ke = gds.kineticenergydensity();
    assert!(ke > 0.0);
}

#[test]
fn storm_vorticity_estimate() {
    let gds = NeptuneStorm::greatdarkspot();
    let v = gds.vorticityestimate();
    assert!(v.is_finite());
}

#[test]
fn storm_rossby_deformation_radius() {
    let gds = NeptuneStorm::greatdarkspot();
    let rdr = gds.rossbydeformationradius();
    assert!(rdr > 0.0);
}

#[test]
fn storm_wind_at_radius() {
    let gds = NeptuneStorm::greatdarkspot();
    let w = gds.windatradius(gds.radiuskm * 2.0);
    assert!(w.is_finite());
    assert!(w < gds.maxwindspeedms);
}

#[test]
fn potential_intensity() {
    let pi = NeptuneStorm::potentialintensityms(0.433, 55.0);
    assert!(pi.is_finite());
    assert!(pi > 0.0);
}

#[test]
fn fujita_scale_sub_ef() {
    assert_eq!(fujitascale(20.0), "Sub-EF");
}

#[test]
fn fujita_scale_ef5() {
    assert_eq!(fujitascale(600.0), "EF5");
}

#[test]
fn cape_layer_positive_buoyancy() {
    let c = capelayer(80.0, 70.0, 1000.0);
    assert!(c > 0.0);
}

#[test]
fn cape_layer_zero_when_parcel_cooler() {
    let c = capelayer(60.0, 70.0, 1000.0);
    assert!(c >= 0.0);
}

#[test]
fn cape_integrated_positive() {
    let c = capeintegrated(80.0, 72.0, 0.001, 1000.0, 10);
    assert!(c.is_finite());
    assert!(c >= 0.0);
}

// ── Winds ────────────────────────────────────────────────────────────────

#[test]
fn coriolis_parameter_zero_at_equator() {
    let f = coriolisparameter(0.0);
    assert!(f.abs() < 1e-6);
}

#[test]
fn coriolis_parameter_max_at_pole() {
    let f = coriolisparameter(90.0);
    assert!(f.abs() > 1e-5);
}

#[test]
fn geostrophic_wind_finite() {
    let v = geostrophicwindspeed(1e-4, 45.0, 0.45);
    assert!(v.is_finite());
    assert!(v >= 0.0);
}

#[test]
fn gradient_wind_finite() {
    let v = gradientwindspeed(1e-4, 1000.0, 45.0, 0.45);
    assert!(v.is_finite());
}

#[test]
fn thermal_wind_shear_finite() {
    let tw = thermalwindshear(1e-4, 72.0, 45.0, 1000.0, 50000.0);
    assert!(tw.is_finite());
}

#[test]
fn ekman_spiral_depth_positive() {
    let d = ekmanspiraldepth(10.0, 45.0);
    assert!(d.is_finite());
    assert!(d > 0.0);
}

#[test]
fn zonal_wind_at_equator() {
    let w = zonalwindprofile(0.0);
    assert!(w.is_finite());
}

#[test]
fn zonal_wind_at_high_latitude() {
    let w = zonalwindprofile(60.0);
    assert!(w.is_finite());
}

#[test]
fn beaufort_to_ms_zero() {
    let ms = beauforttoms(0);
    assert!(ms >= 0.0);
    assert!(ms < 1.0);
}

#[test]
fn beaufort_to_ms_twelve() {
    let ms = beauforttoms(12);
    assert!(ms > 30.0);
}

#[test]
fn beaufort_to_ms_monotonic() {
    let m5 = beauforttoms(5);
    let m10 = beauforttoms(10);
    assert!(m10 > m5);
}
