//! Points of interest (POIs): components, marker tags, and events.
//!
//! In Bevy, entity "capabilities" are expressed as **data components** that
//! systems query — not methods on objects. This file defines the data layout;
//! behavior lives in systems (`mining_system`, `scan_system`, …) that read and
//! write these components and react to events.
//!
//! ## Anatomy of a POI
//!
//! Every POI entity gets:
//!
//! 1. **One variant marker** (e.g. [`Planet`], [`AsteroidBelt`], [`Wormhole`]).
//!    Markers use Bevy 0.16 `#[require(...)]` to declare the components they
//!    are composed of. Bevy enforces this at spawn time — you can't make a
//!    `Planet` without also providing `Orbital`, `Massive`, etc.
//! 2. **All required components** declared by the marker, either with explicit
//!    values or `Default::default()`.
//! 3. **Optional capability components** added dynamically — e.g. a `Planet`
//!    that gets colonized gains [`Inhabited`]; a [`Wormhole`] that destabilizes
//!    loses [`Transit`].
//!
//! World-space position comes from Bevy's built-in `Transform`/`GlobalTransform`
//! — there is no separate `Positioned` component.
//!
//! ## Why components, not traits
//!
//! - **Queries are how Bevy dispatches**: `Query<&Mineable, With<AsteroidBelt>>`
//!   is the equivalent of a downcast, but vectorized, cache-friendly, and
//!   parallelizable across the schedule.
//! - **Composition over inheritance**: a POI's behavior is the sum of the
//!   components attached to it. Adding/removing a capability at runtime is
//!   `commands.insert(...)` / `commands.remove::<...>()`.
//! - **State changes flow through events** ([`Mined`], [`Scanned`], [`Depleted`],
//!   …). Systems react rather than invoke. Multiple systems can observe the
//!   same event (UI, audio, mission tracking, achievements) without coupling.
//! - **`Reflect` enables save/load, hot-reload, and editor inspectors** for
//!   free. Trait objects can't do this without manual plumbing.

use bevy::prelude::*;
use bevy::reflect::Reflect;
use std::time::Duration;

// ─── Supporting enums ───────────────────────────────────────────────────────

/// EVE-style sensor quartet — drives which probe type can resolve a signal.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect, Default)]
pub enum SignalKind {
    #[default]
    Infrared,
    Gravimetric,
    Radar,
    Magnetometric,
}

/// Difficulty of resolving a scannable target with probes.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect, Default)]
pub enum Difficulty {
    #[default]
    Trivial,
    Easy,
    Moderate,
    Hard,
    /// Requires multi-probe triangulation.
    Expert,
}

/// High-level classification shown on HUD / sensor panels.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect, Default)]
pub enum Classification {
    #[default]
    Celestial,
    Structure,
    Anomaly,
    Signature,
    Ship,
    Debris,
}

/// Damage kind for environmental hazards.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect, Default)]
pub enum DamageKind {
    #[default]
    Radiation,
    Emp,
    Heat,
    Kinetic,
}

/// Harvestable gas varieties.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect, Default)]
pub enum GasKind {
    #[default]
    Hydrogen,
    Helium,
    Nitrogen,
    /// Rare/anomalous gas — typically only found in anomalies.
    Exotic,
}

/// What triggers an NPC spawn at a POI.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect, Default)]
pub enum SpawnTrigger {
    /// Player crosses the POI's "aggro bubble".
    #[default]
    OnPlayerEnter,
    /// World tick — happens regardless of player presence.
    WorldTick,
    /// Triggered by an event or mission script.
    EventDriven,
}

/// Kind of interaction a player can initiate with an [`Interactable`] POI.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect, Default)]
pub enum InteractionKind {
    #[default]
    Dialogue,
    Lore,
    Decode,
    Investigate,
}

// ─── Placeholder types (promote to real modules when consumers arrive) ──────

pub type SystemId = String;

// ─── 1. Spatial / physical ──────────────────────────────────────────────────

/// A body that orbits a parent entity (star, planet, moon, belt center).
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)]
pub struct Orbital {
    pub semi_major_axis: f32,
    pub period: f32,
    pub phase: f32,
    /// Parent entity — the body being orbited. Override
    /// `Default::default()` (`Entity::PLACEHOLDER`) at spawn time.
    pub parent: Entity,
}

impl Default for Orbital {
    fn default() -> Self {
        Self {
            semi_major_axis: 0.0,
            period: 0.0,
            phase: 0.0,
            parent: Entity::PLACEHOLDER,
        }
    }
}

