import type { Ellipse } from "../geometries/ellipse.ts";
import type { Polygon } from "../geometries/polygon.ts";
import type { Rect } from "../geometries/rectangle.ts";
import type { Sphere } from "../geometries/sphere.ts";
import type { Vector2d } from "../math/vector2d.ts";
import type { Vector3d } from "../math/vector3d.ts";
import type Renderable from "../renderable/renderable.js";
import type { Bounds } from "./bounds.ts";
import type World from "./world.js";

/**
 * Body simulation kind. `static` bodies never move (terrain, walls);
 * `dynamic` bodies are simulated (player, projectiles); `kinematic`
 * bodies are user-positioned but participate in collision (moving
 * platforms, elevators).
 */
export type BodyType = "static" | "dynamic" | "kinematic";

/** Collision shape that a physics body may be composed of. */
export type BodyShape = Rect | Ellipse | Polygon;

/**
 * Collision response passed to the modern collision lifecycle hooks
 * (`onCollisionStart`, `onCollisionActive`, `onCollisionEnd`) on every
 * adapter.
 *
 * **Receiver-symmetric:** `a` is always the renderable whose handler is
 * firing (`response.a === this`), `b` is always the partner (`response.b
 * === other`). The same overlap is dispatched once to each side as a
 * separate response with `a`/`b` swapped accordingly.
 *
 * **NOT the same contract as the legacy `onCollision`.** The legacy
 * handler receives a different response shape (fixed `a`/`b` per pair,
 * `overlapV` sign convention from b → a, fires 2× per frame for
 * dynamic-dynamic pairs). See `Renderable.onCollision` JSDoc for that
 * one.
 * @example
 *   onCollisionActive(response, other) {
 *       if (response.normal.y < -0.7) {
 *           // push-me-up direction ⇒ I'm on top of `other` (stomp)
 *       }
 *   }
 */
export interface CollisionResponse {
	/** the renderable whose handler is firing — always `=== this`. */
	a: Renderable;
	/** the partner renderable — always `=== other`. */
	b: Renderable;
	/**
	 * Unit minimum-translation vector for the receiver: the direction `a`
	 * must move to separate from `b`. Same sign convention across every
	 * adapter — in canvas coordinates (y grows downward):
	 *
	 * - `normal.y < -0.7` → push me **up** ⇒ I'm on top of `b` (stomp / landed on)
	 * - `normal.y >  0.7` → push me **down** ⇒ I'm underneath `b`
	 * - `Math.abs(normal.x) > 0.7` → mostly horizontal contact (side hit)
	 */
	normal: { x: number; y: number };
	/** Penetration depth along the shortest-separation axis (always positive). */
	depth: number;
	/**
	 * Engine-native contact pair object. **Shape is adapter-specific** —
	 * `Matter.Pair` under `@melonjs/matter-adapter`, a `ContactPair`-shaped
	 * object under a future Rapier adapter, a `b2Contact` under a future
	 * Box2D adapter, etc. Reading this commits your code to a specific
	 * engine; use the portable `normal` / `depth` fields for
	 * adapter-portable handlers.
	 *
	 * Present only when the active adapter has a native pair concept;
	 * `undefined` under the built-in SAT adapter (which has no native
	 * pair representation — the SAT detector works pair-by-pair without
	 * persisting them).
	 */
	pair?: unknown;
	/**
	 * @deprecated Use `depth`. Built-in SAT legacy field; kept on modern
	 * handler dispatches for migration. `undefined` under
	 * `@melonjs/matter-adapter`.
	 */
	overlap?: number;
	/**
	 * @deprecated Use `normal`. Built-in SAT legacy field; flipped per
	 * receiver so the sign convention aligns with `normal` on the modern
	 * dispatch. `undefined` under `@melonjs/matter-adapter`.
	 */
	overlapN?: { x: number; y: number };
	/**
	 * @deprecated Built-in SAT legacy field, equal to `normal × depth`.
	 * Flipped per receiver. `undefined` under `@melonjs/matter-adapter`.
	 */
	overlapV?: { x: number; y: number };
}

