//! Type-erased singleton resource storage.
//!
//! [`World`] is a unified store where each resource type gets a direct pointer
//! ([`ResourceId`]) for O(1) dispatch-time access. Registration happens through
//! [`WorldBuilder`], which freezes into an immutable [`World`] container via
//! [`build()`](WorldBuilder::build).
//!
//! The type [`Registry`] maps types to [`ResourceId`] pointers. It is shared
//! between [`WorldBuilder`] and [`World`], and is passed to [`Param::init`] and
//! [`IntoHandler::into_handler`](crate::IntoHandler::into_handler) so that handlers can resolve their parameter
//! state during driver setup — before or after `build()`.
//!
//! # Lifecycle
//!
//! ```text
//! let mut builder = WorldBuilder::new();
//! builder.register::<PriceCache>(value);
//! builder.register::<TimerDriver>(value);
//!
//! // Drivers can resolve handlers against builder.registry()
//! // before World is built.
//!
//! let world = builder.build();  // → World (frozen)
//! ```
//!
//! After `build()`, the container is frozen — no inserts, no removes. All
//! [`ResourceId`] values are valid for the lifetime of the [`World`] container.

use std::any::{TypeId, type_name};
use std::cell::Cell;
#[cfg(debug_assertions)]
use std::cell::UnsafeCell;
use std::marker::PhantomData;
use std::ptr::NonNull;

use rustc_hash::FxHashMap;

// =============================================================================
// Debug-mode aliasing detection
// =============================================================================

/// Tracks resource accesses within a single Param::fetch phase to detect
/// aliasing violations at runtime in debug builds.
///
/// Dispatch macros call [`World::clear_borrows`] then
/// [`World::track_borrow`] for each resource fetched. If the same resource
/// is fetched twice within a phase, we panic with a diagnostic. This catches
/// framework bugs where two params in the same handler resolve to the same
/// resource — something [`Registry::check_access`] catches at construction
/// time, but this catches dynamically for dispatch paths (like [`Opaque`](crate::Opaque)
/// closures) that bypass static analysis.
///
/// Only active during the narrow `Param::fetch` window — safe API methods
/// are not tracked.
///
/// Completely compiled out in release builds — zero bytes, zero branches.
#[cfg(debug_assertions)]
pub(crate) struct BorrowTracker {
    /// Pointer addresses accessed in current phase.
    /// Uses `UnsafeCell` for interior mutability because `Param::fetch` /
    /// `World::track_borrow` operate on `&World`. Single-threaded,
    /// non-reentrant access only.
    accessed: UnsafeCell<Vec<NonNull<u8>>>,
}

#[cfg(debug_assertions)]
impl BorrowTracker {
    fn new() -> Self {
        Self {
            accessed: UnsafeCell::new(Vec::new()),
        }
    }

    /// Reset all tracking state. Called before each `Param::fetch` phase.
    fn clear(&self) {
        // SAFETY: single-threaded, non-reentrant. No other reference to
        // the inner Vec exists during this call.
        let ptrs = unsafe { &mut *self.accessed.get() };
        ptrs.clear();
    }

    /// Record an access. Panics if already accessed in this phase.
    fn track(&self, id: ResourceId) {
        // SAFETY: single-threaded, non-reentrant. No other reference to
        // the inner Vec exists during this call.
        let ptrs = unsafe { &mut *self.accessed.get() };
        assert!(
            !ptrs.contains(&id.0),
            "conflicting access: resource {id} was accessed by more than one parameter \
             in the same dispatch phase",
        );
        ptrs.push(id.0);
    }
}

// =============================================================================
// Core types
// =============================================================================

/// Direct pointer identifying a resource within a [`World`] container.
///
/// Points to a heap-allocated `ResourceCell<T>` whose `changed_at` field
/// is at offset 0 (`#[repr(C)]`). Dispatch-time access is a single deref —
/// no index lookup, no Vec indirection.
///
/// Obtained from [`WorldBuilder::register`], [`WorldBuilder::ensure`],
/// [`Registry::id`], [`World::id`], or their `try_` / `_default` variants.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct ResourceId(NonNull<u8>);

impl ResourceId {
    fn as_ptr(self) -> *mut u8 {
        self.0.as_ptr()
    }
}

// SAFETY: `ResourceId` is a thin, copyable handle to a `ResourceCell<T>`
// allocated and pinned for the lifetime of its `World`:
// - every `ResourceCell<T>` is registered with `T: Send`, so the pointee is
//   safe to send between threads,
// - the underlying pointer is stable for the `World`'s entire lifetime, and
// - a `ResourceId` cannot be dereferenced without going through `World`,
//   which enforces the single-threaded dispatch / aliasing invariants.
unsafe impl Send for ResourceId {}

impl std::fmt::Display for ResourceId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:p}", self.0)
    }
}

/// Monotonic event sequence number for change detection.
///
/// Each event processed by a driver is assigned a unique sequence number
/// via [`World::next_sequence`]. Resources record the sequence at which
/// they were last written. A resource is considered "changed" if its
/// `changed_at` equals the world's `current_sequence`.
///
/// Wrapping is harmless — at one increment per event, the `u64` space
/// takes ~584 years at 1 GHz to exhaust. Ordering comparisons use natural
/// `u64` ordering (not wrapping-aware), which is correct for any realistic
/// uptime.
///
/// # Examples
///
/// ```
/// use nexus_rt::Sequence;
///
/// let a = Sequence::ZERO;
/// let b = a.next();
///
/// assert!(b > a);
/// assert_eq!(b.get(), 1);
/// assert_eq!(b.elapsed_since(a), 1);
/// ```
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct Sequence(pub(crate) u64);

impl Sequence {
    /// The zero sequence — the starting point before any events.
    pub const ZERO: Self = Self(0);

    /// Create a sequence from a raw `u64` value.
    ///
    /// Use for construction in tests, deserialization, or replay.
    pub const fn new(value: u64) -> Self {
        Self(value)
    }