/// Spherical bound — used for collision, camera framing, sensor-blip scaling.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct BoundingVolume {
    pub radius: f32,
}

/// Massive body — drives gravity wells and (with [`MassLock`]) warp disruption.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct Massive {
    pub mass: f32,
}

// ─── 2. Detection / sensors ─────────────────────────────────────────────────

/// Passive sensor profile of a POI.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct SensorSignature {
    pub signature_radius: f32,
    pub signal_type: SignalKind,
}

/// A POI that can be pinned down with scan probes.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct Scannable {
    pub difficulty: Difficulty,
    /// If true, shipboard sensors aren't enough — a probe must be launched.
    pub requires_probe: bool,
}

/// Stable identity + display metadata for HUDs and target lists.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct Identifiable {
    pub id: String,
    pub display_name: String,
    pub classification: Classification,
}

// ─── 3. Navigation / approach ───────────────────────────────────────────────

/// A POI the ship can warp to.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct WarpTarget {
    pub warp_distance: f32,
    pub landing_radius: f32,
}

/// A POI the ship can dock with.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct Dockable {
    pub dock_range: f32,
}

/// A POI that moves the ship to another system (wormhole, jump gate).
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct Transit {
    pub destination: Option<SystemId>,
    /// `None` = permanent. `Some(d)` = expires after `d`.
    pub lifetime: Option<Duration>,
}

/// Massive enough to prevent warp within a radius. Stars, gas giants, etc.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct MassLock {
    pub radius: f32,
}

// ─── 4. Resource / harvesting ───────────────────────────────────────────────

/// Solid resource deposit — ore in asteroids, minerals in planetoids.
///
/// Depletion happens via [`Mined`] events in a system, not via a method.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct Mineable {
    //Add loot table later
    pub yield_remaining: f32,
}

/// Gaseous resource — scoopable by harvesters.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct Harvestable {
    pub resource: GasKind,
    /// Units per second a harvester can extract under ideal conditions.
    pub flow_rate: f32,
}

/// Wreck or ruin that can be stripped for components.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct Salvageable {
    // add loot table later
    pub condition: f32,
}

/// A POI whose resources can run out. Usually composed with one of the
/// harvesting components above.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct Exhaustible {
    pub depleted: bool,
    /// `None` = never respawns. `Some(d)` = replenished after `d`.
    pub respawn_after: Option<Duration>,
}

// ─── 5. Hazards ─────────────────────────────────────────────────────────────

/// Deals passive damage to anything within range.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct Hazardous {
    pub damage_per_sec: f32,
    pub damage_type: DamageKind,
}

/// Reduces effective sensor / probe range for nearby ships.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct SensorJamming {
    /// Strength of the jamming field. Higher = shorter effective range.
    pub strength: f32,
}

/// Tag component — entity is currently cloaked. Pair with [`CloakField`] for
/// the decloak radius.
#[derive(Component, Debug, Clone, Copy, Reflect, Default)]
#[reflect(Component)]
pub struct Cloaked;

#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct CloakField {
    /// Distance at which the cloak flickers / drops.
    pub decloak_radius: f32,
}

// ─── 6. Habitability / story ────────────────────────────────────────────────

/// Emits light — typically a star, used to compute habitable zones for
/// orbiting planets.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct Luminosity {
    pub value: f32,
}

/// Tag — this body can support life.
#[derive(Component, Debug, Clone, Copy, Reflect, Default)]
#[reflect(Component)]
pub struct Habitable;

/// Tag — this is a planet that can be docked to (e.g., terrestrial or oceanic).
/// Gas giants, ice giants, lava worlds, etc. should not have this component.
#[derive(Component, Debug, Clone, Copy, Reflect, Default)]
#[reflect(Component)]
pub struct HabitablePlanet;

/// Tag — this body currently hosts a population.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct Inhabited {
    pub population: u32,
}

/// Player can trigger a non-combat interaction (dialogue, lore, decode, …).
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct Interactable {
    pub kind: InteractionKind,
}

// ─── 7. World simulation ────────────────────────────────────────────────────

/// Spawns hostile NPCs under some condition.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct SpawnsHostiles; //implement later

/// A POI with a finite lifespan — anomaly, wormhole, world event.
#[derive(Component, Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct EventTimer {
    pub remaining: Duration,
}

