use super::orbit::{MERCURY_MASS, MERCURY_RADIUS};
use sciforge::hub::domain::astronomy::orbits::escape_velocity;
use sciforge::hub::domain::common::constants::G;

const MU_MERCURY: f64 = G * MERCURY_MASS;

pub const MEAN_IMPACT_SPEED_KM_S: f64 = 42.5;
pub const STONY_DENSITY: f64 = 3000.0;
pub const IRON_DENSITY: f64 = 7874.0;
pub const MERCURY_SURFACE_DENSITY: f64 = 3100.0;

pub struct Impactor {
    pub mass_kg: f64,
    pub velocity_m_s: f64,
    pub density_kg_m3: f64,
    pub angle_deg: f64,
}

impl Impactor {
    pub fn asteroid(diameter_m: f64, velocity_km_s: f64) -> Self {
        let density = STONY_DENSITY;
        let radius = diameter_m / 2.0;
        let volume = (4.0 / 3.0) * std::f64::consts::PI * radius.powi(3);
        Self {
            mass_kg: density * volume,
            velocity_m_s: velocity_km_s * 1000.0,
            density_kg_m3: density,
            angle_deg: 45.0,
        }
    }

    pub fn iron_asteroid(diameter_m: f64, velocity_km_s: f64) -> Self {
        let density = IRON_DENSITY;
        let radius = diameter_m / 2.0;
        let volume = (4.0 / 3.0) * std::f64::consts::PI * radius.powi(3);
        Self {
            mass_kg: density * volume,
            velocity_m_s: velocity_km_s * 1000.0,
            density_kg_m3: density,
            angle_deg: 45.0,
        }
    }

    pub fn with_angle(mut self, angle_deg: f64) -> Self {
        self.angle_deg = angle_deg;
        self
    }

    pub fn kinetic_energy_j(&self) -> f64 {
        0.5 * self.mass_kg * self.velocity_m_s.powi(2)
    }

    pub fn kinetic_energy_mt(&self) -> f64 {
        self.kinetic_energy_j() / 4.184e15
    }

    pub fn impact_velocity(&self) -> f64 {
        let v_esc = escape_velocity(MU_MERCURY, MERCURY_RADIUS);
        (self.velocity_m_s.powi(2) + v_esc.powi(2)).sqrt()
    }

    pub fn crater_diameter_m(&self, target_density: f64) -> f64 {
        let g = MU_MERCURY / (MERCURY_RADIUS * MERCURY_RADIUS);
        let angle_rad = self.angle_deg.to_radians();
        let d_proj = (6.0 * self.mass_kg / (std::f64::consts::PI * self.density_kg_m3)).cbrt();
        1.161
            * (self.density_kg_m3 / target_density).powf(1.0 / 3.0)
            * d_proj.powf(0.78)
            * self.velocity_m_s.powf(0.44)
            * g.powf(-0.22)
            * angle_rad.sin().powf(1.0 / 3.0)
    }

    pub fn fireball_radius_m(&self) -> f64 {
        let energy_kt = self.kinetic_energy_j() / 4.184e12;
        55.0 * energy_kt.powf(0.4)
    }

    pub fn ejecta_volume_m3(&self, target_density: f64) -> f64 {
        let d = self.crater_diameter_m(target_density);
        let depth = d / 5.0;
        std::f64::consts::PI / 6.0 * d * d * depth
    }

    pub fn ejecta_escape_fraction(&self) -> f64 {
        let v_esc = escape_velocity(MU_MERCURY, MERCURY_RADIUS);
        let v_mean_ejecta = 0.3 * self.impact_velocity();
        if v_mean_ejecta > v_esc {
            (1.0 - (v_esc / v_mean_ejecta).powi(2)).max(0.0)
        } else {
            0.0
        }
    }
}

pub fn caloris_impactor() -> Impactor {
    Impactor::asteroid(100_000.0, MEAN_IMPACT_SPEED_KM_S)
}

pub fn typical_microimpact() -> Impactor {
    Impactor::asteroid(1.0, MEAN_IMPACT_SPEED_KM_S)
}

pub fn caloris_basin_diameter() -> f64 {
    crate::CALORIS_DIAMETER_M
}

pub fn simple_complex_transition_m() -> f64 {
    10_300.0
}
