use crate::config::parameters::*;

#[derive(Clone, Copy)]
pub enum NEAGroup {
    Apollo,
    Amor,
    Aten,
    Atira,
}

pub struct NearEarthAsteroid {
    pub radius: f64,
    pub density: f64,
    pub mass: f64,
    pub semi_major_axis: f64,
    pub eccentricity: f64,
    pub inclination: f64,
    pub albedo: f64,
    pub group: NEAGroup,
}

impl NearEarthAsteroid {
    pub fn new(radius_m: f64, density: f64, semi_major_au: f64, eccentricity: f64) -> Self {
        let radius = radius_m.clamp(MIN_ASTEROID_RADIUS, MAX_ASTEROID_RADIUS);
        let density = density.clamp(500.0, 8000.0);
        let mass = sphere_mass(radius, density);
        let ecc = eccentricity.clamp(0.0, 0.999);
        let perihelion = semi_major_au * (1.0 - ecc);
        let aphelion = semi_major_au * (1.0 + ecc);

        let group = if aphelion < 0.983 {
            NEAGroup::Atira
        } else if semi_major_au < 1.0 && aphelion > 0.983 {
            NEAGroup::Aten
        } else if perihelion < 1.017 && semi_major_au > 1.0 {
            NEAGroup::Apollo
        } else {
            NEAGroup::Amor
        };

        Self {
            radius,
            density,
            mass,
            semi_major_axis: semi_major_au,
            eccentricity: ecc,
            inclination: 0.0,
            albedo: 0.15,
            group,
        }
    }

    pub fn with_inclination(mut self, inc_deg: f64) -> Self {
        self.inclination = inc_deg.to_radians();
        self
    }

    pub fn with_albedo(mut self, a: f64) -> Self {
        self.albedo = a.clamp(0.01, 0.99);
        self
    }

    pub fn perihelion_au(&self) -> f64 {
        self.semi_major_axis * (1.0 - self.eccentricity)
    }

    pub fn aphelion_au(&self) -> f64 {
        self.semi_major_axis * (1.0 + self.eccentricity)
    }

    pub fn orbital_period(&self) -> f64 {
        orbital_period(self.semi_major_axis, SOLAR_MASS)
    }

    pub fn orbital_period_years(&self) -> f64 {
        self.orbital_period() / YEAR
    }

    pub fn surface_gravity(&self) -> f64 {
        surface_gravity(self.mass, self.radius)
    }

    pub fn escape_velocity(&self) -> f64 {
        escape_velocity(self.mass, self.radius)
    }

    pub fn absolute_magnitude(&self) -> f64 {
        let diameter_km = 2.0 * self.radius / 1000.0;
        let p = self.albedo;
        15.618 - 5.0 * diameter_km.log10() - 2.5 * p.log10()
    }

    pub fn moid_earth(&self) -> f64 {
        let q = self.perihelion_au();
        (q - 1.0).abs()
    }

    pub fn tisserand_jupiter(&self) -> f64 {
        tisserand_parameter(
            self.semi_major_axis * AU,
            self.eccentricity,
            self.inclination,
            5.2 * AU,
        )
    }

    pub fn group_name(&self) -> &'static str {
        match self.group {
            NEAGroup::Apollo => "Apollo",
            NEAGroup::Amor => "Amor",
            NEAGroup::Aten => "Aten",
            NEAGroup::Atira => "Atira",
        }
    }

    pub fn velocity_at_perihelion(&self) -> f64 {
        vis_viva(
            SOLAR_MASS,
            self.perihelion_au() * AU,
            self.semi_major_axis * AU,
        )
    }

    pub fn velocity_at_aphelion(&self) -> f64 {
        vis_viva(
            SOLAR_MASS,
            self.aphelion_au() * AU,
            self.semi_major_axis * AU,
        )
    }

    pub fn equilibrium_temp_perihelion(&self) -> f64 {
        equilibrium_temperature(SOLAR_LUMINOSITY, self.perihelion_au() * AU, self.albedo)
    }

    pub fn equilibrium_temp_aphelion(&self) -> f64 {
        equilibrium_temperature(SOLAR_LUMINOSITY, self.aphelion_au() * AU, self.albedo)
    }
}
