/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { type FieldKey, type TreeFieldStoredSchema } from "../../../core/index.js";
import { type FlexTreeField } from "../../../feature-libraries/index.js";
import type { RestrictiveStringRecord, FlattenKeys } from "../../../util/index.js";
import type { ObjectSchemaOptionsAlpha } from "../../api/index.js";
import { NodeKind, type WithType, type TreeNode, type ImplicitAllowedTypes, type TreeNodeSchemaCorePrivate } from "../../core/index.js";
import { type ImplicitFieldSchema, type TreeFieldFromImplicitField, type InsertableTreeFieldFromImplicitField, type FieldSchema, FieldSchemaAlpha, FieldKind, type FieldProps, type DefaultProvider } from "../../fieldSchema.js";
import type { SimpleObjectFieldSchema } from "../../simpleSchema.js";
import { type InsertableContent } from "../../unhydratedFlexTreeFromInsertable.js";
import type { ObjectNodeSchema, ObjectNodeSchemaInternalData } from "./objectNodeTypes.js";
/**
 * Generates the properties for an ObjectNode from its field schema object.
 * @remarks
 * Due to {@link https://github.com/microsoft/TypeScript/issues/43826}, we can't enable implicit construction of {@link TreeNode|TreeNodes} for setters.
 * Therefore code assigning to these fields must explicitly construct nodes using the schema's constructor or create method,
 * or using some other method like {@link (TreeAlpha:interface).create}.
 * @system @public
 */
export type ObjectFromSchemaRecord<T extends RestrictiveStringRecord<ImplicitFieldSchema>> = RestrictiveStringRecord<ImplicitFieldSchema> extends T ? {} : {
    -readonly [Property in keyof T]: Property extends string ? TreeFieldFromImplicitField<T[Property]> : unknown;
};
/**
 * A {@link TreeNode} which models a JavaScript object.
 * @remarks
 * Object nodes consist of a type which specifies which {@link TreeNodeSchema} they use (see {@link TreeNodeApi.schema} and {@link SchemaFactory.object}),
 * and a collections of fields, each with a distinct `key` and its own {@link FieldSchema} defining what can be placed under that key.
 *
 * All fields on an object node are exposed as own properties with string keys.
 * Non-empty fields are enumerable and empty optional fields are non-enumerable own properties with the value `undefined`.
 * No other own `own` or `enumerable` properties are included on object nodes unless the user of the node manually adds custom session only state.
 * This allows a majority of general purpose JavaScript object processing operations (like `for...in`, `Reflect.ownKeys()` and `Object.entries()`) to enumerate all the children.
 *
 * Field Assignment and Deletion
 * Assigning to a field updates the tree value as long as it is still valid based on the schema.
 * - For required fields, assigning `undefined` is invalid, and will throw.
 * - For optional fields, assigning `undefined` removes the field's contents, and marks it as empty (non-enumerable and value set to undefined).
 *
 * Example:
 * ```ts
 * const Foo = schemaFactory.object("Foo", {bar: schemaFactory.optional(schemaFactory.number)});
 * const node = new Foo({bar: 1})
 *
 * // This clears the field, is non-enumerable, and value is undefined.
 * delete node.bar;
 *
 * // This is equivalent to the delete example.
 * node.bar = undefined
 * ```
 *
 * The API for fields is defined by {@link ObjectFromSchemaRecord}.
 * @public
 */
export type TreeObjectNode<T extends RestrictiveStringRecord<ImplicitFieldSchema>, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecord<T> & WithType<TypeName, NodeKind.Object, T>;
/**
 * Type utility for determining if an implicit field schema is known to have a default value.
 *
 * @remarks Yields `false` when unknown.
 *
 * @privateRemarks
 * This checks for:
 * 1. Optional fields (which have an implicit undefined default)
 * 2. Identifier fields (which have auto-generated defaults)
 *
 * Note that this cannot tell if required fields have defaults. Use `FieldHasDefaultAlpha` for alpha schemas if you need to check for required field defaults.
 *
 * @system @public
 */
export type FieldHasDefault<T extends ImplicitFieldSchema> = [T] extends [
    FieldSchema<FieldKind.Optional | FieldKind.Identifier>
] ? true : false;
/**
 * Type utility for determining if an implicit field schema is known to have a default value.
 * Supports alpha field schemas with explicit default providers.
 *
 * @remarks Yields `false` when unknown.
 *
 * @privateRemarks
 * This checks for:
 * 1. Fields with explicit default providers (computed from the props type)
 * 2. Optional fields (which have an implicit undefined default)
 * 3. Identifier fields (which have auto-generated defaults)
 *
 * @system @alpha
 */
