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

pub struct BinaryAsteroid {
    pub radius_primary: f64,
    pub radius_secondary: f64,
    pub density: f64,
    pub mass_primary: f64,
    pub mass_secondary: f64,
    pub separation: f64,
    pub semi_major_axis_helio: f64,
    pub eccentricity_helio: f64,
    pub eccentricity_mutual: f64,
    pub albedo: f64,
}

impl BinaryAsteroid {
    pub fn new(
        radius_primary_m: f64,
        radius_secondary_m: f64,
        density: f64,
        separation_m: f64,
    ) -> Self {
        let r1 = radius_primary_m.clamp(100.0, MAX_ASTEROID_RADIUS);
        let r2 = radius_secondary_m.clamp(10.0, r1);
        let d = density.clamp(500.0, 8000.0);
        let m1 = sphere_mass(r1, d);
        let m2 = sphere_mass(r2, d);
        let sep = separation_m.max(r1 + r2 + 1.0);

        Self {
            radius_primary: r1,
            radius_secondary: r2,
            density: d,
            mass_primary: m1,
            mass_secondary: m2,
            separation: sep,
            semi_major_axis_helio: 2.5,
            eccentricity_helio: 0.1,
            eccentricity_mutual: 0.0,
            albedo: 0.15,
        }
    }

    pub fn with_heliocentric_orbit(mut self, semi_major_au: f64, ecc: f64) -> Self {
        self.semi_major_axis_helio = semi_major_au.max(0.1);
        self.eccentricity_helio = ecc.clamp(0.0, 0.999);
        self
    }

    pub fn with_mutual_eccentricity(mut self, e: f64) -> Self {
        self.eccentricity_mutual = e.clamp(0.0, 0.9);
        self
    }

    pub fn total_mass(&self) -> f64 {
        self.mass_primary + self.mass_secondary
    }

    pub fn mass_ratio(&self) -> f64 {
        self.mass_secondary / self.mass_primary
    }

    pub fn size_ratio(&self) -> f64 {
        self.radius_secondary / self.radius_primary
    }

    pub fn mutual_orbital_period(&self) -> f64 {
        2.0 * std::f64::consts::PI * (self.separation.powi(3) / (G * self.total_mass())).sqrt()
    }

    pub fn mutual_orbital_velocity_primary(&self) -> f64 {
        let total = self.total_mass();
        let a1 = self.separation * self.mass_secondary / total;
        (G * total / self.separation).sqrt() * a1 / self.separation
    }

    pub fn mutual_orbital_velocity_secondary(&self) -> f64 {
        let total = self.total_mass();
        let a2 = self.separation * self.mass_primary / total;
        (G * total / self.separation).sqrt() * a2 / self.separation
    }

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

    pub fn heliocentric_period_years(&self) -> f64 {
        self.heliocentric_period() / YEAR
    }

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

    pub fn is_stable(&self) -> bool {
        self.separation < self.hill_sphere_radius() / 3.0
    }

    pub fn surface_gravity_primary(&self) -> f64 {
        surface_gravity(self.mass_primary, self.radius_primary)
    }

    pub fn escape_velocity_primary(&self) -> f64 {
        escape_velocity(self.mass_primary, self.radius_primary)
    }

    pub fn roche_limit(&self) -> f64 {
        let rho_ratio = 1.0_f64;
        self.radius_primary * 2.456 * rho_ratio.powf(1.0 / 3.0)
    }

    pub fn is_contact_binary(&self) -> bool {
        self.separation <= self.radius_primary + self.radius_secondary
    }
}
