/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import type { IFluidHandle } from "@fluidframework/core-interfaces";
import type { IIdCompressor } from "@fluidframework/id-compressor";
import { type ICodecOptions, type CodecWriteOptions } from "../codec/index.js";
import { TreeNode, type Unhydrated, type ImplicitFieldSchema, type InsertableField, type TreeFieldFromImplicitField, type TreeLeafValue, type UnsafeUnknownSchema, type TreeEncodingOptions, type VerboseTree, type TreeBranch, type TreeParsingOptions, type TreeChangeEventsAlpha, type ConciseTree, type TreeContextAlpha, type TreeNodeSchema, type TreeBranchAlpha } from "../simple-tree/index.js";
import { type JsonCompatible } from "../util/index.js";
/**
 * A utility interface for manipulating node identifiers.
 * @remarks
 * This provides methods to:
 *
 * - Retrieve identifiers from nodes
 * - Generate identifiers
 * - Convert between short numeric identifiers and long string identifiers
 *
 * @alpha @sealed
 */
export interface TreeIdentifierUtils {
    /**
     * Returns the identifier of a node.
     * @remarks
     * This returns the node's UUID if and only if it has exactly one {@link SchemaFactory.identifier | identifier field}.
     * If it has no identifier field, this returns undefined.
     * If it has more than one identifier field, this throws an error.
     * In that case, query the identifier fields directly instead.
     *
     * @param node - The TreeNode you want to get the identifier from,
     */
    (node: TreeNode): string | undefined;
    /**
     * Returns the shortened identifier as a number given a UUID known by the id compressor on the branch.
     * @remarks
     * If the given string is not a valid identifier and/or was not generated by the SharedTree, this will return `undefined`.
     *
     * See {@link TreeIdentifierUtils.getShort} for additional details about shortened identifiers.
     *
     * This method is the inverse of {@link TreeIdentifierUtils.lengthen}.
     * If you shorten an identifier and then immediately pass it to {@link TreeIdentifierUtils.lengthen}, you will get the original string back.
     *
     * @param branch - The branch (and/or view) of the SharedTree that will perform the compression.
     * @param nodeIdentifier - the stable identifier to be shortened.
     */
    shorten(branch: TreeBranch, nodeIdentifier: string): number | undefined;
    /**
     * Returns the stable id as a string if the identifier is decompressible and known by the id compressor.
     * @remarks
     * If the given number does not correspond to a valid identifier generated by the SharedTree, this will return `undefined`.
     *
     * This method is the inverse of {@link TreeIdentifierUtils.shorten}.
     * If you lengthen an identifier and then immediately pass it to {@link TreeIdentifierUtils.shorten}, you will get the original short identifier back.
     *
     * @param branch - The branch (and/or view) of the SharedTree that will perform the decompression.
     * @param nodeIdentifier - The local identifier to be lengthened.
     */
    lengthen(branch: TreeBranch, nodeIdentifier: number): string;
    /**
     * Returns the {@link TreeIdentifierUtils.shorten | shortened} form of the identifier {@link SchemaFactory.identifier | identifier} for the given node.
     * @remarks
     * If the node is {@link Unhydrated | hydrated} and its identifier is a valid UUID that was automatically generated by the SharedTree it is part of (or something else using the same {@link @fluidframework/id-compressor#IIdCompressor}), then this will return a process-unique integer corresponding to that identifier.
     * This is useful for performance-sensitive scenarios involving many nodes with identifiers that need to be compactly retained in memory or used for efficient lookup.
     * Note that automatically generated identifiers that were accessed before the node was hydrated will not yield a short identifier until after hydration.
     *
     * If the node's identifier is any other user-provided string, then this will return `undefined`.
     *
     * If the node has no identifier (that is, it has no {@link SchemaFactory.identifier | identifier} field), then this returns `undefined`.
     *
     * If the node has more than one identifier, then this will throw an error.
     * In that case, retrieve the identifiers individually via their fields instead.
     *
     * The returned integer should not be serialized or preserved outside of the current process.
     * Its lifetime is that of the current in-memory instance of the FF container for this client, and it is not guaranteed to be unique or stable outside of that context.
     * The same node's identifier may, for example, be different across multiple sessions for the same client and document, or different across two clients in the same session.
     */
    getShort(node: TreeNode): number | undefined;
    /**
     * Creates a new identifier.
     * @remarks
     * The returned UUID string can be {@link TreeIdentifierUtils.shorten | shortened} for high-performance scenarios.
     *
     * @param branch - The branch (and/or view) of the SharedTree that will generate and manage the identifier.
     */
    create(branch: TreeBranch): string;
}
/**
 * Extensions to {@link (Tree:interface)} and {@link (TreeBeta:interface)} which are not yet stable.
 * @remarks
 * Use via the {@link (TreeAlpha:variable)} singleton.
 *
 * The unhydrated node creation APIs in this interface do not support {@link ObjectSchemaOptions.allowUnknownOptionalFields | unknown optional fields}.
 * This is because unknown optional fields still must have a schema: its just that the schema may come from the document's stored schema.
 * Unhydrated nodes created via this interface are not associated with any document, so there is nowhere for them to get schema for unknown optional fields.
 * Note that {@link (TreeBeta:interface).clone} can create an unhydrated node with unknown optional fields, as it uses the source node's stored schema (if any).
 *
 * Export APIs in this interface include {@link ObjectSchemaOptions.allowUnknownOptionalFields | unknown optional fields}
 * if they are using {@link KeyEncodingOptions.allStoredKeys}.
 *
 * @privateRemarks
 * TODO:
 * There should be a way to provide a source for defaulted identifiers for unhydrated node creation, either via these APIs or some way to add them to its output later.
 * If an option were added to these APIs, it could also be used to enable unknown optional fields.
 *
 * @sealed @alpha
 */
