use super::orbit::{MERCURY_MASS, MERCURY_RADIUS};
use sciforge::hub::domain::common::constants::DEGREE_TO_RAD;

pub struct MercuryRotation {
    pub angular_velocity_rad_s: f64,
    pub axial_tilt_rad: f64,
}

impl Default for MercuryRotation {
    fn default() -> Self {
        Self::new()
    }
}

impl MercuryRotation {
    pub fn new() -> Self {
        Self {
            angular_velocity_rad_s: crate::ANGULAR_VELOCITY,
            axial_tilt_rad: crate::AXIAL_TILT_RAD,
        }
    }

    pub fn surface_velocity_at_latitude(&self, latitude_deg: f64) -> f64 {
        let lat_rad = latitude_deg.to_radians();
        self.angular_velocity_rad_s * MERCURY_RADIUS * lat_rad.cos()
    }

    pub fn centripetal_acceleration_at_latitude(&self, latitude_deg: f64) -> f64 {
        let lat_rad = latitude_deg.to_radians();
        let omega = self.angular_velocity_rad_s;
        omega * omega * MERCURY_RADIUS * lat_rad.cos()
    }

    pub fn coriolis_parameter(&self, latitude_deg: f64) -> f64 {
        let lat_rad = latitude_deg.to_radians();
        2.0 * self.angular_velocity_rad_s * lat_rad.sin()
    }

    pub fn moment_of_inertia(&self) -> f64 {
        crate::MOI_FACTOR * MERCURY_MASS * MERCURY_RADIUS * MERCURY_RADIUS
    }

    pub fn rotational_kinetic_energy(&self) -> f64 {
        0.5 * self.moment_of_inertia() * self.angular_velocity_rad_s.powi(2)
    }

    pub fn angular_momentum(&self) -> f64 {
        self.moment_of_inertia() * self.angular_velocity_rad_s
    }

    pub fn spin_orbit_ratio() -> (u32, u32) {
        (3, 2)
    }

    pub fn libration_amplitude_arcsec() -> f64 {
        crate::LIBRATION_AMPLITUDE_ARCSEC
    }

    pub fn libration_amplitude_rad() -> f64 {
        crate::LIBRATION_AMPLITUDE_ARCSEC / 3600.0 * DEGREE_TO_RAD
    }

    pub fn libration_angle_rad(&self, mean_anomaly_rad: f64) -> f64 {
        Self::libration_amplitude_rad() * mean_anomaly_rad.sin()
    }

    pub fn subsolar_longitude_deg(&self, true_anomaly_rad: f64, elapsed_s: f64) -> f64 {
        let rotation_angle = self.angular_velocity_rad_s * elapsed_s;
        let mut lon = (rotation_angle - true_anomaly_rad).to_degrees() % 360.0;
        if lon < 0.0 {
            lon += 360.0;
        }
        lon
    }

    pub fn solar_day_s() -> f64 {
        crate::SOLAR_DAY_S
    }

    pub fn sidereal_day_s() -> f64 {
        crate::SIDEREAL_DAY_S
    }

    pub fn day_length_hours(&self, latitude_deg: f64) -> f64 {
        let lat_rad = latitude_deg.to_radians();
        let declination = self.axial_tilt_rad;
        let cos_h = -(lat_rad.tan() * declination.tan());
        if cos_h > 1.0 {
            return 0.0;
        }
        if cos_h < -1.0 {
            return 24.0;
        }
        2.0 * cos_h.acos() / std::f64::consts::PI * (crate::SOLAR_DAY_S / 3600.0 / 2.0)
    }
}
