import type { RenderPrototype, RenderTransform } from "../render/RenderScene"
import type { Collider } from "../world/Collider"

/** Open actor behavior and representation. Concrete definitions are ordinary game
 * objects referenced directly by instances. */
export type ActorDefinition<State, World> = {
  prototype: RenderPrototype
  update: (state: State, dt: number, world: World) => void
  transform: (state: State) => RenderTransform
  collider?: (state: State, world: World) => Collider | null
}

/** Type-erased live actor. `create` captures concrete state safely, allowing one
 * level to hold unrelated actor state types without a content union. */
export type Actor<World> = {
  readonly definition: object
  readonly prototype: RenderPrototype
  readonly updateState: (dt: number, world: World) => void
  readonly readTransform: () => RenderTransform
  readonly readCollider: (world: World) => Collider | null
}

export namespace Actor {
  export function create<State, World>(
    definition: ActorDefinition<State, World>,
    state: State,
  ): Actor<World> {
    return {
      definition,
      prototype: definition.prototype,
      updateState: (dt, world) => definition.update(state, dt, world),
      readTransform: () => definition.transform(state),
      readCollider: (world) => definition.collider?.(state, world) ?? null,
    }
  }

  export function update<World>(actor: Actor<World>, dt: number, world: World): void {
    actor.updateState(dt, world)
  }

  export function transform<World>(actor: Actor<World>): RenderTransform {
    return actor.readTransform()
  }

  export function collider<World>(actor: Actor<World>, world: World): Collider | null {
    return actor.readCollider(world)
  }
}