// ─── POI variant markers ────────────────────────────────────────────────────
//
// Replace the old `PointOfInterest` enum. Each marker declares the components
// it requires via `#[require(...)]` — Bevy enforces composition at spawn time.
//
// Spawn example:
//
//     commands.spawn((
//         Planet,
//         Orbital { semi_major_axis: 5.0, period: 365.0, phase: 0.0, parent: sun },
//         BoundingVolume { radius: 6371.0 },
//         Massive { mass: 5.97e24 },
//         Identifiable { id: "sol-3".into(), display_name: "Earth".into(), ..default() },
//     ));
//
// `WarpTarget`, `MassLock`, etc. are auto-inserted via `Default` since the
// marker requires them.
//
// Note: stars are NOT POIs — they are the anchor body of a star system and
// live next to [`super::StarSystem`] in `mod.rs`.

#[derive(Component, Debug, Clone, Copy, Reflect)]
#[reflect(Component)]
#[require(Orbital, Massive, BoundingVolume, WarpTarget, MassLock, Dockable, Identifiable)]
pub struct Planet;

#[derive(Component, Debug, Clone, Copy, Reflect)]
#[reflect(Component)]
#[require(
    Orbital,
    BoundingVolume,
    WarpTarget,
    Mineable,
    Exhaustible,
    Identifiable
)]
pub struct AsteroidBelt;

#[derive(Component, Debug, Clone, Copy, Reflect)]
#[reflect(Component)]
#[require(BoundingVolume, WarpTarget, Harvestable, Exhaustible, Identifiable)]
pub struct GasCloud;

#[derive(Component, Debug, Clone, Copy, Reflect)]
#[reflect(Component)]
#[require(
    BoundingVolume,
    WarpTarget,
    SensorSignature,
    Scannable,
    EventTimer,
    Identifiable
)]
pub struct Anomaly;

#[derive(Component, Debug, Clone, Copy, Reflect)]
#[reflect(Component)]
#[require(
    BoundingVolume,
    WarpTarget,
    Transit,
    SensorSignature,
    EventTimer,
    Identifiable
)]
pub struct Wormhole;

/// A constructed jump gate — paired with another stargate in the destination
/// system. Distinct from [`Wormhole`] (which is a natural, transient phenomenon).
#[derive(Component, Debug, Clone, Copy, Reflect)]
#[reflect(Component)]
#[require(BoundingVolume, WarpTarget, Transit, Identifiable)]
pub struct Stargate;

/// A functioning, inhabited station (docking, market, missions).
/// Distinct from [`DerelictStation`] which is a wreck you can salvage.
#[derive(Component, Debug, Clone, Copy, Reflect)]
#[reflect(Component)]
#[require(BoundingVolume, WarpTarget, Dockable, Identifiable)]
pub struct Station;

#[derive(Component, Debug, Clone, Copy, Reflect)]
#[reflect(Component)]
#[require(
    BoundingVolume,
    WarpTarget,
    SensorSignature,
    Scannable,
    Salvageable,
    Identifiable
)]
pub struct DerelictStation;

// ─── Events ─────────────────────────────────────────────────────────────────
//
// State changes that should be observed by other systems. Prefer events over
// direct mutation when:
//   - Multiple systems might react (UI, audio, mission tracking, achievements).
//   - You want to decouple the producer from the consumer.

/// A miner extracted `amount` from `deposit`. Mining systems read this and
/// mutate `&mut Mineable`; UI plays the yield popup; audio plays the laser hit.
#[derive(Event, Debug, Clone)]
pub struct Mined {
    pub deposit: Entity,
    pub miner: Entity,
    pub amount: f32,
}

#[derive(Event, Debug, Clone)]
pub struct Harvested {
    pub source: Entity,
    pub harvester: Entity,
    pub kind: GasKind,
    pub amount: f32,
}

#[derive(Event, Debug, Clone)]
pub struct Salvaged {
    pub target: Entity,
    pub salvager: Entity,
}

/// A scan completed successfully — the target is now revealed to the scanner.
#[derive(Event, Debug, Clone)]
pub struct Scanned {
    pub target: Entity,
    pub scanner: Entity,
}

/// A resource deposit (or anomaly, or wormhole) has been fully consumed.
/// Systems react by clearing blips, queueing respawn timers, etc.
#[derive(Event, Debug, Clone)]
pub struct Depleted {
    pub entity: Entity,
}

#[derive(Event, Debug, Clone)]
pub struct Docked {
    pub ship: Entity,
    pub station: Entity,
}

/// A ship used a [`Transit`] POI (wormhole, jump gate) to change systems.
#[derive(Event, Debug, Clone)]
pub struct Transited {
    pub traveler: Entity,
    pub gate: Entity,
    pub destination: SystemId,
}
