import { alamos } from "@synnaxlabs/alamos";
import { type Sender, type SenderHandler } from "@synnaxlabs/x";
import { Mutex } from "async-mutex";
import { z } from "zod";
import { type MainMessage, type WorkerMessage } from '../message';
import { state } from '../../state';
type UpdateVariant = "state" | "context";
/** An update to an AetherComponent from the main React tree. */
export interface Update {
    /**
     * The variant of update being performed. State updates are used to update
     * the state of a particular component, while context updates are used to propagate
     * context changes to children in the tree.
     */
    variant: UpdateVariant;
    ctx: Context;
    /**
     * The path of the component in the tree. This path is shortened as the update is
     * propagated through the tree
     */
    path: string[];
    /**
     * The type of the component being updated. This is used to create the component
     * if it does not already exist.
     */
    type: string;
    /**
     * The state to udpate on the component . This is only present if the variant is
     * "state".
     */
    state: any;
    /**
     * instrumentation is used for logging and tracing.
     */
    instrumentation: alamos.Instrumentation;
}
/**
 * A component in the Aether tree. Each component instance has a unique key identifying
 * it within the tree, and also has a type identifying it's class. Components
 * implementing different functionality should have different types, and these types
 * typically correlate to the corresponding name of the component in the react tree.
 */
export interface Component {
    /** The type of component. */
    type: string;
    /** A unique key identifying the component within the tree. */
    key: string;
    ctx: Context;
    /**
     * Propagates an update to the internal tree of the component, updating the component
     * itself and any children as necessary.
     *
     * @param update - The update to propagate.
     */
    internalUpdate: (update: Update) => Promise<void>;
    /**
     * Propagates a delete to the internal tree of the component, calling the handleDelete
     * component on the component itself and any children as necessary. It is up to
     * the parent component to remove the child from its internal tree.
     *
     * @param path - The path of the component to delete.
     */
    internalDelete: (path: string[]) => Promise<void>;
}
/** A constructor type for an AetherComponent. */
export type ComponentConstructor = new (update: Update) => Component;
/**
 * AetherContext is used to propagate environment information to components in the
 * aether tree. It allows components to propagate state changes to the main react
 * tree, create new components, and set/get values from the environment, passing
 * them to children as necessary.
 */
export declare class Context {
    readonly providers: Map<string, any>;
    private readonly registry;
    private readonly sender;
    changedKeys: string[];
    constructor(sender: Sender<WorkerMessage>, registry: Record<string, ComponentConstructor>, providers?: Map<string, any>);
    /**
     * Proapgates the given state for the component with the given key to the main
     * react tree.
     *
     * @param key - The key of the component to propagate the state for.
     * @param state - The state to propagate.
     * @param transfer - Any transferable objects to transfer to the main thread.
     */
    propagateState(key: string, state: any, transfer?: Transferable[]): void;
    /**
     * Copies the context, setting it's 'changed' flag to false, and optionally merging
     * another set of context values into it.
     *
     * @param merge - The context to merge into the copy.
     * @returns The copied context.
     */
    copyAndMerge(merge?: Context): Context;
    get changed(): boolean;
    /**
     * Sets a value on the context and flags the context as changed.
     *
     * @param key - The key to set.
     * @param value - The value to set.
     */
    set(key: string, value: any, trigger?: boolean): void;
    setIfNotHas(key: string, value: any): void;
    /**
     * Creates a new component using the given update. It is up to the caller to
     * validate that the component type is valid.
     *
     * @param update - The update to create the component from.
     * @returns The created component.
     */
    create<C extends Component>(update: Update): Promise<C>;
    /**
     * Gets a value from the context, returning null if the value does not exist. It's
     * imporant to note that the context provides no validation of the type of the
     * value, so it is up to the caller to ensure that the value is of the correct type.
     *
     * @param key - The key to get.
     * @returns The value, or null if it does not exist.
     */
    getOptional<P>(key: string): P | null;
    /**
     * Checks if the context has a value for the given key.
     *
     * @param key - The key to check.
     * @returns True if the context has a value for the given key, false otherwise.
     */
    has(key: string): boolean;
    /**
     * Gets a value from the context, throwing an error if the value does not exist.
     * It's imporant to note that the context provides no validation of the type of
     * the value, so it is up to the caller to ensure that the value is of the correct
     *
     * @param key - The key to get.
     * @returns The value.
     */
    get<P>(key: string): P;
}
/**
 * Implements an AtherComponent that does not have any children, and servers as the base
 * class for the AetherComposite type. The corresponding react component should NOT have
 * any children that use Aether functionality; for those cases, use AetherComposite instead.
 */
