//! The [`Module`] trait and the engine's built-in modules.
//!
//! A module is the unit of engine extension: it bundles systems, component
//! types, asset loaders, and resources behind one documented entry point, so
//! anyone — including AI agents — can add a capability by writing a module, and
//! an exported game compiles in only the modules it registers. The editor
//! integration half of the trait arrives in Stage 6.

use super::{App, ModuleBundle, Schedule};
use crate::layer::{Layer, Tags};
use crate::math::Transform;
use crate::render::MeshRenderer;
use crate::scene::Node;

/// A self-contained unit of engine functionality.
///
/// Implement [`build`](Self::build) to register everything the module provides
/// via the [`App`] facade ([`add_system`](App::add_system),
/// [`register_type`](App::register_type), [`add_loader`](App::add_loader),
/// [`insert_resource`](App::insert_resource)). Everything registered during
/// `build` is attributed to the module, so it can be enabled, disabled, or
/// removed as a unit.
pub trait Module: 'static {
    /// A stable, unique name (used to enable/disable/remove the module and, in
    /// later stages, to express dependencies).
    fn name(&self) -> &'static str;

    /// Registers the module's systems, types, loaders, and resources on `app`.
    fn build(&self, app: &mut App);
}

/// The core module: registers the always-present scene component types for
/// reflection (dual-editability), so the editor and scripts can address them.
///
/// This is the runtime "wrapper" for the math/scene/layer building blocks that
/// already exist as plain library types — it does not add behavior, it exposes
/// those types through the [`TypeRegistry`](crate::reflect::TypeRegistry).
pub struct CoreModule;

impl Module for CoreModule {
    fn name(&self) -> &'static str {
        "core"
    }

    fn build(&self, app: &mut App) {
        app.register_type::<Transform>("Transform");
        app.register_type::<Node>("Node");
        app.register_type::<Layer>("Layer");
        app.register_type::<Tags>("Tags");
    }
}

/// The render module: registers the renderable scene components for reflection.
///
/// The forward renderer itself is driven by the editor/host today; this module
/// is what makes [`MeshRenderer`] a first-class, dual-editable component. As the
/// data-driven render pipeline grows it will register its render-phase systems
/// here too.
pub struct RenderModule;

impl Module for RenderModule {
    fn name(&self) -> &'static str {
        "render"
    }

    fn build(&self, app: &mut App) {
        app.register_type::<MeshRenderer>("MeshRenderer");
        // Placeholder render-phase system so the phase is exercised; real passes
        // land with the Stage 5 render pipeline piece.
        app.add_system(Schedule::Render, |_app| {});
    }
}

/// The engine's standard set of built-in modules, added with
/// [`App::add_modules`](super::App::add_modules).
///
/// ```
/// use oxide_engine::app::{App, DefaultModules};
/// let mut app = App::new();
/// app.add_modules(DefaultModules);
/// assert!(app.has_module("core") && app.has_module("render"));
/// ```
pub struct DefaultModules;

impl ModuleBundle for DefaultModules {
    fn add_to(self, app: &mut App) {
        app.add_module(CoreModule);
        app.add_module(RenderModule);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::render::PrimitiveShape;

    #[test]
    fn default_modules_register_core_types() {
        let mut app = App::new();
        app.add_modules(DefaultModules);
        assert!(app.has_module("core"));
        assert!(app.has_module("render"));
        assert!(app.types.is_registered("Transform"));
        assert!(app.types.is_registered("MeshRenderer"));
        assert_eq!(app.modules().collect::<Vec<_>>(), vec!["core", "render"]);
    }

    #[test]
    fn removing_a_module_removes_its_contributions() {
        let mut app = App::new();
        app.add_modules(DefaultModules);
        assert!(app.types.is_registered("MeshRenderer"));
        let systems_before = app.system_count();

        assert!(app.remove_module("render"));
        // Its registered type is gone, its render system is gone, core remains.
        assert!(!app.has_module("render"));
        assert!(!app.types.is_registered("MeshRenderer"));
        assert!(app.types.is_registered("Transform"));
        assert!(app.system_count() < systems_before);
    }

    #[test]
    fn disabling_a_module_skips_its_systems_without_removing() {
        use std::cell::Cell;
        use std::rc::Rc;

        struct Ticker(Rc<Cell<u32>>);
        impl Module for Ticker {
            fn name(&self) -> &'static str {
                "ticker"
            }
            fn build(&self, app: &mut App) {
                let counter = self.0.clone();
                app.add_system(Schedule::Update, move |_| counter.set(counter.get() + 1));
            }
        }

        let count = Rc::new(Cell::new(0u32));
        let mut app = App::new();
        app.add_module(Ticker(count.clone()));

        app.update(0.0);
        assert_eq!(count.get(), 1);

        app.set_module_enabled("ticker", false);
        app.update(0.0); // skipped
        assert_eq!(count.get(), 1);

        app.set_module_enabled("ticker", true);
        app.update(0.0); // runs again
        assert_eq!(count.get(), 2);
    }

    #[test]
    fn a_module_can_add_a_loader_removed_with_it() {
        // A module registers MeshRenderer + uses a primitive, then is removed.
        let mut app = App::new();
        app.add_module(RenderModule);
        // Sanity: the primitive enum the render component references is usable.
        assert_eq!(PrimitiveShape::ALL.len(), 3);
        assert!(app.remove_module("render"));
        assert!(!app.types.is_registered("MeshRenderer"));
    }
}
