use mercurys::atmosphere::climate::{
    MercuryThermalState, equatorial_temp_range, planck_spectral_radiance, polar_cold_trap_temp,
    solar_zenith, wien_peak_wavelength,
};
use mercurys::atmosphere::heatwaves::{
    ThermalCycle, hot_pole_longitudes_deg, hot_pole_max_temp_k, warm_pole_longitudes_deg,
    warm_pole_max_temp_k,
};
use mercurys::atmosphere::layers::{
    SURFACE_PRESSURE_PA, all_species, calcium, effective_temperature_k, helium, hydrogen,
    magnesium, mean_scale_height, mean_solar_irradiance, oxygen, potassium, sodium,
};
use mercurys::atmosphere::storms::{
    CmeImpact, mean_cme_interval_days, reconnection_rate_relative_to_earth, sep_flux_enhancement,
};
use mercurys::atmosphere::winds::{
    IMF_MAGNITUDE_NT, SOLAR_WIND_DENSITY_CM3, SOLAR_WIND_SPEED_KM_S, magnetopause_altitude_m,
    magnetopause_standoff_m, sodium_photoionization_rate, sodium_radiation_pressure_accel,
    sodium_tail_length_estimate_m, solar_wind_dynamic_pressure, surface_exposure_fraction,
    thermal_velocity,
};

// ── Climate ──

#[test]
fn subsolar_temperature_about_700k() {
    let state = MercuryThermalState::at_perihelion();
    let t = state.subsolar_temperature_k();
    assert!(t > 600.0 && t < 800.0);
}

#[test]
fn effective_temperature_reasonable() {
    let state = MercuryThermalState::mean();
    let t = state.effective_temperature_k();
    assert!(t > 200.0 && t < 600.0);
}

#[test]
fn perihelion_hotter_than_aphelion() {
    let peri = MercuryThermalState::at_perihelion();
    let aph = MercuryThermalState::at_aphelion();
    assert!(peri.subsolar_temperature_k() > aph.subsolar_temperature_k());
}

#[test]
fn solar_flux_perihelion_higher() {
    let peri = MercuryThermalState::at_perihelion();
    let aph = MercuryThermalState::at_aphelion();
    assert!(peri.solar_flux() > aph.solar_flux());
}

#[test]
fn temperature_at_zenith_decreases_with_angle() {
    let state = MercuryThermalState::at_perihelion();
    let t0 = state.temperature_at_zenith_k(0.0);
    let t60 = state.temperature_at_zenith_k(60.0);
    assert!(t0 > t60);
}

#[test]
fn outgoing_radiation_increases_with_temp() {
    let state = MercuryThermalState::mean();
    let r400 = state.outgoing_radiation(400.0);
    let r700 = state.outgoing_radiation(700.0);
    assert!(r700 > r400);
}

#[test]
fn greenhouse_effect_near_zero() {
    let state = MercuryThermalState::mean();
    let g = state.greenhouse_effect_k();
    assert!(g.abs() < 10.0, "No atmosphere, ~0 K greenhouse");
}

#[test]
fn energy_imbalance_finite() {
    let state = MercuryThermalState::mean();
    let imb = state.energy_imbalance(440.0);
    assert!(imb.is_finite());
}

#[test]
fn at_distance_adjusts_flux() {
    let close = MercuryThermalState::at_distance(0.307);
    let far = MercuryThermalState::at_distance(0.467);
    assert!(close.solar_flux() > far.solar_flux());
}

#[test]
fn equatorial_temp_range_large() {
    let (lo, hi) = equatorial_temp_range();
    assert!(hi > lo);
    assert!(hi - lo > 500.0, "Mercury has >500 K equatorial swing");
}

#[test]
fn polar_cold_trap_very_cold() {
    let t = polar_cold_trap_temp();
    assert!(t < 100.0);
}

#[test]
fn solar_zenith_angle_at_noon() {
    let z = solar_zenith(0.0, 0.0, 0.0);
    assert!(z < 1.0, "zenith at equator at noon should be ~0°");
}

#[test]
fn planck_radiance_positive() {
    let r = planck_spectral_radiance(10.0e-6, 700.0);
    assert!(r > 0.0);
}

#[test]
fn wien_peak_at_700k() {
    let w = wien_peak_wavelength(700.0);
    assert!(w > 3.0e-6 && w < 5.0e-6);
}

// ── Heatwaves / Thermal Cycle ──

#[test]
fn equatorial_swing_above_500k() {
    let eq = ThermalCycle::equatorial();
    assert!(eq.temperature_swing_k() > 500.0);
}

#[test]
fn polar_swing_smaller() {
    let eq = ThermalCycle::equatorial();
    let polar = ThermalCycle::polar();
    assert!(polar.temperature_swing_k() < eq.temperature_swing_k());
}

#[test]
fn temperature_at_phase_range() {
    let eq = ThermalCycle::equatorial();
    let t0 = eq.temperature_at_phase(0.0);
    let t05 = eq.temperature_at_phase(0.5);
    assert!(t0.is_finite() && t05.is_finite());
    assert!((t0 - t05).abs() > 100.0);
}

#[test]
fn cooling_rate_finite() {
    let eq = ThermalCycle::equatorial();
    let cr = eq.cooling_rate_at_phase(0.6);
    assert!(cr.is_finite());
}

#[test]
fn thermal_stress_positive() {
    let eq = ThermalCycle::equatorial();
    assert!(eq.thermal_stress_mpa() > 0.0);
}

#[test]
fn peak_emission_positive() {
    let eq = ThermalCycle::equatorial();
    assert!(eq.peak_emission() > 0.0);
}

