use neptunes::physics::collisions::*;
use neptunes::physics::orbit::*;
use neptunes::physics::rotation::*;
use neptunes::physics::tides::*;

// ── Collisions ───────────────────────────────────────────────────────────

#[test]
fn asteroid_impactor_kinetic_energy() {
    let imp = Impactor::asteroid(1000.0, 20.0);
    let ke = imp.kineticenergyj();
    assert!(ke.is_finite());
    assert!(ke > 0.0);
}

#[test]
fn asteroid_impactor_kinetic_energy_mt() {
    let imp = Impactor::asteroid(1000.0, 20.0);
    let mt = imp.kineticenergymt();
    assert!(mt.is_finite());
    assert!(mt > 0.0);
}

#[test]
fn asteroid_impact_velocity() {
    let imp = Impactor::asteroid(500.0, 15.0);
    let v = imp.impactvelocity();
    assert!(v.is_finite());
    assert!(v > 0.0);
}

#[test]
fn asteroid_crater_diameter() {
    let imp = Impactor::asteroid(1000.0, 20.0);
    let d = imp.craterdiameterm(1600.0);
    assert!(d.is_finite());
    assert!(d > 0.0);
}

#[test]
fn asteroid_fireball_radius() {
    let imp = Impactor::asteroid(1000.0, 20.0);
    let r = imp.fireballradiusm();
    assert!(r.is_finite());
    assert!(r > 0.0);
}

#[test]
fn asteroid_ejecta_volume() {
    let imp = Impactor::asteroid(1000.0, 20.0);
    let v = imp.ejectavolumem3(1600.0);
    assert!(v.is_finite());
    assert!(v > 0.0);
}

#[test]
fn comet_impact_valid() {
    let imp = cometimpact();
    assert!(imp.masskg > 0.0);
    assert!(imp.velocityms > 0.0);
    let ke = imp.kineticenergyj();
    assert!(ke > 0.0);
}

#[test]
fn kuiper_belt_impactor_valid() {
    let imp = kuiperbeltimpactor();
    assert!(imp.masskg > 0.0);
    assert!(imp.velocityms > 0.0);
}

// ── Orbit ────────────────────────────────────────────────────────────────

#[test]
fn neptune_orbit_default() {
    let orb = NeptuneOrbit::new();
    assert!(orb.semimajoraxism > 0.0);
    assert!(orb.eccentricity >= 0.0 && orb.eccentricity < 1.0);
}

#[test]
fn orbit_period_seconds() {
    let orb = NeptuneOrbit::new();
    let p = orb.orbitalperiods();
    assert!(p.is_finite());
    assert!(p > 0.0);
}

#[test]
fn orbit_period_days() {
    let orb = NeptuneOrbit::new();
    let d = orb.orbitalperioddays();
    assert!(d.is_finite());
    assert!(d > 50000.0, "Neptune orbital period ~ 60000 days");
}

#[test]
fn orbit_perihelion_aphelion() {
    let orb = NeptuneOrbit::new();
    let peri = orb.perihelionm();
    let aph = orb.aphelionm();
    assert!(peri > 0.0);
    assert!(aph > peri);
}

#[test]
fn orbit_velocity_at_distance() {
    let orb = NeptuneOrbit::new();
    let v = orb.velocityatdistance(orb.semimajoraxism);
    assert!(v.is_finite());
    assert!(v > 0.0);
}

#[test]
fn orbit_specific_orbital_energy() {
    let orb = NeptuneOrbit::new();
    let e = orb.specific_orbital_energy();
    assert!(e.is_finite());
    assert!(e < 0.0, "Bound orbit has negative energy");
}

#[test]
fn orbit_specific_angular_momentum() {
    let orb = NeptuneOrbit::new();
    let l = orb.specific_angular_momentum();
    assert!(l.is_finite());
    assert!(l > 0.0);
}

#[test]
fn orbit_escape_velocity_at_surface() {
    let ve = NeptuneOrbit::escape_velocity_at_surface();
    assert!(ve.is_finite());
    assert!(ve > 20000.0, "Neptune escape velocity > 20 km/s");
}

#[test]
fn orbit_gravitational_force_sun() {
    let orb = NeptuneOrbit::new();
    let f = orb.gravitational_force_sun();
    assert!(f.is_finite());
    assert!(f > 0.0);
}

#[test]
fn orbit_current_radius() {
    let orb = NeptuneOrbit::new();
    let r = orb.current_radius();
    assert!(r.is_finite());
    assert!(r > 0.0);
}

#[test]
fn orbit_mean_orbital_velocity() {
    let orb = NeptuneOrbit::new();
    let v = orb.mean_orbital_velocity();
    assert!(v.is_finite());
    assert!(v > 0.0);
}

#[test]
fn orbit_constants_valid() {
    const { assert!(neptunes::physics::orbit::SEMIMAJORAXIS > 0.0) };
    const { assert!(neptunes::physics::orbit::ECCENTRICITY >= 0.0) };
    const { assert!(neptunes::physics::orbit::ECCENTRICITY < 1.0) };
    assert!(INCLINATIONDEG.is_finite());
}

