import { Object3D } from "three";

/**
 * Callback type for prefab providers.
 * @param guid The guid of the prefab to resolve
 * @returns The prefab Object3D, or null if not found
 */
export declare type PrefabProviderCallback = (guid: string) => Promise<Object3D | null>;

const registeredProviders: { [key: string]: PrefabProviderCallback } = {};

/**
 * Prefab registry for networked instantiation.
 *
 * When a remote {@link syncInstantiate} event is received, the engine looks up the prefab
 * by its guid using this registry. Both GLB-loaded objects and runtime-created objects
 * can be registered here.
 *
 * Note: {@link syncInstantiate} auto-registers prefabs if no provider exists for their guid,
 * so manual registration is only needed for custom resolution logic.
 *
 * @example
 * ```ts
 * import { Prefabs, ObjectUtils } from "@needle-tools/engine";
 *
 * const cookie = ObjectUtils.createPrimitive("Cube", { color: 0xff8c00 });
 * cookie.guid = "cookie-prefab";
 * Prefabs.register("cookie-prefab", async () => cookie);
 *
 * console.log(Prefabs.list()); // ["cookie-prefab"]
 * Prefabs.unregister("cookie-prefab");
 * ```
 *
 * @category Networking
 */
export const Prefabs = {

    /**
     * Register a prefab provider that resolves objects by guid for networked instantiation.
     * When a remote `syncInstantiate` event is received, the engine uses this to find the prefab
     * to clone on the receiving client.
     *
     * @param key The guid to register the provider for
     * @param fn Callback that returns the prefab Object3D for the given guid
     */
    register(key: string, fn: PrefabProviderCallback) {
        registeredProviders[key] = fn;
    },

    /**
     * Unregister a previously registered prefab provider.
     * @param key The guid to unregister
     */
    unregister(key: string) {
        delete registeredProviders[key];
    },

    /**
     * Returns a list of all registered prefab provider keys (guids).
     * Useful for debugging to see which prefabs are available for networked instantiation.
     */
    list(): string[] {
        return Object.keys(registeredProviders);
    },

    /**
     * Check if a prefab provider is registered for the given guid.
     * @param key The guid to check
     */
    has(key: string): boolean {
        return key in registeredProviders;
    },

    /** @internal Resolve a prefab by guid. Used by the networking system. */
    async resolve(guid: string): Promise<Object3D | null> {
        const prov = registeredProviders[guid];
        if (prov) return prov(guid);
        return null;
    },
} as const;