/**
 * Portable physics body handle returned by {@link PhysicsAdapter.addBody}
 * and stored on `renderable.body`. Every adapter guarantees these
 * methods so engine-portable code can read and mutate body state
 * without knowing which adapter is active.
 *
 * Each adapter exports a concrete `Body` type (e.g.
 * `MatterAdapter.Body`, `BuiltinAdapter.Body`) that extends this
 * interface with its native fields — cast to the adapter-specific type
 * only when reaching for engine-specific state (Matter's `frictionAir`,
 * BuiltinAdapter's `vel`/`force`, etc.).
 *
 * Methods marked optional are not implemented by every adapter; check
 * the adapter docs or fall back to the equivalent on the adapter
 * itself (e.g. `adapter.setVelocity(r, v)` always works).
 */
export interface PhysicsBody {
	/** Set linear velocity in viewport pixels per frame. */
	setVelocity(x: number, y: number): void;
	/** Read linear velocity; writes into `out` if provided to avoid an alloc. */
	getVelocity(out?: Vector2d): Vector2d;
	/**
	 * Apply a continuous force (integrated over the next step). When the
	 * optional application point is provided and differs from the body's
	 * centroid, a corresponding torque is generated:
	 * `τ = r × F`, where `r = point - centroid`. BuiltinAdapter routes the
	 * torque into {@link applyTorque}; MatterAdapter passes the point
	 * directly to `Matter.Body.applyForce`.
	 * @example
	 * // pure linear thrust (existing 2-arg form, unchanged):
	 * ship.body.applyForce(0, -0.05);
	 *
	 * // off-centre kick: pushes the crate to the right AND tips it over,
	 * // because the contact point is at the top of the crate, away from
	 * // its centroid:
	 * const topX = crate.pos.x + crate.width / 2;
	 * const topY = crate.pos.y;
	 * crate.body.applyForce(1.5, 0, topX, topY);
	 * @param x - force X component
	 * @param y - force Y component
	 * @param pointX - world X of the application point (defaults to centroid)
	 * @param pointY - world Y of the application point (defaults to centroid)
	 */
	applyForce(x: number, y: number, pointX?: number, pointY?: number): void;
	/** Apply an instantaneous impulse (`Δv = J / mass`). */
	applyImpulse(x: number, y: number): void;
	/**
	 * Toggle static (fixed-position, infinite-mass) state. Static bodies
	 * still participate in collisions; they just don't integrate.
	 */
	setStatic(isStatic?: boolean): void;
	/**
	 * Set the bit mask of collision types this body collides with.
	 * @see collision.types
	 */
	setCollisionMask(mask: number): void;
	/** Set the collision category bit for this body. */
	setCollisionType?(type: number): void;
	/** Toggle sensor mode (fires collision events without push-out). */
	setSensor?(isSensor?: boolean): void;
	/** Set mass directly (recomputes inertia where applicable). */
	setMass?(m: number): void;
	/** Set restitution / bounciness. */
	setBounce?(r: number): void;
	/** Set per-body gravity multiplier. */
	setGravityScale?(scale: number): void;
	/**
	 * Set angular velocity (rad / frame). BuiltinAdapter integrates this
	 * each step into the body's `angle` and updates the renderable's
	 * `currentTransform` (visual rotation only — SAT collisions remain
	 * axis-aligned). MatterAdapter routes to `Matter.Body.setAngularVelocity`,
	 * which participates fully in matter's rotational dynamics.
	 * @example
	 * // a coin sprite that spins continuously while sitting on the ground:
	 * coin.body.setAngularVelocity?.(0.05);
	 */
	setAngularVelocity?(omega: number): void;
	/**
	 * Read angular velocity (rad / frame). Returns 0 if rotation isn't tracked.
	 * @example
	 * if ((spinner.body.getAngularVelocity?.() ?? 0) > 1) {
	 *     spinner.body.setAngularVelocity?.(1); // cap spin
	 * }
	 */
	getAngularVelocity?(): number;
	/**
	 * Set absolute rotation angle (radians). Re-syncs the renderable's
	 * `currentTransform` immediately — no need to wait for the next
	 * physics step. Useful when you want to point a body at a target.
	 * @example
	 * // turret tracks the player every frame:
	 * const dx = player.centerX - turret.centerX;
	 * const dy = player.centerY - turret.centerY;
	 * turret.body.setAngle?.(Math.atan2(dy, dx));
	 */
	setAngle?(rad: number): void;
	/** Read absolute rotation angle (radians). */
	getAngle?(): number;
	/**
	 * Apply an angular impulse directly: `Δω = τ / pseudoInertia`. Bypasses
	 * the force/lever-arm computation in {@link applyForce} for the case
	 * where you want to spin something up without applying a linear force —
	 * a thruster, a power-up's intrinsic rotation, a knockback spin effect.
	 * @example
	 * // give a powerup a one-shot 360° spin-up burst on collection:
	 * powerup.body.applyTorque?.(50);
	 */
	applyTorque?(torque: number): void;
	/** Live alias of the collision category bit. */
	collisionType: number;
	/** Live alias of the collision mask bits. */
	collisionMask: number;
}