    /// Returns the raw `u64` value.
    ///
    /// Use for logging, metrics, or passing to external systems.
    pub const fn get(self) -> u64 {
        self.0
    }

    /// Returns the next sequence number (wrapping).
    ///
    /// This is a pure computation — it does not advance any world state.
    /// Use [`World::next_sequence`] to actually advance the world's
    /// current sequence.
    pub const fn next(self) -> Self {
        Self(self.0.wrapping_add(1))
    }

    /// Returns the number of events between `earlier` and `self`.
    ///
    /// Wrapping-aware: if `self` has wrapped past `earlier`, the result
    /// is the wrapping distance. Returns 0 if `self == earlier`.
    pub const fn elapsed_since(self, earlier: Self) -> u64 {
        self.0.wrapping_sub(earlier.0)
    }
}

impl std::fmt::Display for Sequence {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}

/// Inline value + change tick for a single resource.
///
/// `changed_at` at offset 0 is critical — it lets `stamp_changed` and
/// `changed_at` work without knowing T. Cast the pointer to
/// `*const Cell<Sequence>` and read/write directly.
#[repr(C)]
pub(crate) struct ResourceCell<T> {
    pub(crate) changed_at: Cell<Sequence>,
    pub(crate) value: T,
}

// changed_at MUST be at offset 0 — changed_at(), changed_at_cell(), and
// stamp_changed() cast ResourceId's pointer directly to *const Cell<Sequence>
// without knowing T. repr(C) guarantees this, but let's be explicit.
const _: () = assert!(std::mem::offset_of!(ResourceCell<()>, changed_at) == 0);

/// Reconstruct and drop a `Box<ResourceCell<T>>` from a raw pointer.
///
/// # Safety
///
/// `ptr` must have been produced by `Box::into_raw(Box::new(ResourceCell { .. }))`
/// where the value field is `T`. Must only be called once per pointer.
unsafe fn drop_resource<T>(ptr: *mut u8) {
    // SAFETY: ptr was produced by Box::into_raw(Box::new(ResourceCell { .. }))
    // where value: T. Called exactly once in Storage::drop.
    unsafe {
        let _ = Box::from_raw(ptr as *mut ResourceCell<T>);
    }
}

// =============================================================================
// Registry — type-to-pointer mapping
// =============================================================================

/// Type-to-pointer mapping shared between [`WorldBuilder`] and [`World`].
///
/// Contains only the type registry — no storage backing. Passed to
/// [`IntoHandler::into_handler`](crate::IntoHandler::into_handler) and
/// [`Param::init`](crate::Param::init) so handlers can resolve
/// [`ResourceId`]s during driver setup.
///
/// Obtained via [`WorldBuilder::registry()`] or [`World::registry()`].
pub struct Registry {
    indices: FxHashMap<TypeId, ResourceId>,
}

impl Registry {
    pub(crate) fn new() -> Self {
        Self {
            indices: FxHashMap::default(),
        }
    }

    /// Resolve the [`ResourceId`] for a type. Cold path — uses HashMap lookup.
    ///
    /// # Panics
    ///
    /// Panics if the resource type was not registered.
    pub fn id<T: 'static>(&self) -> ResourceId {
        *self
            .indices
            .get(&TypeId::of::<T>())
            .unwrap_or_else(|| panic!("resource `{}` not registered", type_name::<T>()))
    }

    /// Try to resolve the [`ResourceId`] for a type. Returns `None` if the
    /// type was not registered.
    pub fn try_id<T: 'static>(&self) -> Option<ResourceId> {
        self.indices.get(&TypeId::of::<T>()).copied()
    }

    /// Returns `true` if a resource of type `T` has been registered.
    pub fn contains<T: 'static>(&self) -> bool {
        self.indices.contains_key(&TypeId::of::<T>())
    }

    /// Returns the number of registered resources.
    pub fn len(&self) -> usize {
        self.indices.len()
    }

    /// Returns `true` if no resources have been registered.
    pub fn is_empty(&self) -> bool {
        self.indices.is_empty()
    }

    /// Validate that a set of parameter accesses don't conflict.
    ///
    /// Two accesses conflict when they target the same ResourceId (same
    /// pointer). O(n²) pairwise comparison — handler arity is 1-8, so
    /// this is trivially fast at build time.
    ///
    /// # Panics
    ///
    /// Panics if any resource is accessed by more than one parameter.
    #[cold]
    pub fn check_access(&self, accesses: &[(Option<ResourceId>, &str)]) {
        for i in 0..accesses.len() {
            let Some(id_i) = accesses[i].0 else { continue };
            for j in (i + 1)..accesses.len() {
                let Some(id_j) = accesses[j].0 else { continue };
                assert!(
                    id_i != id_j,
                    "conflicting access: resource borrowed by `{}` conflicts with \
                     resource borrowed by `{}` in the same handler",
                    accesses[j].1,
                    accesses[i].1,
                );
            }
        }
    }
}

// =============================================================================
// Storage — shared backing between builder and frozen container
// =============================================================================

/// Pointer + type-erased drop function for a single resource.
struct DropEntry {
    ptr: *mut u8,
    drop_fn: unsafe fn(*mut u8),
}

/// Internal storage for type-erased resource cleanup.
///
/// Only walked during [`World::drop`] — no dispatch-time role. The actual
/// resource data lives in individually heap-allocated `ResourceCell<T>`
/// values, pointed to by [`ResourceId`].
pub(crate) struct Storage {
    drop_entries: Vec<DropEntry>,
}

impl Storage {
    pub(crate) fn new() -> Self {
        Self {
            drop_entries: Vec::new(),
        }
    }

    pub(crate) fn len(&self) -> usize {
        self.drop_entries.len()
    }

    pub(crate) fn is_empty(&self) -> bool {
        self.drop_entries.is_empty()
    }
}

