//! Component and event definitions for the kinematic physics/collision layer.
//!
//! Per ARCH-7 ("Movement & Collision — No Physics Engine") there is no
//! rigid-body solver: movement is point-and-shoot and collision is
//! distance-based. These components describe *who* participates in collision
//! and proximity queries; the actual resolution is pure position/velocity math
//! living in [`super::systems`] and [`super::geometry`].
//!
//! Collision radii come from [`crate::gameplay::galaxy::BoundingVolume`], which
//! is the single source of truth for "how big is this thing" already attached
//! to every spawned POI. [`BodyMass`] is the physics-owned counterpart for the
//! *momentum* of movable bodies (ships) — it is deliberately separate from the
//! galaxy [`crate::gameplay::galaxy::Massive`] component, which describes
//! gravity-well scenery rather than ship inertia.

use bevy::prelude::*;

/// Mass of a movable body, used to weight collision separation.
///
/// Heavier bodies are displaced less than lighter ones when two movable bodies
/// overlap. Static scenery (planets, stations, the star) has no
/// [`crate::gameplay::movement::components::Velocity`] and is treated as
/// immovable regardless of mass, so it does not need this component.
///
/// Defaults to `1.0`. Values are floored internally to avoid division by zero.
#[derive(Component, Debug, Clone, Copy)]
pub struct BodyMass(pub f32);

impl Default for BodyMass {
    fn default() -> Self {
        Self(1.0)
    }
}

/// A proximity scanner. Each frame the owner queries the world for physical
/// bodies (entities with a [`crate::gameplay::galaxy::BoundingVolume`]) entering
/// or leaving `radius` (world units) and emits [`ProximityEvent`]s for the
/// transitions.
///
/// `occupants` is authoritative per-sensor state managed by
/// [`super::systems::update_proximity_sensors`] — do not edit it manually.
#[derive(Component, Debug)]
pub struct ProximitySensor {
    /// World-space scan radius.
    pub radius: f32,
    /// Bodies currently inside the radius, tracked frame to frame.
    pub occupants: Vec<Entity>,
}

impl ProximitySensor {
    pub fn new(radius: f32) -> Self {
        Self {
            radius,
            occupants: Vec::new(),
        }
    }
}

/// Fired whenever two physical bodies overlap, after positional separation has
/// been *planned* (but before it is applied). `normal` points from `a` toward
/// `b`; `penetration` is the overlap depth in world units.
///
/// Future combat/damage systems read this to apply hit effects; today it is the
/// observable signal that the collision pipeline is running.
#[derive(Event, Debug, Clone, Copy)]
pub struct CollisionEvent {
    pub a: Entity,
    pub b: Entity,
    pub normal: Vec3,
    pub penetration: f32,
}

/// A proximity transition. Emitted by [`ProximitySensor`]s when a body enters or
/// leaves the scan radius.
#[derive(Event, Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProximityEvent {
    /// `sensor` detected `other` entering its radius this frame.
    Enter { sensor: Entity, other: Entity },
    /// `other` left the sensor's radius this frame.
    Exit { sensor: Entity, other: Entity },
}