export interface TreeAlpha {
    /**
     * Register an event listener on the given node.
     * @param node - The node whose events should be subscribed to.
     * @param eventName - Which event to subscribe to.
     * @param listener - The callback to trigger for the event. The tree can be read during the callback, but it is invalid to modify the tree during this callback.
     * @returns A callback function which will deregister the event.
     * This callback should be called only once.
     * @remarks
     * Provides richer events than {@link (TreeBeta:interface).on} for array nodes:
     * - `nodeChanged` includes a {@link NodeChangedDataDelta.delta | delta} payload for direct
     * changes (insert, remove, move).
     * - `treeChanged` also includes a {@link NodeChangedDataDelta.delta | delta} payload and fires
     * for both shallow changes and deep changes (e.g. a property of an element changed without
     * any direct array change).
     */
    on<K extends keyof TreeChangeEventsAlpha<TNode>, TNode extends TreeNode>(node: TNode, eventName: K, listener: NoInfer<TreeChangeEventsAlpha<TNode>[K]>): () => void;
    /**
     * Retrieve the {@link TreeBranch | branch}, if any, for the given node.
     * @param node - The node to query
     * @remarks If the node has already been inserted into the tree, this will return the branch associated with that node's {@link TreeView | view}.
     * Otherwise, it will return `undefined` (because the node has not yet been inserted and is therefore not part of a branch or view).
     *
     * This does not fork a new branch, but rather retrieves the _existing_ branch for the node.
     * To create a new branch, use e.g. {@link TreeBranch.fork | `myBranch.fork()`}.
     *
     * @deprecated To obtain a {@link TreeBranchAlpha | branch }, use `TreeAlpha.context(node)` to obtain a {@link TreeContextAlpha | context} and then check {@link TreeContextAlpha.isBranch | isBranch()}.
     */
    branch(node: TreeNode): TreeBranchAlpha | undefined;
    /**
     * Retrieve the {@link TreeContextAlpha | context} for the given node.
     * @param node - The node to query
     */
    context(node: TreeNode): TreeContextAlpha;
    /**
     * Construct tree content that is compatible with the field defined by the provided `schema`.
     * @param schema - The schema for what to construct. As this is an {@link ImplicitFieldSchema}, a {@link FieldSchema}, {@link TreeNodeSchema} or {@link AllowedTypes} array can be provided.
     * @param data - The data used to construct the field content.
     * @remarks
     * When providing a {@link TreeNodeSchemaClass}, this is the same as invoking its constructor except that an unhydrated node can also be provided.
     * This function exists as a generalization that can be used in other cases as well,
     * such as when `undefined` might be allowed (for an optional field), or when the type should be inferred from the data when more than one type is possible.
     */
    create<const TSchema extends ImplicitFieldSchema | UnsafeUnknownSchema>(schema: UnsafeUnknownSchema extends TSchema ? ImplicitFieldSchema : TSchema & ImplicitFieldSchema, data: InsertableField<TSchema>): Unhydrated<TSchema extends ImplicitFieldSchema ? TreeFieldFromImplicitField<TSchema> : TreeNode | TreeLeafValue | undefined>;
    /**
     * {@inheritDoc (TreeBeta:interface).importConcise}
     */
    importConcise<const TSchema extends ImplicitFieldSchema | UnsafeUnknownSchema>(schema: UnsafeUnknownSchema extends TSchema ? ImplicitFieldSchema : TSchema & ImplicitFieldSchema, data: ConciseTree | undefined): Unhydrated<TSchema extends ImplicitFieldSchema ? TreeFieldFromImplicitField<TSchema> : TreeNode | TreeLeafValue | undefined>;
    /**
     * {@inheritDoc (TreeBeta:interface).(exportConcise:1)}
     * @privateRemarks Note: this was retained on this interface because {@link (TreeAlpha:interface).importConcise} exists.
     * It should be removed if/when that is removed from this interface.
     */
    exportConcise(node: TreeNode | TreeLeafValue, options?: TreeEncodingOptions): ConciseTree;
    /**
     * {@inheritDoc (TreeBeta:interface).(exportConcise:2)}
     * @privateRemarks Note: this was retained on this interface because {@link (TreeAlpha:interface).importConcise} exists.
     * It should be removed if/when that is removed from this interface.
     */
    exportConcise(node: TreeNode | TreeLeafValue | undefined, options?: TreeEncodingOptions): ConciseTree | undefined;
    /**
     * Construct tree content compatible with a field defined by the provided `schema`.
     * @param schema - The schema for what to construct. As this is an {@link ImplicitFieldSchema}, a {@link FieldSchema}, {@link TreeNodeSchema} or {@link AllowedTypes} array can be provided.
     * @param data - The data used to construct the field content. See {@link (TreeAlpha:interface).(exportVerbose:1)}.
     * @remarks
     * This currently does not support input containing
     * {@link ObjectSchemaOptions.allowUnknownOptionalFields| unknown optional fields} but does support
     * {@link SchemaStaticsBeta.staged | staged} allowed types.
     * Non-empty default values for fields are currently not supported (must be provided in the input).
     * The content will be validated against the schema and an error will be thrown if out of schema.
     */
    importVerbose<const TSchema extends ImplicitFieldSchema>(schema: TSchema, data: VerboseTree | undefined, options?: TreeParsingOptions): Unhydrated<TreeFieldFromImplicitField<TSchema>>;
    /**
     * Copy a snapshot of the current version of a TreeNode into a JSON compatible plain old JavaScript Object (except for {@link @fluidframework/core-interfaces#IFluidHandle|IFluidHandles}).
     * Uses the {@link VerboseTree} format, with an explicit type on every node.
     *
     * @remarks
     * There are several cases this may be preferred to {@link (TreeBeta:interface).(exportConcise:1)}:
     *
     * 1. When not using {@link ITreeConfigurationOptions.preventAmbiguity} (or when using `useStableFieldKeys`), `exportConcise` can produce ambiguous data (the type may be unclear on some nodes).
     * `exportVerbose` will always be unambiguous and thus lossless.
     *
     * 2. When the data might be interpreted without access to the exact same view schema. In such cases, the types may be unknowable if not included.
     *
     * 3. When easy access to the type is desired.
     */
    exportVerbose(node: TreeNode | TreeLeafValue, options?: TreeEncodingOptions): VerboseTree;
    /**
     * Export the content of the provided `tree` in a compressed JSON compatible format.
     * @remarks
     * If an `idCompressor` is provided, it will be used to compress identifiers and thus will be needed to decompress the data.
     *
     * Always uses "stored" keys.
     * See {@link KeyEncodingOptions.allStoredKeys} for details.
     * @privateRemarks
     * TODO: It is currently not clear how to work with the idCompressors correctly in the package API.
     * Better APIs should probably be provided as there is currently no way to associate an un-hydrated tree with an idCompressor,
     * Nor get the correct idCompressor from a subtree to use when exporting it.
     * Additionally using `createIdCompressor` to make an idCompressor is `@legacy` and thus not intended for use in this API surface.
     * It would probably make more sense if we provided a way to get an idCompressor from the context of a node,
     * which could be optional (and settable if missing) for un0hydrated nodes and required for hydrated ones.
     * Add in a stable public API for creating idCompressors, and a way to get them from a tree (without view schema), and that should address the anticipated use-cases.
     */
    exportCompressed(tree: TreeNode | TreeLeafValue, options: {
        idCompressor?: IIdCompressor;
    } & Pick<CodecWriteOptions, "minVersionForCollab">): JsonCompatible<IFluidHandle>;
    /**
     * Import data encoded by {@link (TreeAlpha:interface).exportCompressed}.
     *
     * @param schema - Schema with which the data must be compatible. This compatibility is not verified and must be ensured by the caller.
     * @param compressedData - Data compressed by {@link (TreeAlpha:interface).exportCompressed}.
     * @param options - If {@link (TreeAlpha:interface).exportCompressed} was given an `idCompressor`, it must be provided here.
     *
     * @remarks
     * If the data could have been encoded with a different schema, consider encoding the schema along side it using {@link extractPersistedSchema} and loading the data using {@link independentView}.
     *
     * @privateRemarks
     * This API could be improved:
     *
     * 1. It could validate that the schema is compatible, and return or throw an error in the invalid case (maybe add a "try" version).
     *
     * 2. A "try" version of this could return an error if the data isn't in a supported format (as determined by version and/or JasonValidator).
     *
     * 3. Requiring the caller provide a JsonValidator isn't the most friendly API. It might be practical to provide a default.
     */
    importCompressed<const TSchema extends ImplicitFieldSchema>(schema: TSchema, compressedData: JsonCompatible<IFluidHandle>, options: {
        idCompressor?: IIdCompressor;
    } & ICodecOptions): Unhydrated<TreeFieldFromImplicitField<TSchema>>;
    /**
     * APIs for creating, converting, and retrieving identifiers.
     */
    readonly identifier: TreeIdentifierUtils;
    /**
     * The key of the given node under its parent.
     * @remarks
     * If `node` is an element in a {@link (TreeArrayNode:interface)}, this returns the index of `node` in the array node (a `number`).
     * If `node` is the root node, this returns undefined.
     * Otherwise, this returns the key of the field that it is under (a `string`).
     */
    key2(node: TreeNode): string | number | undefined;
    /**
     * Gets the child of the given node with the given property key if a child exists under that key.
     *
     * @remarks {@link ObjectSchemaOptions.allowUnknownOptionalFields | Unknown optional fields} of Object nodes will not be returned by this method.
     *
     * @param node - The parent node whose child is being requested.
     * @param key - The property key under the node under which the child is being requested.
     * For Object nodes, this is the developer-facing "property key", not the "{@link SimpleObjectFieldSchema.storedKey | stored keys}".
     *
     * @returns The child node or leaf value under the given key, or `undefined` if no such child exists.
     *
     * @see {@link (TreeAlpha:interface).key2}
     * @see {@link (TreeNodeApi:interface).parent}
     */
    child(node: TreeNode, key: string | number): TreeNode | TreeLeafValue | undefined;
    /**
     * Gets the children of the provided node, paired with their property keys under the node.
     *
     * @remarks
     * No guarantees are made regarding the order of the children in the returned array.
     *
     * Optional properties of Object nodes with no value are not included in the result.
     *
     * {@link ObjectSchemaOptions.allowUnknownOptionalFields | Unknown optional fields} of Object nodes are not included in the result.
     *
     * @param node - The node whose children are being requested.
     *
     * @returns
     * An array of pairs of the form `[propertyKey, child]`.
     *
     * For Array nodes, the `propertyKey` is the index of the child in the array.
     *
     * For Object nodes, the returned `propertyKey`s are the developer-facing "property keys", not the "{@link SimpleObjectFieldSchema.storedKey | stored keys}".
     *
     * @see {@link (TreeAlpha:interface).key2}
     * @see {@link (TreeNodeApi:interface).parent}
     */
    children(node: TreeNode): Iterable<[propertyKey: string | number, child: TreeNode | TreeLeafValue]>;
    /**
     * Track observations of any TreeNode content.
     * @remarks
     * This subscribes to changes to any nodes content observed during `trackDuring`.
     *
     * Currently this does not support tracking parentage (see {@link (TreeAlpha:interface).trackObservationsOnce} for a version which does):
     * if accessing parentage during `trackDuring`, this will throw a usage error.
     *
     * This also does not track node status changes (e.g. whether a node is attached to a view or not).
     * The current behavior of checking status is unspecified: future versions may track it, error, or ignore it.
     *
     * These subscriptions remain active until `unsubscribe` is called: `onInvalidation` may be called multiple times.
     * See {@link (TreeAlpha:interface).trackObservationsOnce} for a version which automatically unsubscribes on the first invalidation.
     * @privateRemarks
     * This version, while more general than {@link (TreeAlpha:interface).trackObservationsOnce}, might be unnecessary.
     * Maybe this should be removed and only `trackObservationsOnce` kept.
     * Reevaluate this before stabilizing.
     */
    trackObservations<TResult>(onInvalidation: () => void, trackDuring: () => TResult): ObservationResults<TResult>;
    /**
     * {@link (TreeAlpha:interface).trackObservations} except automatically unsubscribes when the first invalidation occurs.
     * @remarks
     * This also supports tracking parentage, unlike {@link (TreeAlpha:interface).trackObservations}, as long as the parent is not undefined.
     *
     * @example Simple cached value invalidation
     * ```typescript
     * // Compute and cache this "foo" value, and clear the cache when the fields read in the callback to compute it change.
     * cachedFoo ??= TreeAlpha.trackObservationsOnce(
     * 	() => {
     * 		cachedFoo = undefined;
     * 	},
     * 	() => nodeA.someChild.bar + nodeB.someChild.baz,
     * ).result;
     * ```
     *
     * That is equivalent to doing the following:
     * ```typescript
     * if (cachedFoo === undefined) {
     * 	cachedFoo = nodeA.someChild.bar + nodeB.someChild.baz;
     * 	const invalidate = (): void => {
     * 		cachedFoo = undefined;
     * 		for (const u of unsubscribe) {
     * 			u();
     * 		}
     * 	};
     * 	const unsubscribe: (() => void)[] = [
     * 		TreeBeta.on(nodeA, "nodeChanged", (data) => {
     * 			if (data.changedProperties.has("someChild")) {
     * 				invalidate();
     * 			}
     * 		}),
     * 		TreeBeta.on(nodeB, "nodeChanged", (data) => {
     * 			if (data.changedProperties.has("someChild")) {
     * 				invalidate();
     * 			}
     * 		}),
     * 		TreeBeta.on(nodeA.someChild, "nodeChanged", (data) => {
     * 			if (data.changedProperties.has("bar")) {
     * 				invalidate();
     * 			}
     * 		}),
     * 		TreeBeta.on(nodeB.someChild, "nodeChanged", (data) => {
     * 			if (data.changedProperties.has("baz")) {
     * 				invalidate();
     * 			}
     * 		}),
     * 	];
     * }
     * ```
     * @example Cached derived schema property
     * ```typescript
     * const factory = new SchemaFactory("com.example");
     * class Vector extends factory.object("Vector", {
     * 	x: SchemaFactory.number,
     * 	y: SchemaFactory.number,
     * }) {
     * 	#length: number | undefined = undefined;
     * 	public length(): number {
     * 		if (this.#length === undefined) {
     * 			const result = TreeAlpha.trackObservationsOnce(
     * 				() => {
     * 					this.#length = undefined;
     * 				},
     * 				() => Math.hypot(this.x, this.y),
     * 			);
     * 			this.#length = result.result;
     * 		}
     * 		return this.#length;
     * 	}
     * }
     * const vec = new Vector({ x: 3, y: 4 });
     * assert.equal(vec.length(), 5);
     * vec.x = 0;
     * assert.equal(vec.length(), 4);
     * ```
     */
    trackObservationsOnce<TResult>(onInvalidation: () => void, trackDuring: () => TResult): ObservationResults<TResult>;
    /**
     * Ensures that the provided content will be interpreted as the given schema when inserting into the tree.
     * @returns `content`, for convenience.
     * @remarks
     * If applicable, this will tag the given content with a {@link contentSchemaSymbol | special property} that indicates its intended schema.
     * The `content` will be interpreted as the given `schema` when later inserted into the tree.
     *
     * This does not validate that the content actually conforms to the given schema (such validation will be done at insert time).
     * If the content is not compatible with the tagged schema, an error will be thrown when the content is inserted.
     *
     * This is particularly useful when the content's schema cannot be inferred from its structure alone because it is compatible with multiple schemas.
     * @example
     * ```typescript
     * const sf = new SchemaFactory("example");
     * class Dog extends sf.object("Dog", { name: sf.string() }) {}
     * class Cat extends sf.object("Cat", { name: sf.string() }) {}
     * class Root extends sf.object("Root", { pet: [Dog, Cat] }) {}
     * // ...
     * const pet = { name: "Max" };
     * view.root.pet = pet; // Error: ambiguous schema - is it a Dog or a Cat?
     * TreeAlpha.ensureSchema(Dog, pet); // Tags `pet` as a Dog.
     * view.root.pet = pet; // No error - it's a Dog.
     * ```
     */
    tagContentSchema<TSchema extends TreeNodeSchema, TContent extends InsertableField<TSchema>>(schema: TSchema, content: TContent): TContent;
}
/**
 * Results from an operation with tracked observations.
 * @remarks
 * Results from {@link (TreeAlpha:interface).trackObservations} or {@link (TreeAlpha:interface).trackObservationsOnce}.
 * @sealed @alpha
 */
export interface ObservationResults<TResult> {
    /**
     * The result of the operation which had its observations tracked.
     */
    readonly result: TResult;
    /**
     * Call to unsubscribe from further invalidations.
     */
    readonly unsubscribe: () => void;
}
/**
 * Extensions to {@link (Tree:variable)} and {@link (TreeBeta:variable)} which are not yet stable.
 * @see {@link (TreeAlpha:interface)}.
 * @alpha
 */
export declare const TreeAlpha: TreeAlpha;
//# sourceMappingURL=treeAlpha.d.ts.map