// SAFETY: All values stored in Storage were registered via `register<T: Send + 'static>`,
// so every concrete type behind the raw pointers is Send. Storage exclusively owns
// these heap allocations — they are not aliased or shared. Transferring ownership
// to another thread is safe.
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Send for Storage {}

impl Drop for Storage {
    fn drop(&mut self) {
        for entry in &self.drop_entries {
            // SAFETY: each DropEntry was created in WorldBuilder::register().
            // drop_fn is the monomorphized destructor for the concrete
            // ResourceCell<T> behind ptr. Called exactly once here.
            unsafe {
                (entry.drop_fn)(entry.ptr);
            }
        }
    }
}

// =============================================================================
// WorldBuilder
// =============================================================================

/// Builder for registering resources before freezing into a [`World`] container.
///
/// Each resource type can only be registered once. Registration assigns a
/// direct [`ResourceId`] pointer.
///
/// The [`registry()`](Self::registry) method exposes the type-to-pointer mapping
/// so that drivers can resolve handlers against the builder before `build()`.
///
/// # Examples
///
/// ```
/// use nexus_rt::WorldBuilder;
///
/// let mut builder = WorldBuilder::new();
/// let id = builder.register::<u64>(42);
/// builder.register::<bool>(true);
/// let world = builder.build();
///
/// unsafe {
///     assert_eq!(*world.get::<u64>(id), 42);
/// }
/// ```
pub struct WorldBuilder {
    registry: Registry,
    storage: Storage,
}

impl WorldBuilder {
    /// Create an empty builder.
    pub fn new() -> Self {
        Self {
            registry: Registry::new(),
            storage: Storage::new(),
        }
    }

    /// Register a resource and return its [`ResourceId`].
    ///
    /// The value is heap-allocated inside a `ResourceCell<T>` and ownership
    /// is transferred to the container. The pointer is stable for the
    /// lifetime of the resulting [`World`].
    ///
    /// # Panics
    ///
    /// Panics if a resource of the same type is already registered.
    #[cold]
    pub fn register<T: Send + 'static>(&mut self, value: T) -> ResourceId {
        let type_id = TypeId::of::<T>();
        assert!(
            !self.registry.indices.contains_key(&type_id),
            "resource `{}` already registered",
            type_name::<T>(),
        );

        let cell = Box::new(ResourceCell {
            changed_at: Cell::new(Sequence(0)),
            value,
        });
        let raw = Box::into_raw(cell) as *mut u8;
        // SAFETY: Box::into_raw never returns null.
        let ptr = unsafe { NonNull::new_unchecked(raw) };
        let id = ResourceId(ptr);
        self.registry.indices.insert(type_id, id);
        self.storage.drop_entries.push(DropEntry {
            ptr: raw,
            drop_fn: drop_resource::<T>,
        });
        id
    }

    /// Register a resource using its [`Default`] value and return its
    /// [`ResourceId`].
    ///
    /// Equivalent to `self.register::<T>(T::default())`.
    #[cold]
    pub fn register_default<T: Default + Send + 'static>(&mut self) -> ResourceId {
        self.register(T::default())
    }

    /// Ensure a resource is registered, returning its [`ResourceId`].
    ///
    /// If the type is already registered, returns the existing ID and
    /// drops `value`. If not, registers it and returns the new ID.
    ///
    /// Use [`register`](Self::register) when duplicate registration is a
    /// bug that should panic. Use `ensure` when multiple plugins or
    /// drivers may independently need the same resource type.
    #[cold]
    pub fn ensure<T: Send + 'static>(&mut self, value: T) -> ResourceId {
        if let Some(id) = self.registry.try_id::<T>() {
            return id;
        }
        self.register(value)
    }

    /// Ensure a resource is registered using its [`Default`] value,
    /// returning its [`ResourceId`].
    ///
    /// If the type is already registered, returns the existing ID.
    /// If not, registers `T::default()` and returns the new ID.
    #[cold]
    pub fn ensure_default<T: Default + Send + 'static>(&mut self) -> ResourceId {
        if let Some(id) = self.registry.try_id::<T>() {
            return id;
        }
        self.register(T::default())
    }

    /// Returns a shared reference to the type registry.
    ///
    /// Use this for construction-time calls like
    /// [`into_handler`](crate::IntoHandler::into_handler),
    /// [`into_callback`](crate::IntoCallback::into_callback), and
    /// [`into_step`](crate::IntoStep::into_step).
    pub fn registry(&self) -> &Registry {
        &self.registry
    }

    /// Returns a mutable reference to the type registry.
    ///
    /// Rarely needed — [`registry()`](Self::registry) suffices for
    /// construction-time calls. Exists for direct mutation of the
    /// registry if needed.
    pub fn registry_mut(&mut self) -> &mut Registry {
        &mut self.registry
    }

    /// Returns the number of registered resources.
    pub fn len(&self) -> usize {
        self.storage.len()
    }

    /// Returns `true` if no resources have been registered.
    pub fn is_empty(&self) -> bool {
        self.storage.is_empty()
    }

    /// Returns `true` if a resource of type `T` has been registered.
    pub fn contains<T: 'static>(&self) -> bool {
        self.registry.contains::<T>()
    }

    /// Install a plugin. The plugin is consumed and registers its
    /// resources into this builder.
    pub fn install_plugin(&mut self, plugin: impl crate::plugin::Plugin) -> &mut Self {
        plugin.build(self);
        self
    }

    /// Install a driver. The installer is consumed, registers its resources
    /// into this builder, and returns a concrete poller for dispatch-time
    /// polling.
    pub fn install_driver<D: crate::driver::Installer>(&mut self, driver: D) -> D::Poller {
        driver.install(self)
    }

    /// Freeze the builder into an immutable [`World`] container.
    ///
    /// Automatically registers a [`Shutdown`](crate::shutdown::Shutdown)
    /// resource if one wasn't already registered. After this call, no
    /// more resources can be registered. All [`ResourceId`] values
    /// remain valid for the lifetime of the returned [`World`].
    pub fn build(mut self) -> World {
        self.ensure(crate::shutdown::Shutdown::new());
        World {
            registry: self.registry,
            storage: self.storage,
            current_sequence: Sequence(0),
            _not_sync: PhantomData,
            #[cfg(debug_assertions)]
            borrow_tracker: BorrowTracker::new(),
        }
    }
}

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

