//! linkme distributed slices — every `#[derive(EntityType)]`,
//! `#[handler(...)]`, etc. across the workspace contributes one entry.
//!
//! Each entry carries:
//!
//! - `plugin_crate_name: &'static str` — `env!("CARGO_PKG_NAME")` at
//!   the registering item's compile site. Used by `#[plugin(...)]`'s
//!   manifest assembler to filter the global slice down to its own
//!   crate.
//! - `plugin_struct: &'static str` — **only on `HandlerRegistration`**;
//!   name of the plugin struct the handler belongs to. Used by the
//!   walker to disambiguate when one Cargo crate hosts multiple
//!   plugins. Other registration kinds rely on the crate-level filter
//!   only (P5, 2026-05-21).
//!
//! The signature fields are populated by the macro at expansion time
//! (or at install time for ones that depend on runtime data).

use core::any::Any;

use linkme::distributed_slice;

use crate::events::{EventKind, GameEvent, LogEntry};
use crate::handler::HandlerResponse;
use crate::plugin_version::Blake3;
use crate::priority::Priority;
use crate::settings::SettingsBundle;
use crate::entity_state::{EntityState, EntityView};
use crate::trait_data::{PipelineRole, TraitSet};
use crate::types::{EntityId, EntityTypeId, PhaseId, SeatId};

/// SDK-side erased view of a plugin instance. The full `ModelPlugin`
/// trait now lives in `maat_engine::plugin::traits`; SDK can't
/// reference it (engine depends on SDK, not the reverse). Engine's
/// `ModelPlugin` extends this trait so the SDK-side `HandlerThunk`
/// type alias and the macro-emitted thunks stay agnostic of the
/// engine.
pub trait PluginInstance: Any + Send + Sync + 'static {
    fn as_any(&self) -> &dyn Any;
}

/// Blanket bridge: every `T: Any + Send + Sync + 'static`
/// implements `PluginInstance`. This lets engine's `ModelPlugin:
/// PluginInstance` supertrait bound resolve automatically without
/// each plugin author writing an explicit `impl PluginInstance for
/// X`. Lives in SDK to satisfy the orphan rule.
impl<T: Any + Send + Sync + 'static> PluginInstance for T {
    fn as_any(&self) -> &dyn Any { self }
}

impl dyn PluginInstance {
    /// Downcast `&self` to a concrete plugin type. Returns `None` if
    /// `T` doesn't match the underlying plugin.
    pub fn downcast_ref<T: PluginInstance>(&self) -> Option<&T> {
        self.as_any().downcast_ref::<T>()
    }
}

/// Function pointer to a handler method's thunk. The thunk downcasts
/// `plugin: &dyn PluginInstance` to its concrete type and invokes the
/// `&self` handler method.
///
/// Phase 5a (S25): handlers are now `&self` methods on the plugin
/// struct, not free-standing functions. The macro emits one thunk per
/// `#[handler]`-tagged method that performs the downcast and call.
///
/// Phase 5d-2-prep (S25): the first arg is `&dyn PluginInstance`
/// rather than `&dyn ModelPlugin` so SDK doesn't have to depend on
/// engine. Concrete `ModelPlugin` impls in engine extend
/// `PluginInstance`, so the cast is sound at the call site (every
/// `&dyn ModelPlugin` coerces to `&dyn PluginInstance`).
pub type HandlerThunk =
    fn(plugin: &dyn PluginInstance, event: &GameEvent, ctx: &dyn GameContext) -> HandlerResponse;

/// Back-compat alias — the legacy name `HandlerFn` is re-exported as
/// `RegisteredHandlerFn` from `crate::plugin`. Phase 5a renames the
/// thunk type but keeps the alias around for the duration of the
/// per-plugin migration in Phase 5b.
pub type HandlerFn = HandlerThunk;