/**
 * Engine-portable body description, passed to {@link PhysicsAdapter.addBody}.
 * Adapter-native fields (Box2D meters, Matter collisionFilter bits, etc.)
 * are derived from these portable fields by each adapter.
 */
export interface BodyDefinition {
	/** simulation kind */
	type: BodyType;
	/** collision shapes (one or more — compound body support) */
	shapes: BodyShape[];

	/** mass per unit area; defaults are adapter-specific */
	density?: number;
	/**
	 * Per-step velocity damping (Matter's `frictionAir`). Bleeds velocity
	 * off every frame regardless of contact, creating terminal velocity.
	 * Number applies uniformly; `{x, y}` damps each axis independently
	 * (melonJS-specific — Matter only supports scalar and will average).
	 */
	frictionAir?: number | { x: number; y: number };
	/**
	 * Bounciness (coefficient of restitution): `0` = inelastic (stops on
	 * contact), `1` = perfectly elastic (rebound speed equals impact speed).
	 * Typically in `[0, 1]`. Values `> 1` produce a super-elastic
	 * ("energy-gain") rebound — physically unrealistic but useful for
	 * arcade effects like pinball flippers, slingshot boosters, or
	 * trampoline pads. The value is not clamped at the interface level;
	 * how an adapter handles out-of-range values is adapter-specific.
	 */
	restitution?: number;
	/**
	 * Surface coefficient of friction during contact. Matter's
	 * `body.friction` — `0` = frictionless (objects slide past each
	 * other), `1` = high stick. Determines how much tangential velocity
	 * is transferred between contacting bodies. Combined with body
	 * rotation, this is what produces "throw" between colliding circles
	 * and "english" off a wall. Distinct from `frictionAir` (per-step
	 * drag with no contact required). Builtin SAT adapter ignores this.
	 */
	friction?: number;
	/**
	 * Per-body gravity multiplier. `0` disables gravity for this body;
	 * negative inverts it. Matches Matter's `body.gravityScale`.
	 */
	gravityScale?: number;
	/**
	 * Hard cap on velocity magnitude per axis. melonJS extension —
	 * Matter has no direct equivalent and uses `frictionAir`-induced
	 * terminal velocity instead; MatterAdapter implements this by
	 * clamping velocity in an `afterUpdate` hook.
	 */
	maxVelocity?: { x: number; y: number };

	/** disable rotation simulation; the body keeps its initial angle */
	fixedRotation?: boolean;
	/** the body generates collision events but no physical response */
	isSensor?: boolean;