// =============================================================================
// World — frozen container
// =============================================================================

/// Frozen singleton resource storage.
///
/// Analogous to Bevy's `World`, but restricted to singleton resources
/// (no entities, no components, no archetypes).
///
/// Created by [`WorldBuilder::build()`]. Resources are accessed via
/// [`ResourceId`] direct pointers for O(1) dispatch-time access — a single
/// pointer deref per fetch, zero framework overhead.
///
/// # Safe API
///
/// - [`resource`](Self::resource) / [`resource_mut`](Self::resource_mut) —
///   cold-path access via HashMap lookup.
///
/// # Unsafe API (framework internals)
///
/// The low-level `get` / `get_mut` methods are `unsafe` — used by
/// [`Param::fetch`](crate::Param) for zero-overhead dispatch.
/// The caller must ensure no mutable aliasing.
pub struct World {
    /// Type-to-pointer mapping. Same registry used during build.
    registry: Registry,
    /// Type-erased pointer storage. Drop handled by `Storage`.
    storage: Storage,
    /// Current sequence number. Advanced by the driver before
    /// each event dispatch.
    current_sequence: Sequence,
    /// World must not be shared across threads — it holds interior-mutable
    /// `Cell<Sequence>` values accessed through `&self`. `!Sync` enforced by
    /// `PhantomData<Cell<()>>`.
    _not_sync: PhantomData<Cell<()>>,
    /// Debug-only aliasing tracker. Detects duplicate resource access within
    /// a single dispatch phase. Compiled out entirely in release builds.
    #[cfg(debug_assertions)]
    borrow_tracker: BorrowTracker,
}

impl World {
    /// Convenience constructor — returns a new [`WorldBuilder`].
    pub fn builder() -> WorldBuilder {
        WorldBuilder::new()
    }

    /// Returns a shared reference to the type registry.
    ///
    /// Use this for read-only queries (e.g. [`id`](Registry::id),
    /// [`contains`](Registry::contains)) and construction-time calls
    /// like [`into_handler`](crate::IntoHandler::into_handler).
    pub fn registry(&self) -> &Registry {
        &self.registry
    }

    /// Returns a mutable reference to the type registry.
    ///
    /// Rarely needed — [`registry()`](Self::registry) suffices for
    /// construction-time calls. Exists for direct mutation of the
    /// registry if needed.
    pub fn registry_mut(&mut self) -> &mut Registry {
        &mut self.registry
    }