/// Surface a handler `&self` method needs. The engine's
/// `GameContext<'_>` impls this trait; plugin handlers consume it as
/// `&dyn GameContext` so the SDK does not depend on engine types.
///
/// The method set is the minimum viable surface — Phase 5b will widen
/// it as concrete plugins demand more. Methods that already exist on
/// `GameContext` delegate trivially; iterator-returning methods return
/// boxed `Iterator` to stay object-safe on stable Rust.
pub trait GameContext {
    /// Read-only access to the entity store materialized view.
    fn store(&self) -> &EntityState;
    /// Which seat is currently taking its turn.
    fn current_player(&self) -> Option<SeatId>;
    /// Settings bundle for the running session.
    fn settings(&self) -> &SettingsBundle;
    /// Allocate a fresh entity ID. Safe to call from any handler.
    fn alloc_id(&self) -> EntityId;
    /// True iff the entity's owner is the seat currently taking its
    /// turn.
    fn is_owners_turn(&self, entity: EntityId) -> bool;
    /// Reverse container lookup — which container (if any) currently
    /// holds `entity`.
    fn contained_by(&self, entity: EntityId) -> Option<EntityId>;
    /// Unfiltered (vision-bypassing) view of an entity. Plugin
    /// handlers run inside the trusted host pipeline, so they see the
    /// authoritative state. Returns `None` for unknown entities.
    fn entity_view_for(&self, entity: EntityId) -> Option<EntityView>;
    /// All log entries for the in-progress turn (S08).
    fn this_turn_events(&self) -> Box<dyn Iterator<Item = &LogEntry> + '_>;
    /// All log entries for the in-progress phase (S08).
    fn this_phase_events(&self) -> Box<dyn Iterator<Item = &LogEntry> + '_>;
    /// All log entries with `seq >= seq_floor`.
    fn since(&self, seq_floor: u64) -> Box<dyn Iterator<Item = &LogEntry> + '_>;

    /// Phase 5b (S25): the seats currently participating in the
    /// session, in turn order. Used by life's landing-effect handler
    /// (and any plugin that needs to see the full table at once).
    /// Default returns `&[]` so existing impls don't break — engine
    /// override gives the real list.
    fn players(&self) -> &[SeatId] { &[] }

    /// The phase currently at the top of the phase stack, if any.
    ///
    /// Plugin handlers that gate behavior on the active phase
    /// (placement-phase rules vs main-phase rules, etc.) read this
    /// rather than scanning the event log for the latest
    /// `PhaseEntered` / `PhaseTransitioned`. Default returns `None`
    /// so unit-test contexts and `&dyn GameContext` impls that
    /// don't have access to a phase stack still satisfy the trait;
    /// the engine override reads from `session.phase_stack`.
    fn current_phase(&self) -> Option<PhaseId> { None }

    /// CL1 — role assigned to `seat`. Returns
    /// [`crate::config::SeatRole::Player`] when the seat has no entry
    /// (matching [`crate::config::SessionConfig::role_for`]'s default).
    /// Default returns `Player` so existing impls compile unchanged;
    /// engine override reads `session.policy.seat_roles`.
    fn seat_role_for(&self, _seat: SeatId) -> crate::config::SeatRole {
        crate::config::SeatRole::Player
    }

    /// Phase 5c-handlers (S25): entities currently occupying `pos`
    /// per the session's spatial index. The SDK can't depend on the
    /// engine's `SpatialIndex` directly, so this proxy returns a
    /// `Vec<EntityId>`. Default returns empty (sound for impls that
    /// haven't wired a spatial index — e.g. card games).
    ///
    /// **Deprecated by HP1-C-1**: prefer
    /// `ctx.spatial().map(|s| s.entities_at(pos))` so the call site
    /// surfaces the absence of a spatial impl instead of silently
    /// receiving an empty vec.
    fn entities_at(&self, _pos: &crate::types::Position) -> Vec<EntityId> {
        Vec::new()
    }

    // --- HP1-C-1: typed query accessors ---
    //
    // Engine built-in queries (predicate / vision / spatial) live as
    // direct methods on this trait because their impls borrow
    // per-call session state and therefore can't be `'static` —
    // they can't go through the `Arc<dyn Q>`-based `QueryRegistry`.
    // Capability-provided `'static` queries DO go through the
    // registry, accessed via `queries()`.

    /// Predicate evaluator for the live session. Always available
    /// from the engine; the default impl returns a no-op evaluator
    /// that always reports `false` so unit-test contexts and
    /// pre-HP1 plugin impls compile without engine wiring.
    fn predicates(&self) -> &dyn crate::queries::PredicateQuery {
        &crate::queries::predicate::NOOP_PREDICATE_QUERY
    }

    /// Per-seat visibility queries. `None` when no vision provider
    /// is wired (e.g., open-information games). Plugin handlers
    /// that require visibility data should `expect`-or-fail at the
    /// call site rather than silently receiving wrong answers.
    fn vision(&self) -> Option<&dyn crate::queries::VisionQuery> {
        None
    }