	/**
	 * Bit flag identifying this body's collision type. See
	 * {@link collision.types}. Adapters translate to their native filter
	 * system (Matter `collisionFilter.category`, Box2D `categoryBits`).
	 */
	collisionType?: number;
	/**
	 * Bit mask of collision types this body can collide with. Defaults to
	 * `collision.types.ALL_OBJECT`.
	 */
	collisionMask?: number;

	/** arbitrary user data attached to the body */
	userData?: unknown;
}

/**
 * Adapter feature flags. The engine reads these to negotiate optional
 * capabilities; user code can branch on them to gracefully degrade when
 * a feature isn't available under the active adapter.
 */
export interface AdapterCapabilities {
	/** supports joints, springs, hinges, etc. */
	constraints: boolean;
	/** supports continuous collision detection for fast-moving bodies */
	continuousCollisionDetection: boolean;
	/** supports body sleeping to reduce simulation cost when idle */
	sleepingBodies: boolean;
	/** supports {@link PhysicsAdapter.raycast} */
	raycasts: boolean;
	/**
	 * supports {@link PhysicsAdapter.raycast3d}. 2D-only adapters
	 * (matter, planck) report `false` and omit the method; users can
	 * branch on this flag before calling
	 * `world.adapter.raycast3d?.(...)` to skip a no-op delegation.
	 */
	raycasts3d: boolean;
	/**
	 * supports {@link BodyDefinition.maxVelocity} /
	 * {@link PhysicsAdapter.setMaxVelocity}. BuiltinAdapter does this
	 * via its kinematic integrator; MatterAdapter via an `afterUpdate`
	 * clamp. Adapters that can't enforce a hard cap report `false`.
	 */
	velocityLimit: boolean;
	/**
	 * supports {@link PhysicsAdapter.isGrounded}. Adapters that can't
	 * cheaply derive ground contact (no contact iteration) report `false`.
	 */
	isGrounded: boolean;
}

/**
 * Common adapter constructor options. Concrete adapters may extend this
 * with their own engine-specific options.
 */
export interface AdapterOptions {
	/** world gravity vector, default `(0, 0.98)` */
	gravity?: Vector2d;
}

/** Result of a successful {@link PhysicsAdapter.raycast}. */
export interface RaycastHit {
	/** the renderable whose body the ray hit */
	renderable: Renderable;
	/** world-space hit point */
	point: Vector2d;
	/** surface normal at the hit point */
	normal: Vector2d;
	/** position along the ray, `0..1` from `from` to `to` */
	fraction: number;
}

/**
 * Result of a successful {@link PhysicsAdapter.raycast3d}. Same
 * shape as {@link RaycastHit}, with Vector3d in place of Vector2d.
 * 3D adapters approximate each renderable's collision volume as a
 * bounding sphere centred on `renderable.getAbsolutePosition()`
 * with radius = the renderable's bounds circumradius — same model
 * `Camera3d.isVisible` uses for frustum culling.
 */
export interface RaycastHit3d {
	/** the renderable the ray hit */
	renderable: Renderable;
	/** world-space hit point (sphere entry) */
	point: Vector3d;
	/** surface normal at the hit point (outward from sphere centre) */
	normal: Vector3d;
	/** position along the ray, `0..1` from `from` to `to` */
	fraction: number;
}

/**
 * Swappable physics engine integration. `BuiltinAdapter` ships with
 * melonjs and wraps the engine's native SAT-based physics; third-party
 * adapters (e.g. `@melonjs/matter-adapter`) implement this interface to
 * plug a different engine in.
 *
 * **Lifecycle contract:** one adapter per {@link World}, instantiated and
 * passed to `Application` at construction, never swapped at runtime.
 * Adapter state (bodies, constraints, sleeping flags, etc.) does not
 * migrate between adapters.
 *
 * **Body handle convention:** {@link PhysicsAdapter.addBody} returns an
 * opaque handle that becomes `renderable.body`. Each adapter chooses its
 * own concrete body type — `BuiltinAdapter` returns the legacy
 * {@link Body} class so existing property-based code (`body.vel.x = 5`,
 * `body.isStatic = true`) keeps working unchanged. Third-party adapters
 * return their engine's native body type. For adapter-agnostic code, use
 * the portable methods on the adapter itself ({@link setVelocity},
 * {@link applyForce}, etc.) instead of mutating the handle directly.
 */
