use neptunes::rendering::atmosphere_scattering::*;
use neptunes::rendering::clouds::*;
use neptunes::rendering::materials::*;
use neptunes::rendering::ocean_rendering::*;
use neptunes::rendering::shaders::*;

// ── Atmosphere Scattering ────────────────────────────────────────────────

#[test]
fn atmosphere_params_default() {
    let ap = AtmosphereParams::default();
    assert!(ap.planetradiusm > 0.0);
    assert!(ap.atmosphereheightm > 0.0);
    assert!(ap.sunintensity > 0.0);
}

#[test]
fn rayleigh_density_at_surface() {
    let ap = AtmosphereParams::default();
    let d = ap.rayleighdensity(0.0);
    assert!(d.is_finite());
    assert!((d - 1.0).abs() < 0.01, "Density at surface = 1.0");
}

#[test]
fn rayleigh_density_decreases() {
    let ap = AtmosphereParams::default();
    let d0 = ap.rayleighdensity(0.0);
    let d1 = ap.rayleighdensity(50000.0);
    assert!(d1 < d0);
}

#[test]
fn mie_density_at_surface() {
    let ap = AtmosphereParams::default();
    let d = ap.miedensity(0.0);
    assert!(d.is_finite());
}

#[test]
fn scatter_rayleigh_forward() {
    let ap = AtmosphereParams::default();
    let s = ap.scatterrayleigh(1.0);
    assert!(s.is_finite());
    assert!(s > 0.0);
}

#[test]
fn scatter_mie_forward() {
    let ap = AtmosphereParams::default();
    let s = ap.scattermie(0.99);
    assert!(s.is_finite());
    assert!(s > 0.0);
}

#[test]
fn sky_color_finite() {
    let ap = AtmosphereParams::default();
    let sundir = [0.0, 1.0, 0.0];
    let viewdir = [0.0, 0.5, 0.866];
    let color = ap.skycolor(sundir, viewdir, 0.0);
    assert!(color[0].is_finite());
    assert!(color[1].is_finite());
    assert!(color[2].is_finite());
}

#[test]
fn sky_color_nonnegative() {
    let ap = AtmosphereParams::default();
    let color = ap.skycolor([0.0, 1.0, 0.0], [1.0, 0.0, 0.0], 0.0);
    assert!(color[0] >= 0.0);
    assert!(color[1] >= 0.0);
    assert!(color[2] >= 0.0);
}

// ── Clouds ───────────────────────────────────────────────────────────────

#[test]
fn cloud_type_variants() {
    assert!(matches!(CloudType::MethaneIce, CloudType::MethaneIce));
    assert!(matches!(
        CloudType::HydrogenSulfide,
        CloudType::HydrogenSulfide
    ));
    assert!(matches!(
        CloudType::AmmoniaHydrosulfide,
        CloudType::AmmoniaHydrosulfide
    ));
    assert!(matches!(CloudType::DeepWater, CloudType::DeepWater));
    assert!(matches!(
        CloudType::HighAltitudeHaze,
        CloudType::HighAltitudeHaze
    ));
}

#[test]
fn methane_ice_cloud_layer() {
    let cl = CloudLayer::methaneice();
    assert!(cl.basealtitudem > 0.0);
    assert!(cl.thicknessm > 0.0);
    assert!(cl.coverage >= 0.0 && cl.coverage <= 1.0);
}

#[test]
fn hydrogen_sulfide_cloud_layer() {
    let cl = CloudLayer::hydrogensulfide();
    assert!(cl.thicknessm > 0.0);
}

#[test]
fn ammonia_hydrosulfide_cloud_layer() {
    let cl = CloudLayer::ammoniahydrosulfide();
    assert!(cl.thicknessm > 0.0);
}

#[test]
fn deep_water_cloud_layer() {
    let cl = CloudLayer::deepwater();
    assert!(cl.thicknessm > 0.0);
}

#[test]
fn high_altitude_haze_layer() {
    let cl = CloudLayer::highaltitudehaze();
    assert!(cl.basealtitudem > 0.0);
}

