use neptunes::satellites::artificial::*;
use neptunes::satellites::triton::*;

// ── Artificial Satellites ────────────────────────────────────────────────

#[test]
fn artificial_satellite_new() {
    let sat = ArtificialSatellite::new("NepOrb-1", 500.0, 500_000.0, 0.001, 0.5);
    assert_eq!(sat.name, "NepOrb-1");
    assert!((sat.masskg - 500.0).abs() < 1e-9);
    assert!(sat.semimajoraxism > 0.0);
}

#[test]
fn artificial_satellite_low_orbit() {
    let sat = ArtificialSatellite::loworbit("NLO-1", 200.0, 300_000.0);
    assert_eq!(sat.orbittype, OrbitType::Low);
    assert!(sat.altitudem > 0.0);
}

#[test]
fn artificial_satellite_polar_orbit() {
    let sat = ArtificialSatellite::polarorbit("NPO-1", 300.0, 500_000.0);
    assert!(
        sat.inclinationrad.abs() > 1.0,
        "Polar orbit inclination > 1 rad"
    );
}

#[test]
fn artificial_satellite_orbital_period() {
    let sat = ArtificialSatellite::loworbit("Test", 100.0, 500_000.0);
    let p = sat.orbitalperiods();
    assert!(p.is_finite());
    assert!(p > 0.0);
}

#[test]
fn artificial_satellite_orbital_velocity() {
    let sat = ArtificialSatellite::loworbit("Test", 100.0, 500_000.0);
    let v = sat.orbitalvelocityms();
    assert!(v.is_finite());
    assert!(v > 0.0);
}

#[test]
fn artificial_satellite_step() {
    let mut sat = ArtificialSatellite::loworbit("Test", 100.0, 500_000.0);
    let (x0, y0, z0) = sat.position();
    sat.step(1000.0);
    let (x1, y1, z1) = sat.position();
    let moved = (x1 - x0).abs() + (y1 - y0).abs() + (z1 - z0).abs();
    assert!(moved > 0.0, "Satellite should move after step");
}

#[test]
fn artificial_satellite_position_finite() {
    let sat = ArtificialSatellite::loworbit("Test", 100.0, 500_000.0);
    let (x, y, z) = sat.position();
    assert!(x.is_finite());
    assert!(y.is_finite());
    assert!(z.is_finite());
}

#[test]
fn artificial_satellite_gravity_at_surface() {
    let sat = ArtificialSatellite::loworbit("Test", 100.0, 500_000.0);
    let g = sat.gravityatsurface();
    assert!(g.is_finite());
    assert!(g > 0.0);
}

#[test]
fn orbit_type_variants() {
    assert!(matches!(OrbitType::Low, OrbitType::Low));
    assert!(matches!(OrbitType::Medium, OrbitType::Medium));
    assert!(matches!(OrbitType::High, OrbitType::High));
    assert!(matches!(OrbitType::Custom, OrbitType::Custom));
}

#[test]
fn constellation_new() {
    let c = Constellation::new("NepNet");
    assert_eq!(c.name, "NepNet");
    assert!(c.satellites.is_empty());
}

#[test]
fn constellation_add_and_positions() {
    let mut c = Constellation::new("Test");
    c.add(ArtificialSatellite::loworbit("S1", 100.0, 500_000.0));
    c.add(ArtificialSatellite::loworbit("S2", 100.0, 600_000.0));
    let positions = c.positions();
    assert_eq!(positions.len(), 2);
    for (x, y, z) in &positions {
        assert!(x.is_finite());
        assert!(y.is_finite());
        assert!(z.is_finite());
    }
}

#[test]
fn constellation_step_all() {
    let mut c = Constellation::new("Test");
    c.add(ArtificialSatellite::loworbit("S1", 100.0, 500_000.0));
    c.stepall(500.0);
    let positions = c.positions();
    assert_eq!(positions.len(), 1);
}

// ── Triton ───────────────────────────────────────────────────────────────

#[test]
fn triton_state_new() {
    let ts = TritonState::new();
    assert!((ts.masskg - TRITONMASS).abs() < 1e10);
    assert!((ts.radiusm - TRITONRADIUS).abs() < 1e3);
}

#[test]
fn triton_state_simulated() {
    let ts = TritonState::simulated();
    assert!(ts.masskg > 0.0);
}

#[test]
fn triton_state_distance() {
    let ts = TritonState::new();
    let d = ts.distance();
    assert!(d.is_finite());
    assert!(d > 0.0);
}

#[test]
fn triton_state_position_finite() {
    let ts = TritonState::new();
    let (x, y, z) = ts.position();
    assert!(x.is_finite());
    assert!(y.is_finite());
    assert!(z.is_finite());
}

#[test]
fn triton_state_gravity_at_surface() {
    let ts = TritonState::new();
    let g = ts.gravityat(TRITONRADIUS);
    assert!(g.is_finite());
    assert!(g > 0.0);
}

#[test]
fn triton_state_step() {
    let mut ts = TritonState::new();
    ts.step(86400.0);
    let d = ts.distance();
    assert!(d.is_finite());
    assert!(d > 0.0);
}

#[test]
fn triton_is_binary() {
    let ts = TritonState::new();
    assert!(!ts.isbinary(), "default Triton is not a binary system");
}

#[test]
fn triton_orbital_period_constant() {
    const { assert!(neptunes::satellites::triton::TRITONORBITALPERIOD > 0.0) };
    assert!((TRITONORBITALPERIOD - 507772.8).abs() < 1.0);
}

#[test]
fn triton_constants() {
    const { assert!(neptunes::satellites::triton::TRITONMASS > 0.0) };
    const { assert!(neptunes::satellites::triton::TRITONRADIUS > 0.0) };
    const { assert!(neptunes::satellites::triton::NEPTUNETRITONDISTANCE > 0.0) };
}

#[test]
fn triton_retrograde_orbit() {
    let ts = TritonState::new();
    assert!(ts.inclinationrad.is_finite());
    // Triton has retrograde orbit (inclination > 90 deg)
    let inc_deg = ts.inclinationrad.to_degrees();
    assert!(inc_deg > 90.0, "Triton has retrograde orbit");
}

#[test]
fn triton_eccentricity_near_zero() {
    let ts = TritonState::new();
    assert!(ts.eccentricity < 0.01, "Triton has near-circular orbit");
}
