// A loaded GLB model — its own node kind (a GLB is a node hierarchy with baked animation clips),
// distinct from Mesh (raw primitives, no animation). Animation is always present, reached as
// model.anim:
//   const hero = await Model.load(asset('./hero.glb'))
//   hero.anim.play('Run')

import { fetch, type FetchResponse } from "../runtime/fetch"
import { Node } from "./Node"
import { ModelAnimation } from "./ModelAnimation"

export class Model extends Node {
  /** GLB skeletal animation — always present on a Model. */
  declare readonly anim: ModelAnimation

  constructor(internalId: number) {
    super(internalId)
    this.aspect(ModelAnimation)
  }

  /** Duplicate this model — a deep copy of the GLB (meshes, skeleton, animation clips), attached to
   *  the same parent and scene and sharing this model's current transform. The clone has its own
   *  independent animation state (reach it via clone.anim). Mirrors Model.load's culling default. */
  clone(): Model {
    const id = _creator.cloneEntity(this.id)
    _creator.setGlbCulling(id, false)
    return new Model(id)
  }

  /** Load a GLB model. Returns its root as a Model; play its baked clips via model.anim. */
  static load(
    source: string | FetchResponse,
    options: { culling?: boolean, onProgress?: (p: { loaded: number, total?: number }) => void } = {},
  ): Promise<Model> {
    if (typeof source === "string") {
      return fetch(source, { useOnce: true, onProgress: options.onProgress }).then((resp) => {
        if (resp.status >= 400) return Promise.reject(new Error(`Failed to load GLB from ${source}. HTTP ${resp.status}`))
        return Model.load(resp, options)
      })
    }
    return new Promise<Model>((resolve, reject) => {
      _creator.createGlb((source as unknown as { _id: number })._id, (entityId: number) => {
        if (entityId === 0) { reject(new Error("Failed to load GLB")); return }
        const model = new Model(entityId)
        if (options.culling !== true) _creator.setGlbCulling(entityId, false)
        resolve(model)
      }, reject)
    })
  }
}