export type FieldHasDefaultAlpha<T extends ImplicitFieldSchema> = [
    T
] extends [FieldSchemaAlpha<infer Kind, infer _Types, infer _Meta, infer TProps>] ? Kind extends FieldKind.Optional | FieldKind.Identifier ? true : TProps extends {
    defaultProvider: DefaultProvider;
} ? true : false : FieldHasDefault<T>;
/**
 * Helper used to produce types for:
 *
 * 1. Insertable content which can be used to construct an object node.
 * In this case, only own properties are considered.
 * This reduces the risk of incorrectly interpreting data at the cost of occasionally requiring users to convert data into a compatible format.
 *
 * 2. Insertable content which is an unhydrated object node.
 *
 * 3. Union of 1 and 2.
 *
 * @see {@link Input}
 *
 * @privateRemarks
 * TODO: consider separating these cases into different types.
 *
 * Empty objects don't get "no excess property" checks in literals.
 * To prevent extraneous properties in literals for the fields of an empty object from compiling, the empty case is special cased to produce `Record<string, never>`.
 * More details at {@link https://mercury.com/blog/creating-an-emptyobject-type-in-typescript}.
 *
 * Additionally when T is exactly `RestrictiveStringRecord<ImplicitFieldSchema>` produce just `never` so that it is assignable to the insertable for any given object type.
 *
 * Separating `{}` from `RestrictiveStringRecord<ImplicitFieldSchema>` is a bit messy since both extend each-other despite them being very different types.
 * A third dummy type `{ arbitraryKey: "arbitraryValue" }` is used to resolve this.
 *
 * @system @public
 */
export type InsertableObjectFromSchemaRecord<T extends RestrictiveStringRecord<ImplicitFieldSchema>> = RestrictiveStringRecord<ImplicitFieldSchema> extends T ? {
    arbitraryKey: "arbitraryValue";
} extends T ? Record<string, never> : never : FlattenKeys<{
    readonly [Property in keyof T]?: InsertableTreeFieldFromImplicitField<T[Property & string]>;
} & {
    readonly [Property in keyof T as FieldHasDefault<T[Property & string]> extends false ? Property : never]: InsertableTreeFieldFromImplicitField<T[Property & string]>;
}>;
/**
 * Alpha version of InsertableObjectFromSchemaRecord that supports field defaults.
 *
 * @system @alpha
 */
export type InsertableObjectFromSchemaRecordAlpha<T extends RestrictiveStringRecord<ImplicitFieldSchema>> = RestrictiveStringRecord<ImplicitFieldSchema> extends T ? {
    arbitraryKey: "arbitraryValue";
} extends T ? Record<string, never> : never : FlattenKeys<{
    readonly [Property in keyof T]?: InsertableTreeFieldFromImplicitField<T[Property & string]>;
} & {
    readonly [Property in keyof T as FieldHasDefaultAlpha<T[Property & string]> extends false ? Property : never]: InsertableTreeFieldFromImplicitField<T[Property & string]>;
}>;
/**
 * Maps from simple field keys ("property" keys) to information about the field.
 *
 * @remarks
 * A missing entry for a given property key indicates that no such field exists.
 * Keys with symbols are currently never used, but allowed to make lookups on non-field things
 * (returning undefined) easier.
 */
export type SimpleKeyMap = ReadonlyMap<string | symbol, {
    storedKey: FieldKey;
    schema: FieldSchema;
}>;
export declare function setField(field: FlexTreeField, simpleFieldSchema: FieldSchema, value: InsertableContent | undefined, destinationSchema: TreeFieldStoredSchema): void;
/**
 * {@link FieldSchemaAlpha} including {@link SimpleObjectFieldSchema}.
 */
export declare class ObjectFieldSchema<Kind extends FieldKind, Types extends ImplicitAllowedTypes, TCustomMetadata = unknown, TProps extends FieldProps<TCustomMetadata> & {
    readonly key: string;
} = FieldProps<TCustomMetadata> & {
    readonly key: string;
}> extends FieldSchemaAlpha<Kind, Types, TCustomMetadata, TProps> implements SimpleObjectFieldSchema {
    readonly storedKey: string;
    constructor(kind: Kind, allowedTypes: Types, props: TProps);
}
/**
 * Define a {@link TreeNodeSchema} for a {@link TreeObjectNode}.
 *
 * @param name - Unique identifier for this schema within this factory's scope.
 * @param fields - Schema for fields of the object node's schema. Defines what children can be placed under each key.
 * @param persistedMetadata - Optional persisted metadata for the object node schema.
 */
export declare function objectSchema<TName extends string, const T extends RestrictiveStringRecord<ImplicitFieldSchema>, const ImplicitlyConstructable extends boolean, const TCustomMetadata = unknown>(identifier: TName, info: T, implicitlyConstructable: ImplicitlyConstructable, nodeOptions: ObjectSchemaOptionsAlpha<TCustomMetadata>): ObjectNodeSchema<TName, T, ImplicitlyConstructable, TCustomMetadata> & ObjectNodeSchemaInternalData & TreeNodeSchemaCorePrivate;
//# sourceMappingURL=objectNode.d.ts.map