#[test]
fn thermal_skin_depth_positive() {
    let eq = ThermalCycle::equatorial();
    assert!(eq.thermal_skin_depth() > 0.0);
}

#[test]
fn hot_pole_hotter_than_warm() {
    assert!(hot_pole_max_temp_k() > warm_pole_max_temp_k());
}

#[test]
fn hot_pole_longitudes_180_apart() {
    let (a, b) = hot_pole_longitudes_deg();
    assert!(((b - a).abs() - 180.0).abs() < 1.0);
}

#[test]
fn warm_pole_longitudes_180_apart() {
    let (a, b) = warm_pole_longitudes_deg();
    assert!(((b - a).abs() - 180.0).abs() < 1.0);
}

// ── Layers (Exosphere) ──

#[test]
fn exosphere_has_species() {
    let species = all_species();
    assert!(species.len() >= 5);
}

#[test]
fn sodium_column_density_positive() {
    let na = sodium();
    assert!(na.column_density_cm2 > 0.0);
}

#[test]
fn number_density_decreases_with_altitude() {
    let na = sodium();
    let n0 = na.number_density_at(0.0, 440.0);
    let n100 = na.number_density_at(100_000.0, 440.0);
    assert!(n0 > n100);
}

#[test]
fn jeans_escape_hydrogen() {
    let h = hydrogen();
    let rate = h.jeans_escape_rate(700.0);
    assert!(rate > 0.0);
}

#[test]
fn helium_scale_height_largest() {
    let he = helium();
    let na = sodium();
    assert!(he.scale_height_km > na.scale_height_km);
}

#[test]
fn oxygen_column_density_positive() {
    let o = oxygen();
    assert!(o.column_density_cm2 > 0.0);
}

#[test]
fn potassium_heavier_than_sodium() {
    let k = potassium();
    let na = sodium();
    assert!(k.molar_mass_kg > na.molar_mass_kg);
}

#[test]
fn calcium_column_density_low() {
    let ca = calcium();
    assert!(ca.column_density_cm2 < sodium().column_density_cm2);
}

#[test]
fn magnesium_column_density_positive() {
    let mg = magnesium();
    assert!(mg.column_density_cm2 > 0.0);
}

#[test]
fn mean_scale_height_positive() {
    let sh = mean_scale_height(440.0);
    assert!(sh > 0.0);
}

#[test]
fn mean_solar_irradiance_high() {
    let irr = mean_solar_irradiance();
    assert!(irr > 5000.0);
}

#[test]
fn effective_temp_reasonable() {
    let t = effective_temperature_k();
    assert!(t > 200.0 && t < 600.0);
}

#[test]
fn surface_pressure_extremely_low() {
    assert_eq!(SURFACE_PRESSURE_PA, 1.0e-10);
}

// ── Storms (CME) ──

#[test]
fn cme_typical_speed() {
    let cme = CmeImpact::typical();
    assert!(cme.speed_km_s > 400.0);
}

#[test]
fn cme_extreme_stronger() {
    let typical = CmeImpact::typical();
    let extreme = CmeImpact::extreme();
    assert!(extreme.dynamic_pressure() > typical.dynamic_pressure());
}

#[test]
fn cme_typical_magnetopause_intact() {
    let cme = CmeImpact::typical();
    assert!(!cme.magnetopause_collapse());
}

#[test]
fn cme_extreme_may_collapse_magnetopause() {
    let cme = CmeImpact::extreme();
    assert!(
        cme.magnetopause_collapse(),
        "Extreme CME should collapse magnetopause"
    );
}

#[test]
fn cme_exosphere_enhancement_positive() {
    let cme = CmeImpact::extreme();
    assert!(cme.exosphere_enhancement() > 1.0);
}

#[test]
fn sep_flux_enhanced() {
    assert!(sep_flux_enhancement() > 5.0);
}

#[test]
fn cme_interval_reasonable() {
    let days = mean_cme_interval_days();
    assert!(days > 5.0 && days < 60.0);
}

#[test]
fn reconnection_rate_gt_earth() {
    assert!(reconnection_rate_relative_to_earth() > 1.0);
}

// ── Winds (Solar Wind) ──

#[test]
fn solar_wind_pressure_positive() {
    assert!(solar_wind_dynamic_pressure() > 0.0);
}

#[test]
fn magnetopause_above_surface() {
    let r = magnetopause_standoff_m();
    assert!(r > mercurys::physics::orbit::MERCURY_RADIUS);
}

#[test]
fn magnetopause_altitude_positive() {
    assert!(magnetopause_altitude_m() > 0.0);
}

#[test]
fn surface_exposure_fraction_reasonable() {
    let f = surface_exposure_fraction();
    assert!(f > 0.0 && f < 1.0);
}

#[test]
fn thermal_velocity_increases_with_temp() {
    let v1 = thermal_velocity(0.023, 300.0);
    let v2 = thermal_velocity(0.023, 700.0);
    assert!(v2 > v1);
}

#[test]
fn sodium_photoionization_positive() {
    assert!(sodium_photoionization_rate() > 0.0);
}

#[test]
fn sodium_radiation_pressure_positive() {
    assert!(sodium_radiation_pressure_accel() > 0.0);
}

#[test]
fn sodium_tail_length_positive() {
    assert!(sodium_tail_length_estimate_m() > 0.0);
}

#[test]
fn solar_wind_constants_positive() {
    assert_eq!(SOLAR_WIND_SPEED_KM_S, 400.0);
    assert_eq!(SOLAR_WIND_DENSITY_CM3, 60.0);
    assert_eq!(IMF_MAGNITUDE_NT, 35.0);
}