#[test]
fn cloud_system_default() {
    let cs = CloudSystem::neptunedefault();
    assert!(!cs.layers.is_empty());
}

#[test]
fn cloud_system_step() {
    let mut cs = CloudSystem::neptunedefault();
    cs.step(100.0);
    assert!(cs.timeoffsets > 0.0);
}

#[test]
fn cloud_system_sample_density() {
    let cs = CloudSystem::neptunedefault();
    let d = cs.sampledensity(50000.0, 20.0, 45.0);
    assert!(d.is_finite());
    assert!(d >= 0.0);
}

// ── Materials ────────────────────────────────────────────────────────────

#[test]
fn methane_cloud_material() {
    let m = PbrMaterial::methanecloud();
    assert!(m.roughness >= 0.0 && m.roughness <= 1.0);
    assert!(m.metallic >= 0.0 && m.metallic <= 1.0);
}

#[test]
fn h2s_cloud_material() {
    let m = PbrMaterial::h2scloud();
    assert!(m.roughness >= 0.0 && m.roughness <= 1.0);
}

#[test]
fn deep_atmosphere_material() {
    let m = PbrMaterial::deepatmosphere();
    assert!(m.albedo[3] >= 0.0);
}

#[test]
fn haze_layer_material() {
    let m = PbrMaterial::hazelayer();
    assert!(m.albedo[0] >= 0.0);
}

#[test]
fn core_rock_material() {
    let m = PbrMaterial::corerock();
    assert!(m.roughness > 0.0);
}

#[test]
fn superionic_ice_material() {
    let m = PbrMaterial::superionicice();
    assert!(m.normalstrength >= 0.0);
}

#[test]
fn storm_region_material() {
    let m = PbrMaterial::stormregion();
    assert!(m.albedo[0] >= 0.0);
}

// ── Ocean Rendering ──────────────────────────────────────────────────────

#[test]
fn ocean_params_default() {
    let op = OceanParams::default();
    assert!(op.gridsize > 0);
    assert!(op.gravity > 0.0);
    assert!(op.patchsizem > 0.0);
}

#[test]
fn phillips_spectrum_positive() {
    let op = OceanParams::default();
    let ps = op.phillipsspectrum(0.01, 0.01);
    assert!(ps.is_finite());
    assert!(ps >= 0.0);
}

#[test]
fn generate_spectrum_valid() {
    let op = OceanParams::default();
    let spec = op.generatespectrum();
    assert!(!spec.heights.is_empty());
    assert_eq!(spec.gridsize, op.gridsize);
}

#[test]
fn dispersion_positive() {
    let op = OceanParams::default();
    let d = op.dispersion(0.1);
    assert!(d.is_finite());
    assert!(d > 0.0);
}

// ── Shaders ──────────────────────────────────────────────────────────────

#[test]
fn shader_terrain() {
    let sd = ShaderData::terrain();
    assert!(!sd.name.is_empty());
    assert!(!sd.uniforms.is_empty());
}

#[test]
fn shader_atmosphere() {
    let sd = ShaderData::atmosphere();
    assert!(!sd.name.is_empty());
    assert!(!sd.uniforms.is_empty());
}

#[test]
fn shader_interior_ocean() {
    let sd = ShaderData::interiorocean();
    assert!(!sd.name.is_empty());
    assert!(!sd.uniforms.is_empty());
}

#[test]
fn uniform_value_float() {
    let uv = UniformValue::Float(1.0);
    match uv {
        UniformValue::Float(f) => assert!((f - 1.0).abs() < 1e-6),
        _ => panic!("Expected Float"),
    }
}

#[test]
fn uniform_value_vec3() {
    let uv = UniformValue::Vec3([1.0, 2.0, 3.0]);
    match uv {
        UniformValue::Vec3(v) => assert_eq!(v.len(), 3),
        _ => panic!("Expected Vec3"),
    }
}

#[test]
fn uniform_value_mat4() {
    let m = [[1.0f32; 4]; 4];
    let uv = UniformValue::Mat4(m);
    match uv {
        UniformValue::Mat4(mat) => assert_eq!(mat.len(), 4),
        _ => panic!("Expected Mat4"),
    }
}