export interface PhysicsAdapter {
	/** advertised capabilities; user code may branch on these */
	readonly capabilities: AdapterCapabilities;

	/**
	 * Short adapter identifier exposed as `world.physic`. User code uses
	 * it to branch on which physics implementation is active without
	 * importing the adapter class — e.g.
	 *
	 * ```ts
	 * if (app.world.physic === "matter") {
	 *     // matter-only setup (constraints, etc.)
	 * }
	 * ```
	 *
	 * Convention: a single lowercase token. The first-party labels are
	 * `"builtin"` (default — `BuiltinAdapter`) and `"matter"`
	 * (`@melonjs/matter-adapter`). Third-party adapters should pick a
	 * concise identifier that won't collide with future official ones.
	 *
	 * The reserved value `"none"` is set on `world.physic` only when the
	 * user passes `physic: "none"` to `Application` to disable physics
	 * entirely; adapters should not use it.
	 *
	 * Defaults to `"builtin"` if an adapter doesn't declare its own — keeps
	 * legacy adapters wired in before this field was added still working.
	 */
	readonly physicLabel?: string;

	/**
	 * Optional display name reported on the startup banner. Defaults to
	 * the adapter class name. Third-party packages typically set this to
	 * their npm package id (e.g. `"@melonjs/matter-adapter"`).
	 */
	readonly name?: string;
	/**
	 * Optional package version reported on the startup banner. Set this
	 * when shipping the adapter as a separate npm package so users can
	 * tell which version is wired in at a glance.
	 */
	readonly version?: string;
	/**
	 * Optional URL (npm / homepage / repo) reported on the startup banner.
	 * Convention matches the debug-plugin's startup line.
	 */
	readonly url?: string;

	/** world gravity. Mutate to change at runtime. */
	gravity: Vector2d;

	/**
	 * Called once after the adapter is attached to a {@link World}.
	 * Adapters may register internal listeners, allocate native engine
	 * state, or read world bounds here.
	 */
	init?(world: World): void;

	/** Called when the adapter is being torn down; release native resources. */
	destroy?(): void;

	/** Advance the simulation by one frame. Called from `World.update(dt)`. */
	step(dt: number): void;

	/**
	 * Copy physics-engine body positions back to `renderable.pos` and
	 * rotations to the renderable's transform. Called after {@link step}
	 * each frame. Adapters that mutate the renderable directly during
	 * step (e.g. `BuiltinAdapter`) may leave this a no-op.
	 */
	syncFromPhysics(): void;

	/**
	 * Optional inverse of {@link syncFromPhysics} — push a single
	 * renderable's current pos/angle into the physics engine. Called by
	 * the engine when game code teleports a renderable. Adapters may
	 * also expose this via {@link setPosition} and skip implementing
	 * it directly.
	 */
	syncToPhysics?(renderable: Renderable): void;

	/**
	 * Register a body with the simulation. Returns an opaque handle that
	 * becomes `renderable.body`. Each adapter chooses its own concrete
	 * body type — see the class doc for the convention.
	 *
	 * **Prefer the declarative path.** Set `renderable.bodyDef` and call
	 * `Container.addChild(renderable)` — the container auto-invokes
	 * `addBody` AND inserts the renderable into the world's broadphase
	 * (QuadTree) in one atomic step. Direct calls to `addBody` only
	 * register the body with this adapter; they do NOT add the renderable
	 * to the world's container hierarchy, so the broadphase won't return
	 * it as a collision candidate. A body registered via direct `addBody`
	 * without a matching `addChild` will integrate (velocity, forces) but
	 * never collide.
	 */
	addBody(renderable: Renderable, def: BodyDefinition): PhysicsBody;

