// A trigger zone, as an aspect on a 3D Node. Requires a Shape (its geometry); creates a static SENSOR
// body from it, so a physics body overlapping the zone fires the node's 'enter' / 'exit' events
// instead of colliding. A trigger does not block movement, and is still pointer-pickable.
//
//   const goal = new Mesh(box(), material)
//     .aspect(Shape, { box: [1, 2, 1] })
//     .aspect(Trigger)
//   goal.addEventListener('enter', other => win(other))
//   goal.addEventListener('exit',  other => …)

import { Aspect } from "../core/Aspect"
import { Shape } from "./Shape"
import { ensurePhysicsEvents } from "./physicsEvents"
import type { Node } from "./Node"

export class Trigger extends Aspect<"trigger", Node> {
  static readonly aspect = "trigger"

  private _bodyId = 0
  /** Native body id (0 if no physics support). */
  get id(): number { return this._bodyId }

  onAttach(): void {
    if (!_creator.physicsHasSupport || !_creator.physicsHasSupport()) return
    const shape = this.node.get(Shape)
    if (!shape) {
      throw new Error("Trigger requires a Shape aspect — add it first: node.aspect(Shape, {…}).aspect(Trigger)")
    }
    const shapeId = shape._claim()
    this._bodyId = _creator.physicsCreateBody(this.node.id, shapeId, 0 /* static */, 0, true /* sensor */, false)
    shape._ownBody(this._bodyId)
    ensurePhysicsEvents() // routes overlap events → the node's 'enter' / 'exit'
  }

  onDetach(): void {
    if (this._bodyId) { _creator.physicsRemoveBody(this._bodyId); this._bodyId = 0 }
    const shape = this.node.get(Shape)
    if (shape) {
      shape._ownBody(0)          // the Shape held OUR body id — forget it, or its own detach removes it twice (native crash)
      shape._recreatePickBody()
    }
  }

  // No moveTo(): `node.position = p` moves the node AND the sensor body it owns (see Node._xf).
}
