import type { Entity, FilteredEntity, RemoveEntityOptions, HierarchyEntry, HierarchyIteratorOptions } from "./types";
import QueryCache from "./query-cache";
export default class EntityManager<ComponentTypes> {
    private nextId;
    private entities;
    private componentIndices;
    /**
     * Callbacks registered for component additions
     */
    private addedCallbacks;
    /**
     * Callbacks registered for component removals
     */
    private removedCallbacks;
    /**
     * Hierarchy manager for parent-child relationships
     */
    private hierarchyManager;
    /**
     * Per-type component dispose callbacks.
     * Called when a component is removed (explicit removal, entity destruction, or replacement).
     */
    private disposeCallbacks;
    /**
     * Per-entity per-component change sequence tracking.
     * Flat storage: changeSeqs[entityId][componentIdx] = seq number when last changed.
     * Component names are mapped to dense indices via componentNameToIdx.
     * Uint32Array zero-init means "never changed" (seq numbers start at 1).
     */
    private changeSeqs;
    private componentNameToIdx;
    private _idxCache0Name;
    private _idxCache0Idx;
    private _idxCache1Name;
    private _idxCache1Idx;
    /**
     * Subscription bitmap for change tracking. `null` means track all (the
     * default); a Uint8Array means explicit-only, with 1 at indices that opted
     * in via `subscribeChanged`. Indices outside the array's bounds are
     * treated as 0 (not tracked).
     */
    private _subscribedComponentIdx;
    /**
     * Monotonic sequence counter for change detection.
     * Each markChanged call increments this and stamps the new value.
     */
    private _changeSeq;
    private _afterComponentAddedHooks;
    private _afterEntityMutatedHooks;
    private _afterComponentRemovedHooks;
    private _beforeEntityRemovedHooks;
    private _afterParentChangedHooks;
    /**
     * Incrementally-maintained query result cache. Caches the static portion
     * (with / without / parentHas) of each registered query shape and is
     * updated via the lifecycle hook arrays above. Lazily created on the
     * first cacheable query lookup.
     */
    private readonly _queryCache;
    private _batchingDepth;
    private _batchedEntityIds;
    /** Component keys being added in the current addComponents batch, if any.
     *  Used by required component resolution to skip auto-adding explicitly provided components. */
    _pendingBatchKeys: ReadonlySet<keyof ComponentTypes> | null;
    get entityCount(): number;
    createEntity(): Entity<ComponentTypes>;
    /**
     * Register a dispose callback for a component type.
     * Called when a component is removed (explicit removal, entity destruction, or replacement).
     * Later registrations replace earlier ones for the same component type.
     * @param componentName The component type to register disposal for
     * @param callback Function receiving the component value being disposed and the entity ID
     */
    registerDispose<ComponentName extends keyof ComponentTypes>(componentName: ComponentName, callback: (ctx: {
        value: ComponentTypes[ComponentName];
        entityId: number;
    }) => void): void;
    /**
     * Get all registered dispose callbacks.
     * @internal Used by ECSpresso for plugin installation
     */
    getDisposeCallbacks(): Map<keyof ComponentTypes, (ctx: {
        value: unknown;
        entityId: number;
    }) => void>;
    /**
     * Invoke the dispose callback for a component, if registered.
     * Errors are caught and logged to prevent blocking removal.
     */
    private invokeDispose;
    addComponent<ComponentName extends keyof ComponentTypes>(entityId: number, componentName: ComponentName, data: ComponentTypes[ComponentName]): this;
    /**
     * Add multiple components to an entity at once
     * @param entityId Entity ID to add components to
     * @param components Object with component names as keys and component data as values
     */
    addComponents<T extends {
        [K in keyof ComponentTypes]?: ComponentTypes[K];
    }>(entityId: number, components: T & Record<Exclude<keyof T, keyof ComponentTypes>, never>): this;
    removeComponent<ComponentName extends keyof ComponentTypes>(entityId: number, componentName: ComponentName): this;
    getComponent<ComponentName extends keyof ComponentTypes>(entityId: number, componentName: ComponentName): ComponentTypes[ComponentName] | undefined;
    getEntitiesWithQuery<WithComponents extends keyof ComponentTypes = never, WithoutComponents extends keyof ComponentTypes = never>(required?: ReadonlyArray<WithComponents>, excluded?: ReadonlyArray<WithoutComponents>, changed?: ReadonlyArray<keyof ComponentTypes>, changeThreshold?: number, parentHas?: ReadonlyArray<keyof ComponentTypes>, changedIdx?: ReadonlyArray<number>): Array<FilteredEntity<ComponentTypes, WithComponents extends never ? never : WithComponents, WithoutComponents extends never ? never : WithoutComponents>>;
    /**
     * Fill an existing array with entities matching the query, clearing it first.
     * Returns the same array reference for convenience.
     *
     * `changedIdx`, when supplied, skips the per-call name→idx resolution loop.
     * The framework pre-resolves it once at system registration; ad-hoc callers
     * may omit it and pay the per-call lookup cost.
     */
    getEntitiesWithQueryInto<WithComponents extends keyof ComponentTypes = never, WithoutComponents extends keyof ComponentTypes = never>(output: Array<FilteredEntity<ComponentTypes, WithComponents extends never ? never : WithComponents, WithoutComponents extends never ? never : WithoutComponents>>, required?: ReadonlyArray<WithComponents>, excluded?: ReadonlyArray<WithoutComponents>, changed?: ReadonlyArray<keyof ComponentTypes>, changeThreshold?: number, parentHas?: ReadonlyArray<keyof ComponentTypes>, changedIdx?: ReadonlyArray<number>): Array<FilteredEntity<ComponentTypes, WithComponents extends never ? never : WithComponents, WithoutComponents extends never ? never : WithoutComponents>>;
    /** Test-only accessor for the internal query cache. @internal */
    get _queryCacheForTesting(): QueryCache<ComponentTypes>;
    removeEntity(entityId: number, options?: RemoveEntityOptions): boolean;
    /**
     * Internal method to remove a single entity without cascade logic
     */
    private removeEntityInternal;
    getEntity(entityId: number): Entity<ComponentTypes> | undefined;
    /**
     * Register a callback when a specific component is added to any entity
     * @param componentName The component key
     * @param handler Function receiving the new component value and the entity
     * @returns Unsubscribe function to remove the callback
     */
    onComponentAdded<ComponentName extends keyof ComponentTypes>(componentName: ComponentName, handler: (ctx: {
        value: ComponentTypes[ComponentName];
        entity: Entity<ComponentTypes>;
    }) => void): () => void;
    /**
     * Register a callback when a specific component is removed from any entity
     * @param componentName The component key
     * @param handler Function receiving the old component value and the entity
     * @returns Unsubscribe function to remove the callback
     */
    onComponentRemoved<ComponentName extends keyof ComponentTypes>(componentName: ComponentName, handler: (ctx: {
        value: ComponentTypes[ComponentName];
        entity: Entity<ComponentTypes>;
    }) => void): () => void;
    onAfterComponentAdded(hook: (entityId: number, componentName: keyof ComponentTypes) => void): () => void;
    onAfterEntityMutated(hook: (entityId: number) => void): () => void;
    onAfterComponentRemoved(hook: (entityId: number, componentName: keyof ComponentTypes) => void): () => void;
    onBeforeEntityRemoved(hook: (entityId: number) => void): () => void;
    onAfterParentChanged(hook: (childId: number) => void): () => void;
    /**
     * The current monotonic change sequence value.
     * Each markChanged call increments this before stamping.
     */
    get changeSeq(): number;
    /**
     * Mark a component as changed on an entity, stamping the next sequence number.
     * @param entityId The entity ID
     * @param componentName The component that changed
     */
    markChanged<K extends keyof ComponentTypes>(entityId: number, componentName: K): void;
    /**
     * Fast-path companion to markChanged that skips the component-name lookup.
     * Use after resolving names to indices once via getOrAssignComponentIdx.
     */
    markChangedByIdx(entityId: number, componentIdx: number): void;
    getOrAssignComponentIdx<K extends keyof ComponentTypes>(componentName: K): number;
    /**
     * @internal Subscribe a component to change tracking. First call transitions
     * from default track-all (null bitmap) to explicit-only mode; subsequent calls
     * extend the subscription set. Called by ECSpresso._registerSystem for each
     * component named in a query's `changed:` filter.
     */
    subscribeChanged<K extends keyof ComponentTypes>(componentName: K): void;
    /**
     * @internal Opt out of change tracking entirely. Installs an empty bitmap,
     * making every markChanged call a no-op until subscribeChanged is called.
     * Used by `.disableChangeTracking()` on the builder for worlds with no
     * reactive consumers.
     */
    disableChangeTracking(): void;
    /**
     * @internal True if at least one of `idxs` is currently subscribed for
     * change tracking (or the bitmap is null = track-all). Used by the
     * auto-mark walk to skip entity iteration when every mark would be a no-op.
     */
    hasAnySubscribed(idxs: ReadonlyArray<number>): boolean;
    /**
     * Get the sequence number at which a component was last changed on an entity
     * @param entityId The entity ID
     * @param componentName The component to check
     * @returns The sequence number when last changed, or -1 if never changed
     */
    getChangeSeq<K extends keyof ComponentTypes>(entityId: number, componentName: K): number;
    /**
     * Create an entity as a child of another entity with initial components
     * @param parentId The parent entity ID
     * @param components Initial components to add
     * @returns The created child entity
     */
    spawnChild<T extends {
        [K in keyof ComponentTypes]?: ComponentTypes[K];
    }>(parentId: number, components: T & Record<Exclude<keyof T, keyof ComponentTypes>, never>): FilteredEntity<ComponentTypes, keyof T & keyof ComponentTypes>;
    /**
     * Set the parent of an entity
     * @param childId The entity ID to set as a child
     * @param parentId The entity ID to set as the parent
     */
    setParent(childId: number, parentId: number): this;
    /**
     * Remove the parent relationship for an entity (orphan it)
     * @param childId The entity ID to orphan
     * @returns true if a parent was removed, false if entity had no parent
     */
    removeParent(childId: number): boolean;
    /**
     * Get the parent of an entity
     * @param entityId The entity ID to get the parent of
     * @returns The parent entity ID, or null if no parent
     */
    getParent(entityId: number): number | null;
    /**
     * Get all children of an entity in insertion order
     * @param parentId The parent entity ID
     * @returns Readonly array of child entity IDs
     */
    getChildren(parentId: number): readonly number[];
    /**
     * Get a child at a specific index
     * @param parentId The parent entity ID
     * @param index The index of the child
     * @returns The child entity ID, or null if index is out of bounds
     */
    getChildAt(parentId: number, index: number): number | null;
    /**
     * Get the index of a child within its parent's children list
     * @param parentId The parent entity ID
     * @param childId The child entity ID to find
     * @returns The index of the child, or -1 if not found
     */
    getChildIndex(parentId: number, childId: number): number;
    /**
     * Get all ancestors of an entity in order [parent, grandparent, ...]
     * @param entityId The entity ID to get ancestors of
     * @returns Readonly array of ancestor entity IDs
     */
    getAncestors(entityId: number): readonly number[];
    /**
     * Get all descendants of an entity in depth-first order
     * @param entityId The entity ID to get descendants of
     * @returns Readonly array of descendant entity IDs
     */
    getDescendants(entityId: number): readonly number[];
    /**
     * Get the root ancestor of an entity (topmost parent), or self if no parent
     * @param entityId The entity ID to get the root of
     * @returns The root entity ID
     */
    getRoot(entityId: number): number;
    /**
     * Get siblings of an entity (other children of the same parent)
     * @param entityId The entity ID to get siblings of
     * @returns Readonly array of sibling entity IDs
     */
    getSiblings(entityId: number): readonly number[];
    /**
     * Check if an entity is a descendant of another entity
     * @param entityId The potential descendant ID
     * @param ancestorId The potential ancestor ID
     * @returns true if entityId is a descendant of ancestorId
     */
    isDescendantOf(entityId: number, ancestorId: number): boolean;
    /**
     * Check if an entity is an ancestor of another entity
     * @param entityId The potential ancestor ID
     * @param descendantId The potential descendant ID
     * @returns true if entityId is an ancestor of descendantId
     */
    isAncestorOf(entityId: number, descendantId: number): boolean;
    /**
     * Returns true when at least one parent-child relationship exists.
     */
    get hasHierarchy(): boolean;
    /**
     * Get all root entities (entities that have children but no parent)
     * @returns Readonly array of root entity IDs
     */
    getRootEntities(): readonly number[];
    /**
     * Traverse the hierarchy in parent-first (breadth-first) order.
     * Parents are guaranteed to be visited before their children.
     * @param callback Function called for each entity with (entityId, parentId, depth)
     * @param options Optional traversal options (roots to filter to specific subtrees)
     */
    forEachInHierarchy(callback: (entityId: number, parentId: number | null, depth: number) => void, options?: HierarchyIteratorOptions): void;
    /**
     * Generator-based hierarchy traversal in parent-first (breadth-first) order.
     * Supports early termination via break.
     * @param options Optional traversal options (roots to filter to specific subtrees)
     * @yields HierarchyEntry for each entity in parent-first order
     */
    hierarchyIterator(options?: HierarchyIteratorOptions): Generator<HierarchyEntry, void, unknown>;
}