	/**
	 * Unregister a body. Called automatically when `Container.removeChild`
	 * detaches the renderable; direct calls are the inverse of a direct
	 * `addBody` (rare — use `removeChild` for the normal lifecycle).
	 */
	removeBody(renderable: Renderable): void;

	/** Replace the body's collision geometry without re-creating the body. */
	updateShape?(renderable: Renderable, shapes: BodyShape[]): void;

	/**
	 * Portable velocity / force / position API. Every adapter implements
	 * these by routing to its native engine. Use these instead of
	 * mutating the body handle directly when writing adapter-agnostic
	 * code.
	 */
	getVelocity(renderable: Renderable, out?: Vector2d): Vector2d;
	setVelocity(renderable: Renderable, v: Vector2d): void;
	applyForce(renderable: Renderable, force: Vector2d, point?: Vector2d): void;
	applyImpulse(
		renderable: Renderable,
		impulse: Vector2d,
		point?: Vector2d,
	): void;
	setPosition(renderable: Renderable, p: Vector2d): void;
	setAngle?(renderable: Renderable, angle: number): void;
	/** Read absolute rotation angle (radians). Returns 0 if not tracked. */
	getAngle?(renderable: Renderable): number;
	/** Set angular velocity (rad / frame). */
	setAngularVelocity?(renderable: Renderable, omega: number): void;
	/** Read angular velocity (rad / frame). Returns 0 if not tracked. */
	getAngularVelocity?(renderable: Renderable): number;
	/** Apply an angular impulse (`Δω = τ / inertia`). */
	applyTorque?(renderable: Renderable, torque: number): void;

	/**
	 * Runtime body-property mutators. Each maps to the corresponding
	 * {@link BodyDefinition} field and lets game code change a body's
	 * physical properties without re-creating it. Adapter implementations
	 * route to their native engine (BuiltinAdapter writes to the `Body`
	 * handle; MatterAdapter calls Matter's `Body.set*` helpers).
	 */
	setStatic(renderable: Renderable, isStatic: boolean): void;
	setGravityScale(renderable: Renderable, scale: number): void;
	setFrictionAir(
		renderable: Renderable,
		friction: number | { x: number; y: number },
	): void;
	setMaxVelocity(renderable: Renderable, limit: { x: number; y: number }): void;
	/**
	 * Read the body's current velocity cap (mirror of
	 * {@link setMaxVelocity}). Returns plain `{x, y}` so callers don't
	 * need to import a vector type. Optional — adapters that don't
	 * implement velocity caps omit this method.
	 */
	getMaxVelocity?(renderable: Renderable): {
		x: number;
		y: number;
	};
	setCollisionType(renderable: Renderable, type: number): void;
	setCollisionMask(renderable: Renderable, mask: number): void;
	/**
	 * Toggle a body between solid and sensor mode. A sensor still fires
	 * collision events (`onCollisionStart` / `onCollisionActive` /
	 * `onCollisionEnd`) but the engine does not push the bodies apart on
	 * contact — useful for one-way platforms, trigger zones, ground-snap
	 * ground assists, etc.
	 *
	 * Adapters without a native sensor flag emulate by toggling the
	 * collision mask between its previous value and `NO_OBJECT`.
	 */
	setSensor?(renderable: Renderable, isSensor: boolean): void;

	/**
	 * Whether the body has at least one active contact with a surface
	 * below it (collision normal pointing up). Capability-gated by
	 * {@link AdapterCapabilities.isGrounded}. melonJS extension — Matter
	 * has no direct equivalent and the MatterAdapter implements it by
	 * scanning active pairs each call.
	 */
	isGrounded?(renderable: Renderable): boolean;

