use mercurys::lighting::day_night::{DayNightCycle, sun_angular_diameter_rad, twilight_duration_s};
use mercurys::lighting::seasons::{
    AXIAL_TILT_DEG, MercurySeason, is_hot_pole, solar_declination_rad,
};
use mercurys::lighting::solar_position::{SolarPosition, zenith_angle};

// ── Day/Night ──

#[test]
fn day_night_cycle_creates() {
    let dn = DayNightCycle::new();
    assert!(dn.solar_day_s > 0.0);
    assert!(dn.sidereal_day_s > 0.0);
}

#[test]
fn solar_day_longer_than_sidereal() {
    let dn = DayNightCycle::new();
    assert!(dn.solar_day_s > dn.sidereal_day_s);
}

#[test]
fn solar_day_fraction_wraps() {
    let dn = DayNightCycle::new();
    let f = dn.solar_day_fraction(dn.solar_day_s * 1.5);
    assert!((0.0..1.0).contains(&f));
}

#[test]
fn is_daytime_at_subsolar() {
    let dn = DayNightCycle::new();
    assert!(dn.is_daytime(45.0, 45.0));
}

#[test]
fn is_nighttime_opposite_side() {
    let dn = DayNightCycle::new();
    assert!(!dn.is_daytime(45.0, 45.0 + 180.0));
}

#[test]
fn daylight_hours_positive() {
    let dn = DayNightCycle::new();
    let h = dn.daylight_hours(30.0);
    assert!(h.is_finite() && h > 0.0);
}

#[test]
fn sidereal_angle_increases() {
    let dn = DayNightCycle::new();
    let a1 = dn.sidereal_angle(0.0);
    let a2 = dn.sidereal_angle(1_000_000.0);
    assert!(a2 > a1);
}

#[test]
fn solar_hour_angle_finite() {
    let dn = DayNightCycle::new();
    assert!(dn.solar_hour_angle(500_000.0).is_finite());
}

#[test]
fn twilight_duration_zero() {
    assert_eq!(twilight_duration_s(), 0.0);
}

#[test]
fn sun_angular_diameter_bigger_at_perihelion() {
    let peri = sun_angular_diameter_rad(0.307);
    let aph = sun_angular_diameter_rad(0.467);
    assert!(peri > aph);
}

#[test]
fn sun_angular_diameter_reasonable() {
    let d = sun_angular_diameter_rad(0.387);
    let deg = d.to_degrees();
    assert!(deg > 1.0 && deg < 2.0, "~1.4° from Mercury mean distance");
}

// ── Seasons ──

#[test]
fn axial_tilt_near_zero() {
    assert_eq!(AXIAL_TILT_DEG, 0.034);
}

#[test]
fn perihelion_flux_higher() {
    let peri = MercurySeason::perihelion();
    let aph = MercurySeason::aphelion();
    assert!(peri.solar_flux_w_m2 > aph.solar_flux_w_m2);
}

#[test]
fn flux_ratio_positive() {
    let s = MercurySeason::at_anomaly(0.5);
    assert!(s.flux_ratio() > 0.0);
}

#[test]
fn perihelion_to_aphelion_flux_ratio_gt_1() {
    assert!(MercurySeason::perihelion_to_aphelion_flux_ratio() > 1.0);
}

#[test]
fn heliocentric_distance_varies() {
    let peri = MercurySeason::perihelion();
    let aph = MercurySeason::aphelion();
    assert!(peri.heliocentric_distance_m < aph.heliocentric_distance_m);
}

#[test]
fn solar_declination_near_zero() {
    let dec = solar_declination_rad(0.0);
    assert!(dec.abs() < 0.01, "Tiny axial tilt → negligible declination");
}

#[test]
fn is_hot_pole_at_0_and_180() {
    assert!(is_hot_pole(0.0) || is_hot_pole(180.0));
}

#[test]
fn not_hot_pole_at_90() {
    assert!(!is_hot_pole(90.0));
}

// ── Solar Position ──

#[test]
fn solar_position_at_subsolar() {
    let sp = SolarPosition::compute(0.0, 0.0, 0.0, 0.0);
    assert!(sp.elevation_deg() > 80.0);
    assert!(sp.is_above_horizon());
}

#[test]
fn solar_position_nightside() {
    let sp = SolarPosition::compute(0.0, 180.0, 0.0, 0.0);
    assert!(!sp.is_above_horizon());
}

#[test]
fn irradiance_at_subsolar_positive() {
    let sp = SolarPosition::compute(0.0, 0.0, 0.0, 0.0);
    assert!(sp.irradiance() > 0.0);
}

#[test]
fn irradiance_nightside_finite() {
    let sp = SolarPosition::compute(0.0, 180.0, 0.0, 0.0);
    assert!(sp.irradiance().is_finite());
}

#[test]
fn zenith_angle_at_noon_equator() {
    let z = zenith_angle(0.0, 0.0, 0.0);
    assert!(z < 1.0, "Should be near 0° at noon");
}

#[test]
fn zenith_angle_at_sunset_finite() {
    let z = zenith_angle(0.0, 0.0, 90.0);
    assert!(z.is_finite());
}

#[test]
fn distance_au_stored() {
    let sp = SolarPosition::compute(0.0, 0.0, 0.0, 0.0);
    assert!(sp.distance_au > 0.0);
}