    /// Resolve the [`ResourceId`] for a type. Cold path — uses HashMap lookup.
    ///
    /// # Panics
    ///
    /// Panics if the resource type was not registered.
    pub fn id<T: 'static>(&self) -> ResourceId {
        self.registry.id::<T>()
    }

    /// Try to resolve the [`ResourceId`] for a type. Returns `None` if the
    /// type was not registered.
    pub fn try_id<T: 'static>(&self) -> Option<ResourceId> {
        self.registry.try_id::<T>()
    }

    /// Returns the number of registered resources.
    pub fn len(&self) -> usize {
        self.storage.len()
    }

    /// Returns `true` if no resources are stored.
    pub fn is_empty(&self) -> bool {
        self.storage.is_empty()
    }

    /// Returns `true` if a resource of type `T` is stored.
    pub fn contains<T: 'static>(&self) -> bool {
        self.registry.contains::<T>()
    }

    // =========================================================================
    // Safe resource access (cold path — HashMap lookup per call)
    // =========================================================================

    /// Safe shared access to a resource. Cold path — resolves via HashMap.
    ///
    /// Takes `&self` — multiple shared references can coexist. The borrow
    /// checker prevents mixing with [`resource_mut`](Self::resource_mut)
    /// (which takes `&mut self`).
    ///
    /// # Panics
    ///
    /// Panics if the resource type was not registered.
    pub fn resource<T: 'static>(&self) -> &T {
        let id = self.registry.id::<T>();
        // SAFETY: id resolved from our own registry. &self prevents mutable
        // aliases — resource_mut takes &mut self.
        unsafe { self.get(id) }
    }

    /// Safe exclusive access to a resource. Cold path — resolves via HashMap.
    ///
    /// # Panics
    ///
    /// Panics if the resource type was not registered.
    pub fn resource_mut<T: 'static>(&mut self) -> &mut T {
        let id = self.registry.id::<T>();
        // Cold path — stamp unconditionally. If you request &mut, you're writing.
        // SAFETY: id resolved from our own registry.
        unsafe { self.stamp_changed(id) };
        // SAFETY: id resolved from our own registry. &mut self ensures
        // exclusive access — no other references can exist.
        unsafe { self.get_mut(id) }
    }

    // =========================================================================
    // One-shot dispatch
    // =========================================================================

    /// Run a handler once with full Param resolution.
    ///
    /// Intended for one-shot initialization after [`build()`](WorldBuilder::build).
    /// The handler receives `()` as the event — the event parameter is
    /// discarded. Named functions only (same closure limitation as
    /// [`IntoHandler`](crate::IntoHandler)).
    ///
    /// Can be called multiple times for phased initialization.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// fn startup(
    ///     mut driver: ResMut<MioDriver>,
    ///     mut listener: ResMut<Listener>,
    ///     _event: (),
    /// ) {
    ///     // wire drivers to IO sources...
    /// }
    ///
    /// let mut world = wb.build();
    /// world.run_startup(startup);
    /// ```
    pub fn run_startup<F, Params>(&mut self, f: F)
    where
        F: crate::IntoHandler<(), Params>,
    {
        use crate::Handler;
        let mut handler = f.into_handler(&self.registry);
        handler.run(self, ());
    }

    // =========================================================================
    // Shutdown
    // =========================================================================

    /// Returns a [`ShutdownHandle`](crate::shutdown::ShutdownHandle)
    /// sharing the same flag as the [`Shutdown`](crate::shutdown::Shutdown)
    /// resource.
    ///
    /// The handle is owned by the event loop and checked each iteration.
    /// Handlers trigger shutdown via `Res<Shutdown>::shutdown()`.
    pub fn shutdown_handle(&self) -> crate::shutdown::ShutdownHandle {
        self.resource::<crate::shutdown::Shutdown>().handle()
    }

    /// Run the event loop until shutdown is triggered.
    ///
    /// The closure receives `&mut World` and defines one iteration of
    /// the poll loop — which drivers to poll, in what order, with what
    /// timeout. The loop exits when a handler calls
    /// [`Shutdown::shutdown`](crate::shutdown::Shutdown::shutdown) or
    /// an external signal flips the flag (see
    /// [`ShutdownHandle::enable_signals`](crate::shutdown::ShutdownHandle::enable_signals)).
    ///
    /// The shutdown flag is resolved once before entering the loop —
    /// no resource lookup per iteration.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut world = wb.build();
    /// world.run_startup(startup);
    ///
    /// world.run(|world| {
    ///     let now = Instant::now();
    ///     let timeout = timer.next_deadline(world)
    ///         .map(|d| d.saturating_duration_since(now));
    ///     mio.poll(world, timeout).expect("mio poll");
    ///     timer.poll(world, now);
    /// });
    /// ```
    pub fn run(&mut self, mut f: impl FnMut(&mut World)) {
        let flag = self.resource::<crate::shutdown::Shutdown>().flag();
        while !flag.load(std::sync::atomic::Ordering::Relaxed) {
            f(self);
        }
    }

    // =========================================================================
    // Sequence / change detection
    // =========================================================================

    /// Returns the current event sequence number.
    pub fn current_sequence(&self) -> Sequence {
        self.current_sequence
    }

    /// Advance to the next event sequence number and return it.
    ///
    /// Drivers call this before dispatching each event. The returned
    /// sequence number identifies the event being processed. Resources
    /// mutated during dispatch will record this sequence in `changed_at`.
    pub fn next_sequence(&mut self) -> Sequence {
        self.current_sequence = Sequence(self.current_sequence.0.wrapping_add(1));
        self.current_sequence
    }

    // =========================================================================
    // Unsafe resource access (hot path — pre-resolved ResourceId)
    // =========================================================================

    /// Fetch a shared reference to a resource by direct pointer.
    ///
    /// # Safety
    ///
    /// - `id` must have been returned by [`WorldBuilder::register`] for
    ///   the same builder that produced this container.
    /// - `T` must be the same type that was registered at this `id`.
    /// - The caller must ensure no mutable reference to this resource exists.
    #[inline(always)]
    pub unsafe fn get<T: 'static>(&self, id: ResourceId) -> &T {
        // SAFETY: caller guarantees id was returned by register() on the
        // builder that produced this container. T matches the registered type.
        // No mutable alias exists. ResourceId points to a valid ResourceCell<T>.
        unsafe { &(*(id.as_ptr() as *const ResourceCell<T>)).value }
    }

    /// Fetch a mutable reference to a resource by direct pointer.
    ///
    /// Takes `&self` — the container structure is frozen, but individual
    /// resources have interior mutability via raw pointers. Sound because
    /// callers (single-threaded sequential dispatch) uphold no-aliasing.
    ///
    /// # Safety
    ///
    /// - `id` must have been returned by [`WorldBuilder::register`] for
    ///   the same builder that produced this container.
    /// - `T` must be the same type that was registered at this `id`.
    /// - The caller must ensure no other reference (shared or mutable) to this
    ///   resource exists.
    #[inline(always)]
    #[allow(clippy::mut_from_ref)]
    pub unsafe fn get_mut<T: 'static>(&self, id: ResourceId) -> &mut T {
        // SAFETY: caller guarantees id was returned by register() on the
        // builder that produced this container. T matches the registered type.
        // No aliases exist. ResourceId points to a valid ResourceCell<T>.
        unsafe { &mut (*(id.as_ptr() as *mut ResourceCell<T>)).value }
    }

    /// Reset borrow tracking for a new dispatch phase.
    ///
    /// Called before each [`Param::fetch`](crate::Param::fetch) in dispatch
    /// macros. Only exists in debug builds.
    #[cfg(debug_assertions)]
    pub(crate) fn clear_borrows(&self) {
        self.borrow_tracker.clear();
    }

    /// Record a resource access in the debug borrow tracker.
    ///
    /// Called by [`Param::fetch`](crate::Param::fetch) impls for each
    /// resource parameter. Panics if the resource was already accessed
    /// in the current phase (since the last [`clear_borrows`](Self::clear_borrows)).
    /// Only exists in debug builds.
    #[cfg(debug_assertions)]
    pub(crate) fn track_borrow(&self, id: ResourceId) {
        self.borrow_tracker.track(id);
    }

    // =========================================================================
    // Change-detection internals (framework use only)
    // =========================================================================

    /// Read the sequence at which a resource was last changed.
    ///
    /// Uses the `#[repr(C)]` guarantee that `changed_at` is at offset 0
    /// of `ResourceCell<T>`, so we can read it without knowing T.
    ///
    /// # Safety
    ///
    /// `id` must have been returned by [`WorldBuilder::register`] for
    /// the same builder that produced this container.
    #[inline(always)]
    #[allow(clippy::unused_self)] // Method interface — callers use world.changed_at(id).
    pub(crate) unsafe fn changed_at(&self, id: ResourceId) -> Sequence {
        // SAFETY: ResourceCell is #[repr(C)] with changed_at: Cell<Sequence>
        // at offset 0. id.as_ptr() points to a valid ResourceCell<T>.
        unsafe { (*(id.as_ptr() as *const Cell<Sequence>)).get() }
    }

    /// Get a reference to the `Cell` tracking a resource's change sequence.
    ///
    /// Uses the `#[repr(C)]` guarantee that `changed_at` is at offset 0.
    ///
    /// # Safety
    ///
    /// `id` must have been returned by [`WorldBuilder::register`] for
    /// the same builder that produced this container.
    #[inline(always)]
    #[allow(clippy::unused_self)] // Method interface — callers use world.changed_at_cell(id).
    pub(crate) unsafe fn changed_at_cell(&self, id: ResourceId) -> &Cell<Sequence> {
        // SAFETY: ResourceCell is #[repr(C)] with changed_at: Cell<Sequence>
        // at offset 0. id.as_ptr() points to a valid ResourceCell<T>.
        unsafe { &*(id.as_ptr() as *const Cell<Sequence>) }
    }

    /// Stamp a resource as changed at the current sequence.
    ///
    /// Uses the `#[repr(C)]` guarantee that `changed_at` is at offset 0.
    ///
    /// # Safety
    ///
    /// `id` must have been returned by [`WorldBuilder::register`] for
    /// the same builder that produced this container.
    #[inline(always)]
    #[allow(dead_code)] // Available for driver implementations.
    pub(crate) unsafe fn stamp_changed(&self, id: ResourceId) {
        // SAFETY: ResourceCell is #[repr(C)] with changed_at: Cell<Sequence>
        // at offset 0. id.as_ptr() points to a valid ResourceCell<T>.
        unsafe { (*(id.as_ptr() as *const Cell<Sequence>)).set(self.current_sequence) }
    }
}