// ── Rotation ─────────────────────────────────────────────────────────────

#[test]
fn neptune_rotation_default() {
    let rot = NeptuneRotation::new();
    assert!(rot.angularvelocityrads > 0.0);
    assert!(rot.axialtiltrad > 0.0);
}

#[test]
fn surface_velocity_at_equator() {
    let rot = NeptuneRotation::new();
    let v = rot.surfacevelocityatlatitude(0.0);
    assert!(v.is_finite());
    assert!(v > 0.0);
}

#[test]
fn surface_velocity_at_pole_near_zero() {
    let rot = NeptuneRotation::new();
    let v = rot.surfacevelocityatlatitude(90.0);
    assert!(v.is_finite());
    assert!(v < 100.0, "Velocity near pole is small");
}

#[test]
fn centripetal_acceleration_equator() {
    let rot = NeptuneRotation::new();
    let a = rot.centripetalaccelerationatlatitude(0.0);
    assert!(a.is_finite());
    assert!(a > 0.0);
}

#[test]
fn coriolis_parameter_rotation() {
    let rot = NeptuneRotation::new();
    let f = rot.coriolisparameter(45.0);
    assert!(f.is_finite());
    assert!(f.abs() > 0.0);
}

#[test]
fn moment_of_inertia() {
    let rot = NeptuneRotation::new();
    let moi = rot.momentofinertia();
    assert!(moi.is_finite());
    assert!(moi > 0.0);
}

#[test]
fn rotational_kinetic_energy() {
    let rot = NeptuneRotation::new();
    let ke = rot.rotationalkineticenergy();
    assert!(ke.is_finite());
    assert!(ke > 0.0);
}

#[test]
fn angular_momentum() {
    let rot = NeptuneRotation::new();
    let am = rot.angular_momentum();
    assert!(am.is_finite());
    assert!(am > 0.0);
}

#[test]
fn precession_rate() {
    let rot = NeptuneRotation::new();
    let pr = rot.precession_rate_rad_per_year();
    assert!(pr.is_finite());
}

#[test]
fn effective_tilt() {
    let rot = NeptuneRotation::new();
    let tilt = rot.effective_tilt_rad(0.0);
    assert!(tilt.is_finite());
    assert!(tilt > 0.0);
}

#[test]
fn day_length_variation() {
    let rot = NeptuneRotation::new();
    let dlv = rot.day_length_variation_due_to_tilt(100, 45.0);
    assert!(dlv.is_finite());
}

#[test]
fn rotation_constants() {
    const { assert!(neptunes::physics::rotation::SIDEREALDAYS > 0.0) };
    assert!((AXIALTILTDEG - 28.32).abs() < 0.1);
}

// ── Tides ────────────────────────────────────────────────────────────────

#[test]
fn tidal_from_triton_valid() {
    let tf = TidalForce::fromtriton();
    assert!(tf.bodymass > 0.0);
    assert!(tf.bodydistance > 0.0);
}

#[test]
fn tidal_from_sun_valid() {
    let tf = TidalForce::fromsun();
    assert!(tf.bodymass > 0.0);
    assert!(tf.bodydistance > 0.0);
}

#[test]
fn tidal_acceleration_triton() {
    let tf = TidalForce::fromtriton();
    let ta = tf.tidalacceleration();
    assert!(ta.is_finite());
    assert!(ta > 0.0);
}

#[test]
fn tidal_potential_triton() {
    let tf = TidalForce::fromtriton();
    let tp = tf.tidalpotential(0.0);
    assert!(tp.is_finite());
}

#[test]
fn tidal_bulge_height_triton() {
    let tf = TidalForce::fromtriton();
    let bh = tf.tidalbulgeheight();
    assert!(bh.is_finite());
    assert!(bh >= 0.0);
}

#[test]
fn gravitational_attraction_triton() {
    let tf = TidalForce::fromtriton();
    let ga = tf.gravitationalattraction();
    assert!(ga.is_finite());
    assert!(ga > 0.0);
}

#[test]
fn spring_tide_amplitude() {
    let sta = springtideamplitude();
    assert!(sta.is_finite());
    assert!(sta >= 0.0);
}

#[test]
fn neap_tide_amplitude() {
    let nta = neaptideamplitude();
    assert!(nta.is_finite());
    assert!(nta >= 0.0);
}

#[test]
fn triton_to_solar_tide_ratio() {
    let ratio = tritontosolartideratio();
    assert!(ratio.is_finite());
    assert!(ratio > 0.0);
}

#[test]
fn spring_greater_than_neap() {
    let spring = springtideamplitude();
    let neap = neaptideamplitude();
    assert!(spring >= neap);
}

#[test]
fn triton_tidal_dominates_sun() {
    let triton = TidalForce::fromtriton();
    let sun = TidalForce::fromsun();
    assert!(triton.tidalacceleration() > sun.tidalacceleration());
}