export declare class Leaf<S extends z.ZodTypeAny, IS extends {} = {}> implements Component {
    readonly type: string;
    readonly key: string;
    _ctx: Context;
    private readonly _internalState;
    private _state;
    private _prevState;
    private _deleted;
    instrumentation: alamos.Instrumentation;
    schema: S | undefined;
    constructor(u: Update);
    private get _schema();
    /**
     * Sets the state on the component, communicating the change to the corresponding
     * React component on the main thread.
     *
     * @param next - The new state to set on the component. This can be the state object
     * or a pure function that takes in the previous state and returns the next state.
     */
    setState(next: state.SetArg<z.input<S> | z.output<S>, z.output<S>>): void;
    /** Returns the current context on the component. */
    get ctx(): Context;
    /** @returns the current state of the component. */
    get state(): z.output<S>;
    get internal(): IS;
    /** @returns the previous state of the component. */
    get prevState(): z.output<S>;
    get deleted(): boolean;
    /**
     * @implements AetherComponent, and should NOT be called by a subclass other than
     * AetherComposite.
     */
    internalUpdate({ variant, path, ctx, state }: Update): Promise<void>;
    /**
     * @implements AetherComponent, and should NOT be called by a subclass other than
     * AetherComposite.
     */
    internalDelete(path: string[]): Promise<void>;
    beforeUpdate(): void;
    /**
     * afterUpdate is optionally defined by a subclass, allowing the component to
     * perform some action after the component is updated. At this point, the current
     * state, previous state, derived state, and current context are all available to
     * the component.
     */
    afterUpdate(): Promise<void>;
    /**
     * Runs after the component has been spliced out of the tree. This is useful for
     * running cleanup code, such as unsubscribing from an event emitter. At this point,
     * the current state, previous state, derived state, and current context are all
     * available to the component, and this.deleted is true.
     */
    afterDelete(): Promise<void>;
    private validatePath;
}
/**
 * AetherComposite is an implementation of AetherComponent that allows it to maintain
 * child components. It is the base class for all composite components, and should not
 * be used directly.
 */
export declare class Composite<S extends z.ZodTypeAny, IS extends {} = {}, C extends Component = Component> extends Leaf<S, IS> implements Component {
    private _children;
    constructor(u: Update);
    /** @returns a readonly array of the children of the component. */
    get children(): readonly C[];
    /**
     * @implements AetherComponent, and should NOT be called by a subclass, except for
     * AetherRoot.
     */
    internalUpdate(u: Update): Promise<void>;
    private updateContext;
    private updateChild;
    private updateThis;
    internalDelete(path: string[]): Promise<void>;
    getRequiredKey(path: string[], type?: string): [string, string[]];
    /**
     * Finds a child component with the given key.
     *
     * @param key - the key of the child component to find.
     * @returns the child component, or null if no child component with the given key
     */
    getChild<T extends C = C>(key: string): T | null;
    /**
     * Finds all children of the component with the given type
     *
     * @param types - the type of the children to find
     * @returns an array of all children of the component with the given type
     */
    childrenOfType<T extends C = C>(...types: Array<T["type"]>): readonly T[];
}
export type ComponentRegistry = Record<string, ComponentConstructor>;
declare const aetherRootState: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
export interface RootProps {
    worker: SenderHandler<WorkerMessage, MainMessage>;
    registry: ComponentRegistry;
    instrumentation?: alamos.Instrumentation;
}
export declare class Root extends Composite<typeof aetherRootState> {
    wrap: SenderHandler<WorkerMessage, MainMessage>;
    private static readonly TYPE;
    private static readonly KEY;
    private static readonly ZERO_UPDATE;
    static readonly schema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
    schema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
    mu: Mutex;
    constructor({ worker: wrap, registry, instrumentation, }: RootProps);
    handle(msg: MainMessage): Promise<void>;
}
export declare const render: (props: RootProps) => Root;
export {};