    /// Spatial index / distance / area lookups. `None` when no
    /// spatial provider is wired. Replaces the legacy
    /// `entities_at(pos)` method for new call sites.
    fn spatial(&self) -> Option<&dyn crate::queries::SpatialQuery> {
        None
    }

    /// Capability-provided extensions. Returns the per-session
    /// `QueryRegistry` holding `'static` query traits installed by
    /// capability plugins via the manifest-driven port-provider
    /// resolution (HP1.6). Default returns a fresh empty registry
    /// so impls that don't have one still satisfy the trait.
    fn queries(&self) -> &crate::queries::QueryRegistry {
        use std::sync::OnceLock;
        static EMPTY: OnceLock<crate::queries::QueryRegistry> = OnceLock::new();
        EMPTY.get_or_init(crate::queries::QueryRegistry::new)
    }
}

// WASM1-narrow-shim — blanket reference impl. Lets `&dyn GameContext`
// be passed where a `&impl GameContext` (Sized) is expected by
// wrapping it as `&&dyn GameContext` — the outer `&dyn GameContext`
// reference IS Sized, and this impl delegates every method through
// to the inner trait object.
//
// Why we need this: plugin handler/compose methods now take
// `ctx: &impl GameContext` (Sized) to enable wasm32 per-method
// monomorphization. The native macro-emitted thunk receives
// `&dyn GameContext` from the pipeline; without this blanket
// impl it couldn't satisfy the user method's Sized bound. The
// double-reference (`user_method(&ctx_dyn)`) gives the compiler
// a Sized `C = &dyn GameContext` to monomorphize against; this
// blanket impl makes that monomorphization typecheck.
//
// On wasm32, the thunk passes `&WasmGameContext` directly (no
// double-ref) so `C = WasmGameContext` and the per-method DCE
// works as designed.
impl<T: ?Sized + GameContext> GameContext for &T {
    fn store(&self) -> &EntityState {
        (**self).store()
    }
    fn current_player(&self) -> Option<SeatId> {
        (**self).current_player()
    }
    fn settings(&self) -> &SettingsBundle {
        (**self).settings()
    }
    fn alloc_id(&self) -> EntityId {
        (**self).alloc_id()
    }
    fn is_owners_turn(&self, entity: EntityId) -> bool {
        (**self).is_owners_turn(entity)
    }
    fn contained_by(&self, entity: EntityId) -> Option<EntityId> {
        (**self).contained_by(entity)
    }
    fn entity_view_for(&self, entity: EntityId) -> Option<EntityView> {
        (**self).entity_view_for(entity)
    }
    fn this_turn_events(&self) -> Box<dyn Iterator<Item = &LogEntry> + '_> {
        (**self).this_turn_events()
    }
    fn this_phase_events(&self) -> Box<dyn Iterator<Item = &LogEntry> + '_> {
        (**self).this_phase_events()
    }
    fn since(&self, seq_floor: u64) -> Box<dyn Iterator<Item = &LogEntry> + '_> {
        (**self).since(seq_floor)
    }
    fn players(&self) -> &[SeatId] {
        (**self).players()
    }
    fn current_phase(&self) -> Option<PhaseId> {
        (**self).current_phase()
    }
    fn seat_role_for(&self, seat: SeatId) -> crate::config::SeatRole {
        (**self).seat_role_for(seat)
    }
    fn entities_at(&self, pos: &crate::types::Position) -> Vec<EntityId> {
        (**self).entities_at(pos)
    }
    fn predicates(&self) -> &dyn crate::queries::PredicateQuery {
        (**self).predicates()
    }
    fn vision(&self) -> Option<&dyn crate::queries::VisionQuery> {
        (**self).vision()
    }
    fn spatial(&self) -> Option<&dyn crate::queries::SpatialQuery> {
        (**self).spatial()
    }
    fn queries(&self) -> &crate::queries::QueryRegistry {
        (**self).queries()
    }
}

#[derive(Debug, Clone, Copy)]
pub struct EntityRegistration {
    pub plugin_crate_name: &'static str,
    pub type_name: &'static str,
    pub type_id: EntityTypeId,
    pub trait_set_bits: u64,
    pub trait_data_signature: Blake3,
}

impl EntityRegistration {
    pub fn trait_set(&self) -> TraitSet {
        TraitSet::from_bits_truncate(self.trait_set_bits)
    }
}

