use neptunes::lighting::day_night::*;
use neptunes::lighting::seasons::*;
use neptunes::lighting::solar_position::*;

// ── Day/Night ────────────────────────────────────────────────────────────

#[test]
fn day_night_cycle_new() {
    let dnc = DayNightCycle::new(2451545.0);
    assert!((dnc.juliandate - 2451545.0).abs() < 1e-9);
}

#[test]
fn day_night_state_at_equator() {
    let dnc = DayNightCycle::new(2451545.0);
    let state = dnc.stateat(0.0, 0.0);
    assert!(matches!(
        state,
        DaylightState::Day
            | DaylightState::CivilTwilight
            | DaylightState::NauticalTwilight
            | DaylightState::AstronomicalTwilight
            | DaylightState::Night
    ));
}

#[test]
fn day_night_state_at_pole() {
    let dnc = DayNightCycle::new(2451545.0);
    let state = dnc.stateat(90.0, 0.0);
    assert!(matches!(
        state,
        DaylightState::Day
            | DaylightState::CivilTwilight
            | DaylightState::NauticalTwilight
            | DaylightState::AstronomicalTwilight
            | DaylightState::Night
    ));
}

#[test]
fn terminator_points_count() {
    let dnc = DayNightCycle::new(2451545.0);
    let pts = dnc.terminatorpoints(100);
    assert_eq!(pts.len(), 100);
    for pt in &pts {
        assert!(pt[0].is_finite());
        assert!(pt[1].is_finite());
    }
}

#[test]
fn ambient_light_range() {
    let dnc = DayNightCycle::new(2451545.0);
    let al = dnc.ambientlight(0.0, 0.0);
    assert!(al.is_finite());
    assert!((0.0..=1.0).contains(&al));
}

#[test]
fn daylight_state_variants() {
    assert!(matches!(DaylightState::Day, DaylightState::Day));
    assert!(matches!(
        DaylightState::CivilTwilight,
        DaylightState::CivilTwilight
    ));
    assert!(matches!(
        DaylightState::NauticalTwilight,
        DaylightState::NauticalTwilight
    ));
    assert!(matches!(
        DaylightState::AstronomicalTwilight,
        DaylightState::AstronomicalTwilight
    ));
    assert!(matches!(DaylightState::Night, DaylightState::Night));
}

// ── Seasons ──────────────────────────────────────────────────────────────

#[test]
fn season_at_j2000() {
    let ss = seasonat(2451545.0, 0.0);
    assert!(ss.solardeclinationdeg.is_finite());
    assert!(ss.daylengthhours.is_finite());
    assert!(ss.daylengthhours >= 0.0);
}

#[test]
fn season_at_high_north_latitude() {
    let ss = seasonat(2451545.0, 80.0);
    assert!(ss.daylengthhours.is_finite());
}

#[test]
fn season_at_high_south_latitude() {
    let ss = seasonat(2451545.0, -80.0);
    assert!(ss.daylengthhours.is_finite());
}

#[test]
fn season_hemispheres_opposite() {
    let ss = seasonat(2451545.0, 45.0);
    let opposite = match ss.seasonnorth {
        Season::Spring => Season::Autumn,
        Season::Summer => Season::Winter,
        Season::Autumn => Season::Spring,
        Season::Winter => Season::Summer,
    };
    assert_eq!(ss.seasonsouth, opposite);
}

#[test]
fn subsolar_point_finite() {
    let (lat, lon) = subsolarpoint(2451545.0);
    assert!(lat.is_finite());
    assert!(lon.is_finite());
    assert!((-90.0..=90.0).contains(&lat));
}

#[test]
fn neptune_orbital_period_days_constant() {
    assert!((NEPTUNEORBITALPERIODDAYS - 60182.0).abs() < 1.0);
}

#[test]
fn axial_tilt_matches_lib() {
    assert!((AXIALTILTDEG - 28.32).abs() < 0.1);
}

// ── Solar Position ───────────────────────────────────────────────────────

#[test]
fn solar_position_compute() {
    let sp = SolarPosition::compute(2451545.0, 0.0, 0.0);
    assert!(sp.azimuthdeg.is_finite());
    assert!(sp.elevationdeg.is_finite());
    assert!(sp.distanceau > 0.0);
}

#[test]
fn solar_position_direction_unit_vector() {
    let sp = SolarPosition::compute(2451545.0, 45.0, 90.0);
    let mag = (sp.direction[0].powi(2) + sp.direction[1].powi(2) + sp.direction[2].powi(2)).sqrt();
    assert!((mag - 1.0).abs() < 0.01);
}

#[test]
fn solar_position_distance_m() {
    let sp = SolarPosition::compute(2451545.0, 0.0, 0.0);
    let dm = sp.distancem();
    assert!(dm.is_finite());
    assert!(dm > 4e12, "Neptune is ~30 AU from Sun");
}

#[test]
fn solar_position_above_horizon_check() {
    let sp = SolarPosition::compute(2451545.0, 0.0, 0.0);
    let above = sp.isabovehorizon();
    assert_eq!(above, sp.elevationdeg > 0.0);
}

#[test]
fn solar_flux_wm2() {
    let sp = SolarPosition::compute(2451545.0, 0.0, 0.0);
    let flux = sp.solarfluxwm2();
    assert!(flux.is_finite());
    assert!(flux >= 0.0);
}

#[test]
fn neptune_axial_tilt_deg_constant() {
    assert!((NEPTUNEAXIALTILTDEG - 28.32).abs() < 0.01);
}
