use sciforge::hub::prelude::constants::elements::atomic_mass;

pub struct ExosphereSpecies {
    pub name: &'static str,
    pub symbol: &'static str,
    pub molar_mass_kg_mol: f64,
    pub column_density_m2: f64,
    pub scale_height_m: f64,
}

pub struct ExosphereEndpoint {
    pub body_radius_m: f64,
    pub species: Vec<ExosphereSpecies>,
}

pub struct IceShellEndpoint {
    pub shell_thickness_m: f64,
    pub surface_temperature_k: f64,
    pub basal_temperature_k: f64,
    pub ice_albedo: f64,
    pub crack_depth_m: f64,
    pub lineae_width_m: f64,
}

fn o2_molar() -> f64 {
    2.0 * atomic_mass(8) * 1e-3
}
fn h2_molar() -> f64 {
    2.0 * atomic_mass(1) * 1e-3
}
fn o_molar() -> f64 {
    atomic_mass(8) * 1e-3
}
fn na_molar() -> f64 {
    atomic_mass(11) * 1e-3
}
fn k_molar() -> f64 {
    atomic_mass(19) * 1e-3
}
fn h2o_molar() -> f64 {
    (2.0 * atomic_mass(1) + atomic_mass(8)) * 1e-3
}

impl ExosphereEndpoint {
    pub fn europa() -> Self {
        Self {
            body_radius_m: crate::EUROPA_RADIUS_M,
            species: vec![
                ExosphereSpecies {
                    name: "Molecular oxygen",
                    symbol: "O2",
                    molar_mass_kg_mol: o2_molar(),
                    column_density_m2: 1e19,
                    scale_height_m: 120_000.0,
                },
                ExosphereSpecies {
                    name: "Molecular hydrogen",
                    symbol: "H2",
                    molar_mass_kg_mol: h2_molar(),
                    column_density_m2: 1e17,
                    scale_height_m: 500_000.0,
                },
                ExosphereSpecies {
                    name: "Atomic oxygen",
                    symbol: "O",
                    molar_mass_kg_mol: o_molar(),
                    column_density_m2: 5e17,
                    scale_height_m: 200_000.0,
                },
                ExosphereSpecies {
                    name: "Water vapor",
                    symbol: "H2O",
                    molar_mass_kg_mol: h2o_molar(),
                    column_density_m2: 1e16,
                    scale_height_m: 25_000.0,
                },
                ExosphereSpecies {
                    name: "Sodium",
                    symbol: "Na",
                    molar_mass_kg_mol: na_molar(),
                    column_density_m2: 1e13,
                    scale_height_m: 80_000.0,
                },
                ExosphereSpecies {
                    name: "Potassium",
                    symbol: "K",
                    molar_mass_kg_mol: k_molar(),
                    column_density_m2: 1e11,
                    scale_height_m: 60_000.0,
                },
            ],
        }
    }

    pub fn density_at_altitude(&self, species_symbol: &str, altitude_m: f64) -> f64 {
        self.species
            .iter()
            .find(|s| s.symbol == species_symbol)
            .map(|s| {
                let n_surface = s.column_density_m2 / s.scale_height_m;
                n_surface * (-altitude_m / s.scale_height_m).exp()
            })
            .unwrap_or(0.0)
    }

    pub fn total_column_density(&self) -> f64 {
        self.species.iter().map(|s| s.column_density_m2).sum()
    }
}

impl IceShellEndpoint {
    pub fn europa_default() -> Self {
        Self {
            shell_thickness_m: 20_000.0,
            surface_temperature_k: 100.0,
            basal_temperature_k: 273.0,
            ice_albedo: 0.67,
            crack_depth_m: 5_000.0,
            lineae_width_m: 200.0,
        }
    }

    pub fn thermal_gradient_k_per_m(&self) -> f64 {
        (self.basal_temperature_k - self.surface_temperature_k) / self.shell_thickness_m
    }

    pub fn temperature_at_depth(&self, depth_m: f64) -> f64 {
        self.surface_temperature_k
            + self.thermal_gradient_k_per_m() * depth_m.min(self.shell_thickness_m)
    }

    pub fn ice_conductivity_w_m_k(&self) -> f64 {
        631.0 / self.surface_temperature_k
    }

    pub fn surface_heat_flux_w_m2(&self) -> f64 {
        self.ice_conductivity_w_m_k() * self.thermal_gradient_k_per_m()
    }
}