/// One handler entry contributed by a `#[handler]`-tagged method.
///
/// Phase 5a (S25): handlers are `&self` methods on the plugin struct.
/// The thunk downcasts `&dyn ModelPlugin` to the plugin's concrete
/// type and invokes the method. `local_id` (used by `dispatch_handler`
/// and the manifest's `declared_handlers`) is the position of this
/// registration after sorting by `method_name` per
/// `(plugin_crate_name, plugin_struct)` group. **Both must use the
/// same ordering rule** — see `core/sdk-derive/src/plugin_attr.rs`.
#[derive(Debug, Clone, Copy)]
pub struct HandlerRegistration {
    pub plugin_crate_name: &'static str,
    pub plugin_struct: &'static str,
    /// Identifier of the underlying `&self` method on the plugin
    /// struct. Used as the deterministic sort key for `local_id`
    /// assignment.
    pub method_name: &'static str,
    /// Wire-visible handler name (the `name = "..."` arg on
    /// `#[handler]`). Defaults to `method_name` when unspecified.
    pub name: &'static str,
    pub event_kind: EventKind,
    pub role: PipelineRole,
    pub priority: Priority,
    pub thunk: HandlerThunk,
    pub signature: Blake3,
}

#[derive(Debug, Clone, Copy)]
pub struct CapabilityRegistration {
    pub plugin_crate_name: &'static str,
    pub name: &'static str,
    pub signature: Blake3,
}

#[derive(Debug, Clone, Copy)]
pub struct PhaseRegistration {
    pub plugin_crate_name: &'static str,
    pub phase_id: PhaseId,
    pub signature: Blake3,
}

#[derive(Debug, Clone, Copy)]
pub struct WireFrameRegistration {
    pub plugin_crate_name: &'static str,
    pub frame_name: &'static str,
    pub signature: Blake3,
}

// All slice declarations carry `#[linkme(crate = crate::linkme)]` so
// downstream plugin code can register via the `maat_sdk` re-export
// without requiring a direct `linkme` dep.

// linkme `distributed_slice` is not implemented on wasm32 (no
// linker-anchored section symbols). The plugin registry in
// `core/engine` is wasm-gated for the same reason. Mirror the gate
// here and provide empty fallback slices for wasm32 callers so
// downstream code that iterates these (the `maat` façade, the
// `plugin/mod.rs` re-exports) compiles cleanly for the WASM client
// — the client never registers plugins via linkme; the server
// (host target) is the only registration site.

#[cfg(not(target_arch = "wasm32"))]
#[distributed_slice]
#[linkme(crate = crate::linkme)]
pub static REGISTERED_ENTITIES: [EntityRegistration] = [..];
#[cfg(target_arch = "wasm32")]
pub static REGISTERED_ENTITIES: &[EntityRegistration] = &[];

#[cfg(not(target_arch = "wasm32"))]
#[distributed_slice]
#[linkme(crate = crate::linkme)]
pub static REGISTERED_HANDLERS: [HandlerRegistration] = [..];
#[cfg(target_arch = "wasm32")]
pub static REGISTERED_HANDLERS: &[HandlerRegistration] = &[];

#[cfg(not(target_arch = "wasm32"))]
#[distributed_slice]
#[linkme(crate = crate::linkme)]
pub static REGISTERED_CAPABILITIES: [CapabilityRegistration] = [..];
#[cfg(target_arch = "wasm32")]
pub static REGISTERED_CAPABILITIES: &[CapabilityRegistration] = &[];

#[cfg(not(target_arch = "wasm32"))]
#[distributed_slice]
#[linkme(crate = crate::linkme)]
pub static REGISTERED_PHASES: [PhaseRegistration] = [..];
#[cfg(target_arch = "wasm32")]
pub static REGISTERED_PHASES: &[PhaseRegistration] = &[];

#[cfg(not(target_arch = "wasm32"))]
#[distributed_slice]
#[linkme(crate = crate::linkme)]
pub static REGISTERED_WIRE_FRAMES: [WireFrameRegistration] = [..];
#[cfg(target_arch = "wasm32")]
pub static REGISTERED_WIRE_FRAMES: &[WireFrameRegistration] = &[];

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

    #[test]
    fn slices_are_addressable() {
        // We just want to confirm the symbols resolve and can be
        // iterated. Population is verified by sdk-derive integration
        // tests that emit registrations.
        let _ = REGISTERED_ENTITIES.iter().count();
        let _ = REGISTERED_HANDLERS.iter().count();
        let _ = REGISTERED_CAPABILITIES.iter().count();
        let _ = REGISTERED_PHASES.iter().count();
        let _ = REGISTERED_WIRE_FRAMES.iter().count();
    }
}