	/**
	 * Spatial queries.
	 *
	 * `raycast` is capability-gated by {@link AdapterCapabilities.raycasts}.
	 * Adapters that don't support it may omit the method entirely
	 * (`typeof adapter.raycast === "function"`).
	 *
	 * `queryAABB` is mandatory — every adapter must support a region query
	 * (a broadphase walk is already needed for collision detection, so
	 * exposing it costs nothing).
	 */
	raycast?(from: Vector2d, to: Vector2d): RaycastHit | null;
	/**
	 * Optional 3D variant of {@link PhysicsAdapter.raycast}. Walks
	 * the world's {@link Octree} (active only under `cameraClass:
	 * Camera3d`) and returns the nearest renderable whose bounding
	 * sphere the ray intersects. 2D adapters omit this method;
	 * call sites should check `capabilities.raycasts3d` first.
	 */
	raycast3d?(from: Vector3d, to: Vector3d): RaycastHit3d | null;
	queryAABB(rect: Rect): Renderable[];
	/**
	 * Optional 3D sphere region query — returns renderables whose
	 * `getAbsolutePosition()` lies within `radius` of `center`.
	 * The narrow phase is a point-in-sphere test against each
	 * candidate's centre; callers wanting bounding-sphere overlap
	 * should intersect with the candidate's bounds afterwards.
	 *
	 * Two call shapes:
	 * - `querySphere(center: Vector3d, radius: number)` — loose form
	 * - `querySphere(sphere: Sphere)` — packaged form, the
	 *   idiomatic surface once a query shape needs to be passed
	 *   around or cached
	 *
	 * Active only under a 3D broadphase ({@link Octree}, set when
	 * `world.sortOn === "depth"`). Adapters that don't expose a 3D
	 * spatial index omit the method.
	 * @example
	 * // Loose form — quick one-off query:
	 * const nearby = world.adapter.querySphere?.(enemy.pos, HIT_RADIUS);
	 * @example
	 * // Idiomatic form — give an entity its own bounding sphere and
	 * // reuse it each frame:
	 * import { Sphere } from "melonjs";
	 *
	 * class Enemy extends Renderable {
	 *   collider = new Sphere(0, 0, 0, 30);
	 *
	 *   update(dt) {
	 *     // keep the sphere glued to this renderable's pose
	 *     this.collider.pos.set(this.pos.x, this.pos.y, this.pos.z);
	 *     // query — returns every renderable whose centre is within
	 *     // this enemy's collider
	 *     const nearby = world.adapter.querySphere?.(this.collider) ?? [];
	 *     for (const r of nearby) {
	 *       // narrow-phase / kind filter / etc.
	 *     }
	 *     return super.update(dt);
	 *   }
	 * }
	 */
	querySphere?(center: Vector3d, radius: number): Renderable[];
	querySphere?(sphere: Sphere): Renderable[];

	/**
	 * Return the body's axis-aligned bounding box in **renderable-local**
	 * coordinates (relative to `renderable.pos`). Writes into the
	 * supplied `out` Bounds so callers can poll allocation-free each
	 * frame. Returns `undefined` if the renderable has no registered
	 * body.
	 *
	 * Used by ecosystem tooling — most notably `@melonjs/debug-plugin`
	 * — to visualize the body's effective collision extent. The adapter
	 * owns coordinate-system conversion: builtin Body already stores
	 * bounds in local space, matter stores them in world space and the
	 * adapter subtracts `renderable.pos` before writing.
	 *
	 * Required by the adapter contract: any adapter that registers
	 * bodies MUST implement this. The output is the canonical, local-
	 * space representation tools rely on regardless of which engine
	 * the adapter wraps.
	 */
	getBodyAABB(renderable: Renderable, out: Bounds): Bounds | undefined;

	/**
	 * Return a snapshot of the body's collision shapes in
	 * **renderable-local** coordinates. The returned array is intended
	 * for read-only inspection (debug drawing, hit-region queries) —
	 * adapters may return a live reference for performance, so callers
	 * MUST NOT mutate it. Returns an empty array if no body.
	 *
	 * Pairs with {@link getBodyAABB} as the adapter-side debug surface;
	 * lets each adapter own its coordinate convention without polluting
	 * the underlying physics-engine body objects. Required by the
	 * adapter contract.
	 */
	getBodyShapes(renderable: Renderable): readonly BodyShape[];
}
