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

#[derive(Clone, Copy, Debug)]
pub enum SpectralType {
    C,
    S,
    M,
    B,
    D,
    V,
    X,
}

pub struct MainBeltAsteroid {
    pub radius: f64,
    pub density: f64,
    pub mass: f64,
    pub semi_major_axis: f64,
    pub eccentricity: f64,
    pub inclination: f64,
    pub albedo: f64,
    pub spectral_type: SpectralType,
    pub rotation_period: f64,
}

impl MainBeltAsteroid {
    pub fn new(radius_m: f64, density: f64, semi_major_au: 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 semi_major_axis = semi_major_au.clamp(2.0, 3.5);

        let spectral_type = if semi_major_axis < 2.5 {
            SpectralType::S
        } else if semi_major_axis < 3.0 {
            SpectralType::C
        } else {
            SpectralType::D
        };

        let albedo = match spectral_type {
            SpectralType::C | SpectralType::B | SpectralType::D => 0.06,
            SpectralType::S | SpectralType::V => 0.20,
            SpectralType::M | SpectralType::X => 0.12,
        };

        Self {
            radius,
            density,
            mass,
            semi_major_axis,
            eccentricity: 0.1,
            inclination: 0.0,
            albedo,
            spectral_type,
            rotation_period: 8.0 * 3600.0,
        }
    }

    pub fn with_eccentricity(mut self, e: f64) -> Self {
        self.eccentricity = e.clamp(0.0, 0.5);
        self
    }

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

    pub fn with_spectral_type(mut self, st: SpectralType) -> Self {
        self.spectral_type = st;
        self.albedo = match st {
            SpectralType::C | SpectralType::B | SpectralType::D => 0.06,
            SpectralType::S | SpectralType::V => 0.20,
            SpectralType::M | SpectralType::X => 0.12,
        };
        self
    }

    pub fn with_rotation_period(mut self, hours: f64) -> Self {
        self.rotation_period = hours.max(0.1) * 3600.0;
        self
    }

    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 is_in_kirkwood_gap(&self) -> bool {
        let a = self.semi_major_axis;
        (a - 2.502).abs() < 0.02
            || (a - 2.825).abs() < 0.02
            || (a - 2.958).abs() < 0.02
            || (a - 3.279).abs() < 0.02
    }

    pub fn spectral_name(&self) -> &'static str {
        match self.spectral_type {
            SpectralType::C => "C-type",
            SpectralType::S => "S-type",
            SpectralType::M => "M-type",
            SpectralType::B => "B-type",
            SpectralType::D => "D-type",
            SpectralType::V => "V-type",
            SpectralType::X => "X-type",
        }
    }

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

    pub fn rotation_frequency(&self) -> f64 {
        1.0 / self.rotation_period
    }

    pub fn centrifugal_limit_density(&self) -> f64 {
        3.0 * std::f64::consts::PI / (G * self.rotation_period * self.rotation_period)
    }

    pub fn hill_sphere_radius(&self) -> f64 {
        hill_sphere(self.semi_major_axis * AU, self.mass, SOLAR_MASS)
    }

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