//! `PredicateQuery` — handler-side access to predicate evaluation.
//!
//! The SDK ships a `Predicate` IR (see `crate::predicate`) that
//! describes activation gates, distance checks, ability-score
//! lookups, and the like. Evaluating a predicate against live
//! state requires more than the entity store alone — distance
//! predicates need the engine's reachability cache; some lookups
//! consult the loaded map. That machinery lives in
//! `maat_engine`, which the SDK can't depend on.
//!
//! `PredicateQuery` decouples the two: handlers ask
//! `ctx.query::<dyn PredicateQuery>().evaluate(&predicate, …)`; the
//! engine installs an impl that builds a fully-furnished
//! `EvalContext` internally and delegates.

use crate::predicate::Predicate;
use crate::types::EntityId;

/// No-op default `PredicateQuery` used by trait default impls when
/// no real evaluator is installed (test contexts, SDK-only stubs).
/// Always returns `false`.
pub struct NoOpPredicateQuery;
impl PredicateQuery for NoOpPredicateQuery {
    fn evaluate(
        &self,
        _predicate: &Predicate,
        _self_id: EntityId,
        _target: Option<EntityId>,
    ) -> bool { false }
}

/// Singleton `NoOpPredicateQuery` reachable as a `'static` reference
/// from default trait method bodies.
pub static NOOP_PREDICATE_QUERY: NoOpPredicateQuery = NoOpPredicateQuery;

/// Plugin-facing predicate evaluation.
///
/// Methods take SDK-only types so the trait can live in the SDK.
/// Implementors (engine, capability plugins, test harnesses) are
/// free to back this with whatever internal state their evaluator
/// needs.
///
/// The trait carries **no supertrait bounds**. Engine built-in
/// impls borrow per-call session state including `!Sync` cells
/// (the entity-id allocator), so they can't satisfy `Send + Sync`;
/// they live on `GameContext` directly and are accessed through a
/// dedicated method, not the registry. Capability-provided
/// `'static` impls go into the `QueryRegistry` and add `Send + Sync`
/// inline at the call site (`Arc<dyn PredicateQuery + Send + Sync>`).
pub trait PredicateQuery {
    /// Evaluate `predicate` with `EntityRef::Self_` resolving to
    /// `self_id` and `EntityRef::Target` resolving to `target` (or
    /// unbound if `target` is `None`).
    ///
    /// Returns `false` for any failure mode: predicate evaluates
    /// to false, required cache state unavailable, or
    /// `self_id` / `target` no longer alive. Handlers should treat
    /// `false` as "this predicate doesn't currently hold" and act
    /// accordingly.
    fn evaluate(
        &self,
        predicate: &Predicate,
        self_id: EntityId,
        target: Option<EntityId>,
    ) -> bool;
}
