use mercurys::geodata::bathymetry::{bathymetry, has_oceans, max_depth, ocean_fraction};
use mercurys::geodata::coordinates::{
    LatLon, cartesian_to_latlon, latlon_to_cartesian, surface_area_band,
};
use mercurys::geodata::elevation::{ElevationGrid, elevation, radius_at};
use mercurys::geodata::regions::{
    MAJOR_REGIONS, QUADRANGLE_COUNT, RegionKind, find_region, quadrangle_name, regions,
};

// ── Bathymetry ──

#[test]
fn no_oceans_geodata() {
    assert!(!has_oceans());
}

#[test]
fn ocean_fraction_zero() {
    assert_eq!(ocean_fraction(), 0.0);
}

#[test]
fn max_depth_zero() {
    assert_eq!(max_depth(), 0.0);
}

#[test]
fn bathymetry_returns_zero() {
    assert_eq!(bathymetry(0.0, 0.0), 0.0);
}

// ── Coordinates ──

#[test]
fn latlon_new_stores_values() {
    let ll = LatLon::new(30.0, 120.0);
    assert!((ll.lat_deg - 30.0).abs() < 1.0e-10);
    assert!((ll.lon_deg - 120.0).abs() < 1.0e-10);
}

#[test]
fn lat_lon_rad_conversions() {
    let ll = LatLon::new(90.0, 180.0);
    assert!((ll.lat_rad() - std::f64::consts::FRAC_PI_2).abs() < 1.0e-10);
    assert!((ll.lon_rad() - std::f64::consts::PI).abs() < 1.0e-10);
}

#[test]
fn to_cartesian_unit_sphere() {
    let ll = LatLon::new(0.0, 0.0);
    let (x, y, z) = ll.to_cartesian();
    let r = (x * x + y * y + z * z).sqrt();
    assert!(r > 0.0);
}

#[test]
fn to_ecef_roundtrip() {
    let ll1 = LatLon::new(30.0, 60.0);
    let ecef = ll1.to_ecef(0.0);
    let ll2 = ecef.to_latlon();
    assert!((ll2.lat_deg - 30.0).abs() < 0.1);
    assert!((ll2.lon_deg - 60.0).abs() < 0.1);
}

#[test]
fn ecef_altitude_at_surface() {
    let ll = LatLon::new(0.0, 0.0);
    let ecef = ll.to_ecef(0.0);
    let alt = ecef.altitude();
    assert!(alt.abs() < 1000.0);
}

#[test]
fn ecef_distance_positive() {
    let a = LatLon::new(0.0, 0.0).to_ecef(0.0);
    let b = LatLon::new(45.0, 90.0).to_ecef(0.0);
    assert!(a.distance(&b) > 0.0);
}

#[test]
fn great_circle_distance_equator() {
    let a = LatLon::new(0.0, 0.0);
    let b = LatLon::new(0.0, 90.0);
    let dist = a.great_circle_distance(&b);
    let expected_quarter_circumference =
        2.0 * std::f64::consts::PI * mercurys::physics::orbit::MERCURY_RADIUS / 4.0;
    assert!((dist - expected_quarter_circumference).abs() / expected_quarter_circumference < 0.05);
}

#[test]
fn great_circle_distance_same_point() {
    let a = LatLon::new(45.0, 45.0);
    assert!(a.great_circle_distance(&a) < 1.0);
}

#[test]
fn latlon_cartesian_roundtrip() {
    let (x, y, z) = latlon_to_cartesian(30.0, 60.0);
    let (lat, lon) = cartesian_to_latlon(x, y, z);
    assert!((lat - 30.0).abs() < 0.1);
    assert!((lon - 60.0).abs() < 0.1);
}

#[test]
fn surface_area_band_positive() {
    let area = surface_area_band(0.0, 30.0);
    assert!(area > 0.0);
}

#[test]
fn surface_area_band_equator_bigger() {
    let eq = surface_area_band(0.0, 10.0);
    let polar = surface_area_band(80.0, 90.0);
    assert!(eq > polar);
}

// ── Elevation ──

#[test]
fn elevation_at_origin_finite() {
    let e = elevation(0.0, 0.0);
    assert!(e.is_finite());
}

#[test]
fn radius_at_positive() {
    let r = radius_at(0.0, 0.0);
    assert!(r > 2_000_000.0 && r < 3_000_000.0);
}

#[test]
fn elevation_grid_generates() {
    let grid = ElevationGrid::generate(-10.0, 10.0, -10.0, 10.0, 5, 5);
    assert_eq!(grid.rows, 5);
    assert_eq!(grid.cols, 5);
}

#[test]
fn elevation_grid_get() {
    let grid = ElevationGrid::generate(-10.0, 10.0, -10.0, 10.0, 5, 5);
    let e = grid.get(2, 2);
    assert!(e.is_finite());
}

#[test]
fn elevation_grid_min_lt_max() {
    let grid = ElevationGrid::generate(-90.0, 90.0, -180.0, 180.0, 10, 10);
    assert!(grid.min_elevation() <= grid.max_elevation());
}

#[test]
fn elevation_grid_mean_finite() {
    let grid = ElevationGrid::generate(-90.0, 90.0, -180.0, 180.0, 10, 10);
    assert!(grid.mean_elevation().is_finite());
}

// ── Regions ──

#[test]
fn regions_non_empty() {
    let r = regions();
    assert!(!r.is_empty());
}

#[test]
fn major_regions_has_caloris() {
    assert!(MAJOR_REGIONS.iter().any(|r| r.name == "Caloris Basin"));
}

#[test]
fn find_region_caloris() {
    let r = find_region("Caloris Basin");
    assert!(r.is_some());
    let r = r.unwrap();
    assert!(matches!(r.kind, RegionKind::Basin));
    assert!(r.diameter_km > 1000.0);
}

#[test]
fn find_region_nonexistent() {
    assert!(find_region("Atlantis").is_none());
}

#[test]
fn quadrangle_count_15() {
    assert_eq!(QUADRANGLE_COUNT, 15);
}

#[test]
fn quadrangle_names_non_empty() {
    for i in 0..QUADRANGLE_COUNT {
        let name = quadrangle_name(i);
        assert!(!name.is_empty());
    }
}

#[test]
fn all_major_regions_have_positive_diameter() {
    for r in MAJOR_REGIONS {
        assert!(
            r.diameter_km > 0.0,
            "{} should have positive diameter",
            r.name
        );
    }
}