// SAFETY: All resources are `T: Send` (enforced by `register`). World owns all
// heap-allocated data exclusively — the raw pointers are not aliased or shared.
// Transferring ownership to another thread is safe; the new thread becomes the
// sole accessor.
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Send for World {}

// =============================================================================
// Tests
// =============================================================================

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::{Arc, Weak};

    struct Price {
        value: f64,
    }

    struct Venue {
        name: &'static str,
    }

    struct Config {
        max_orders: usize,
    }

    #[test]
    fn register_and_build() {
        let mut builder = WorldBuilder::new();
        builder.register::<Price>(Price { value: 100.0 });
        builder.register::<Venue>(Venue { name: "test" });
        let world = builder.build();
        // +1 for auto-registered Shutdown.
        assert_eq!(world.len(), 3);
    }

    #[test]
    fn resource_ids_are_distinct() {
        let mut builder = WorldBuilder::new();
        let id0 = builder.register::<Price>(Price { value: 0.0 });
        let id1 = builder.register::<Venue>(Venue { name: "" });
        let id2 = builder.register::<Config>(Config { max_orders: 0 });
        assert_ne!(id0, id1);
        assert_ne!(id1, id2);
        assert_ne!(id0, id2);

        let world = builder.build();
        unsafe {
            assert_eq!(world.get::<Price>(id0).value, 0.0);
            assert_eq!(world.get::<Venue>(id1).name, "");
            assert_eq!(world.get::<Config>(id2).max_orders, 0);
        }
    }

    #[test]
    fn get_returns_registered_value() {
        let mut builder = WorldBuilder::new();
        builder.register::<Price>(Price { value: 42.5 });
        let world = builder.build();

        let id = world.id::<Price>();
        // SAFETY: id resolved from this container, type matches, no aliasing.
        let price = unsafe { world.get::<Price>(id) };
        assert_eq!(price.value, 42.5);
    }

    #[test]
    fn get_mut_modifies_value() {
        let mut builder = WorldBuilder::new();
        builder.register::<Price>(Price { value: 1.0 });
        let world = builder.build();

        let id = world.id::<Price>();
        // SAFETY: id resolved from this container, type matches, no aliasing.
        unsafe {
            world.get_mut::<Price>(id).value = 99.0;
            assert_eq!(world.get::<Price>(id).value, 99.0);
        }
    }

    #[test]
    fn try_id_returns_none_for_unregistered() {
        let world = WorldBuilder::new().build();
        assert!(world.try_id::<Price>().is_none());
    }

    #[test]
    fn try_id_returns_some_for_registered() {
        let mut builder = WorldBuilder::new();
        builder.register::<Price>(Price { value: 0.0 });
        let world = builder.build();

        assert!(world.try_id::<Price>().is_some());
    }

    #[test]
    #[should_panic(expected = "already registered")]
    fn panics_on_duplicate_registration() {
        let mut builder = WorldBuilder::new();
        builder.register::<Price>(Price { value: 1.0 });
        builder.register::<Price>(Price { value: 2.0 });
    }

    #[test]
    #[should_panic(expected = "not registered")]
    fn panics_on_unregistered_id() {
        let world = WorldBuilder::new().build();
        world.id::<Price>();
    }

    #[test]
    fn empty_builder_builds_empty_world() {
        let world = WorldBuilder::new().build();
        // Shutdown is auto-registered by build().
        assert_eq!(world.len(), 1);
        assert!(world.contains::<crate::shutdown::Shutdown>());
    }

    #[test]
    fn drop_runs_destructors() {
        let arc = Arc::new(42u32);
        let weak: Weak<u32> = Arc::downgrade(&arc);

        {
            let mut builder = WorldBuilder::new();
            builder.register::<Arc<u32>>(arc);
            let _world = builder.build();
            // Arc still alive — held by World
            assert!(weak.upgrade().is_some());
        }
        // World dropped — Arc should be deallocated
        assert!(weak.upgrade().is_none());
    }

    #[test]
    fn builder_drop_cleans_up_without_build() {
        let arc = Arc::new(99u32);
        let weak: Weak<u32> = Arc::downgrade(&arc);

        {
            let mut builder = WorldBuilder::new();
            builder.register::<Arc<u32>>(arc);
        }
        // Builder dropped without build() — Storage::drop cleans up
        assert!(weak.upgrade().is_none());
    }

    #[test]
    fn multiple_types_independent() {
        let mut builder = WorldBuilder::new();
        let price_id = builder.register::<Price>(Price { value: 10.0 });
        let venue_id = builder.register::<Venue>(Venue { name: "CB" });
        let config_id = builder.register::<Config>(Config { max_orders: 500 });
        let world = builder.build();

        unsafe {
            assert_eq!(world.get::<Price>(price_id).value, 10.0);
            assert_eq!(world.get::<Venue>(venue_id).name, "CB");
            assert_eq!(world.get::<Config>(config_id).max_orders, 500);
        }
    }

    #[test]
    fn contains_reflects_registration() {
        let mut builder = WorldBuilder::new();
        assert!(!builder.contains::<Price>());

        builder.register::<Price>(Price { value: 0.0 });
        assert!(builder.contains::<Price>());
        assert!(!builder.contains::<Venue>());

        let world = builder.build();
        assert!(world.contains::<Price>());
        assert!(!world.contains::<Venue>());
    }

    #[test]
    fn send_to_another_thread() {
        let mut builder = WorldBuilder::new();
        builder.register::<Price>(Price { value: 55.5 });
        let world = builder.build();

        let handle = std::thread::spawn(move || {
            let id = world.id::<Price>();
            // SAFETY: sole owner on this thread, no aliasing.
            unsafe { world.get::<Price>(id).value }
        });
        assert_eq!(handle.join().unwrap(), 55.5);
    }

    #[test]
    fn registry_accessible_from_builder() {
        let mut builder = WorldBuilder::new();
        let registered_id = builder.register::<u64>(42);

        let registry = builder.registry();
        assert!(registry.contains::<u64>());
        assert!(!registry.contains::<bool>());

        let id = registry.id::<u64>();
        assert_eq!(id, registered_id);
    }

    #[test]
    fn registry_accessible_from_world() {
        let mut builder = WorldBuilder::new();
        builder.register::<u64>(42);
        let world = builder.build();

        let registry = world.registry();
        assert!(registry.contains::<u64>());

        // Registry from world and world.id() agree.
        assert_eq!(registry.id::<u64>(), world.id::<u64>());
    }

    // -- Safe accessor tests --------------------------------------------------

    #[test]
    fn resource_reads_value() {
        let mut builder = WorldBuilder::new();
        builder.register::<Price>(Price { value: 42.5 });
        let world = builder.build();

        assert_eq!(world.resource::<Price>().value, 42.5);
    }

    #[test]
    fn resource_mut_modifies_value() {
        let mut builder = WorldBuilder::new();
        builder.register::<u64>(0);
        let mut world = builder.build();

        *world.resource_mut::<u64>() = 99;
        assert_eq!(*world.resource::<u64>(), 99);
    }

    #[test]
    fn register_default_works() {
        let mut builder = WorldBuilder::new();
        let id = builder.register_default::<Vec<u32>>();
        let world = builder.build();

        assert_eq!(id, world.id::<Vec<u32>>());
        let v = world.resource::<Vec<u32>>();
        assert!(v.is_empty());
    }

    #[test]
    fn ensure_registers_new_type() {
        let mut builder = WorldBuilder::new();
        let id = builder.ensure::<u64>(42);
        let world = builder.build();

        assert_eq!(id, world.id::<u64>());
        assert_eq!(*world.resource::<u64>(), 42);
    }

    #[test]
    fn ensure_returns_existing_id() {
        let mut builder = WorldBuilder::new();
        let id1 = builder.register::<u64>(42);
        let id2 = builder.ensure::<u64>(99);
        assert_eq!(id1, id2);

        // Original value preserved, new value dropped.
        let world = builder.build();
        assert_eq!(*world.resource::<u64>(), 42);
    }

    #[test]
    fn ensure_default_registers_new_type() {
        let mut builder = WorldBuilder::new();
        let id = builder.ensure_default::<Vec<u32>>();
        let world = builder.build();

        assert_eq!(id, world.id::<Vec<u32>>());
        assert!(world.resource::<Vec<u32>>().is_empty());
    }

    #[test]
    fn ensure_default_returns_existing_id() {
        let mut builder = WorldBuilder::new();
        builder.register::<Vec<u32>>(vec![1, 2, 3]);
        let id = builder.ensure_default::<Vec<u32>>();
        let world = builder.build();

        assert_eq!(id, world.id::<Vec<u32>>());
        // Original value preserved.
        assert_eq!(*world.resource::<Vec<u32>>(), vec![1, 2, 3]);
    }

    // -- Sequence / change detection tests ----------------------------------------

    #[test]
    fn sequence_default_is_zero() {
        assert_eq!(Sequence::default(), Sequence(0));
    }

    #[test]
    fn next_sequence_increments() {
        let mut world = WorldBuilder::new().build();
        assert_eq!(world.current_sequence(), Sequence(0));
        world.next_sequence();
        assert_eq!(world.current_sequence(), Sequence(1));
        world.next_sequence();
        assert_eq!(world.current_sequence(), Sequence(2));
    }

    #[test]
    fn resource_registered_at_current_sequence() {
        // Resources registered at build time get changed_at=Sequence(0).
        // World starts at current_sequence=Sequence(0). So they match — "changed."
        let mut builder = WorldBuilder::new();
        builder.register::<u64>(42);
        let world = builder.build();

        let id = world.id::<u64>();
        unsafe {
            assert_eq!(world.changed_at(id), Sequence(0));
            assert_eq!(world.current_sequence(), Sequence(0));
            // changed_at == current_sequence → "changed"
            assert_eq!(world.changed_at(id), world.current_sequence());
        }
    }

    #[test]
    fn resource_mut_stamps_changed_at() {
        let mut builder = WorldBuilder::new();
        builder.register::<u64>(0);
        let mut world = builder.build();

        world.next_sequence(); // tick=1
        let id = world.id::<u64>();

        // changed_at is still 0, current_sequence is 1 → not changed
        unsafe {
            assert_eq!(world.changed_at(id), Sequence(0));
        }

        // resource_mut stamps changed_at to current_sequence
        *world.resource_mut::<u64>() = 99;
        unsafe {
            assert_eq!(world.changed_at(id), Sequence(1));
        }
    }

    // -- run_startup tests ----------------------------------------------------

    #[test]
    fn run_startup_dispatches_handler() {
        use crate::ResMut;

        let mut builder = WorldBuilder::new();
        builder.register::<u64>(0);
        builder.register::<bool>(false);
        let mut world = builder.build();

        fn init(mut counter: ResMut<u64>, mut flag: ResMut<bool>, _event: ()) {
            *counter = 42;
            *flag = true;
        }

        world.run_startup(init);

        assert_eq!(*world.resource::<u64>(), 42);
        assert!(*world.resource::<bool>());
    }

    #[test]
    fn run_startup_multiple_phases() {
        use crate::ResMut;

        let mut builder = WorldBuilder::new();
        builder.register::<u64>(0);
        let mut world = builder.build();

        fn phase1(mut counter: ResMut<u64>, _event: ()) {
            *counter += 10;
        }

        fn phase2(mut counter: ResMut<u64>, _event: ()) {
            *counter += 5;
        }

        world.run_startup(phase1);
        world.run_startup(phase2);

        assert_eq!(*world.resource::<u64>(), 15);
    }

    // -- Plugin / Driver tests ------------------------------------------------

    #[test]
    fn plugin_registers_resources() {
        struct TestPlugin;

        impl crate::plugin::Plugin for TestPlugin {
            fn build(self, world: &mut WorldBuilder) {
                world.register::<u64>(42);
                world.register::<bool>(true);
            }
        }

        let mut builder = WorldBuilder::new();
        builder.install_plugin(TestPlugin);
        let world = builder.build();

        assert_eq!(*world.resource::<u64>(), 42);
        assert_eq!(*world.resource::<bool>(), true);
    }

    #[test]
    fn driver_installs_and_returns_handle() {
        struct TestInstaller;
        struct TestHandle {
            counter_id: ResourceId,
        }

        impl crate::driver::Installer for TestInstaller {
            type Poller = TestHandle;

            fn install(self, world: &mut WorldBuilder) -> TestHandle {
                let counter_id = world.register::<u64>(0);
                TestHandle { counter_id }
            }
        }

        let mut builder = WorldBuilder::new();
        let handle = builder.install_driver(TestInstaller);
        let world = builder.build();

        // Handle's pre-resolved ID can access the resource.
        unsafe {
            assert_eq!(*world.get::<u64>(handle.counter_id), 0);
        }
    }

    // -- check_access conflict detection ----------------------------------------

    #[test]
    fn check_access_no_conflict() {
        let mut builder = WorldBuilder::new();
        let id_a = builder.register::<u64>(0);
        let id_b = builder.register::<u32>(0);
        builder.registry().check_access(&[
            (Some(id_a), "a"),
            (Some(id_b), "b"),
        ]);
    }

    #[test]
    #[should_panic(expected = "conflicting access")]
    fn check_access_detects_conflict() {
        let mut builder = WorldBuilder::new();
        let id = builder.register::<u64>(0);
        builder.registry().check_access(&[
            (Some(id), "a"),
            (Some(id), "b"),
        ]);
    }

    #[test]
    fn sequence_wrapping() {
        let mut builder = WorldBuilder::new();
        builder.register::<u64>(0);
        let mut world = builder.build();

        // Advance to MAX.
        world.current_sequence = Sequence(u64::MAX);
        assert_eq!(world.current_sequence(), Sequence(u64::MAX));

        // Stamp resource at MAX.
        *world.resource_mut::<u64>() = 99;
        let id = world.id::<u64>();
        unsafe {
            assert_eq!(world.changed_at(id), Sequence(u64::MAX));
        }

        // Wrap to 0.
        let seq = world.next_sequence();
        assert_eq!(seq, Sequence(0));
        assert_eq!(world.current_sequence(), Sequence(0));

        // Resource changed at MAX, current is 0 → not changed.
        unsafe {
            assert_ne!(world.changed_at(id), world.current_sequence());
        }
    }

    // -- BorrowTracker tests (debug builds only) ------------------------------

    #[cfg(debug_assertions)]
    #[test]
    #[should_panic(expected = "conflicting access")]
    fn borrow_tracker_catches_double_access() {
        let mut builder = WorldBuilder::new();
        let id = builder.register::<u64>(42);
        let world = builder.build();
        world.clear_borrows();
        world.track_borrow(id);
        world.track_borrow(id); // same resource, same phase
    }

    #[cfg(debug_assertions)]
    #[test]
    fn borrow_tracker_allows_after_clear() {
        let mut builder = WorldBuilder::new();
        let id = builder.register::<u64>(42);
        let world = builder.build();
        world.clear_borrows();
        world.track_borrow(id);
        world.clear_borrows();
        world.track_borrow(id); // new phase, no conflict
    }

    #[cfg(debug_assertions)]
    #[test]
    fn borrow_tracker_different_resources_ok() {
        let mut builder = WorldBuilder::new();
        let id_a = builder.register::<u64>(1);
        let id_b = builder.register::<u32>(2);
        let world = builder.build();
        world.clear_borrows();
        world.track_borrow(id_a);
        world.track_borrow(id_b); // different resources, no conflict
    }
}
