/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { normalizeAllowedTypes, isTreeNode, createSchemaUpgrade, } from "../core/index.js";
// These imports prevent a large number of type references in the API reports from showing up as *_2.
/* eslint-disable unused-imports/no-unused-imports, @typescript-eslint/no-unused-vars, import-x/no-duplicates */
import { FieldKind, getDefaultProvider, createFieldSchema, } from "../fieldSchema.js";
import { arraySchema, mapSchema, objectSchema, recordSchema, } from "../node-kinds/index.js";
import { unhydratedFlexTreeFromInsertableNode } from "../unhydratedFlexTreeFromInsertable.js";
import { defaultSchemaFactoryObjectOptions, scoped, } from "./schemaFactory.js";
import { SchemaFactoryBeta } from "./schemaFactoryBeta.js";
import { schemaStatics } from "./schemaStatics.js";
import { TreeBeta } from "./treeBeta.js";
const withDefault = (fieldSchema, defaultValue) => {
    const typedFieldSchema = fieldSchema;
    // create the default provider function, it is called eagerly during node construction
    const defaultProvider = getDefaultProvider(() => {
        // Resolve the value: if it's a function, call it; otherwise use it directly
        let insertableValue = typeof defaultValue === "function" ? defaultValue() : defaultValue;
        // If the value is an already-constructed TreeNode (e.g., from a generator function that
        // returns the same instance repeatedly), clone it to ensure each use gets a fresh instance.
        // This prevents multi-parenting errors.
        if (isTreeNode(insertableValue)) {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            insertableValue = TreeBeta.clone(insertableValue);
        }
        // For optional fields with an undefined default, return an empty array (no value).
        if (insertableValue === undefined) {
            return [];
        }
        // Convert the insertable value to an unhydrated flex tree.
        // For insertable data, this creates a fresh tree structure.
        const allowedTypeSet = normalizeAllowedTypes(typedFieldSchema.allowedTypes).evaluateSet();
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        return [unhydratedFlexTreeFromInsertableNode(insertableValue, allowedTypeSet)];
    });
    // create a new field schema with the default provider
    const propsWithDefault = {
        ...typedFieldSchema.props,
        defaultProvider,
    };
    return createFieldSchema(typedFieldSchema.kind, typedFieldSchema.allowedTypes, propsWithDefault);
};
const stagedOptional = (t, props) => {
    return createFieldSchema(FieldKind.Optional, t, {
        defaultProvider: getDefaultProvider(() => []),
        ...props,
        stagedOptionalUpgrade: createSchemaUpgrade(),
    });
};
const schemaStaticsAlpha = {
    withDefault,
    withDefaultRecursive: withDefault,
    stagedOptional,
    stagedOptionalRecursive: stagedOptional,
};
/**
 * {@link SchemaFactory} with additional alpha APIs.
 *
 * @alpha
 * @privateRemarks
 * When building schema, when `options` is not provided, `TCustomMetadata` is inferred as `unknown`.
 * If desired, this could be made to infer `undefined` instead by adding overloads for everything,
 * but currently it is not worth the maintenance overhead as there is no use case which this is known to be helpful for.
 */
export class SchemaFactoryAlpha extends SchemaFactoryBeta {
    constructor() {
        super(...arguments);
        /**
         * {@inheritDoc SchemaStatics.leaves}
         */
        this.leaves = schemaStatics.leaves;
        /**
         * {@inheritDoc SchemaStatics.optional}
         */
        this.optional = schemaStatics.optional;
        /**
         * {@inheritDoc SchemaStatics.required}
         */
        this.required = schemaStatics.required;
        /**
         * {@inheritDoc SchemaStatics.optionalRecursive}
         */
        this.optionalRecursive = schemaStatics.optionalRecursive;
        /**
         * {@inheritDoc SchemaStatics.requiredRecursive}
         */
        this.requiredRecursive = schemaStatics.requiredRecursive;
        /**
         * {@inheritdoc SchemaStaticsAlpha.withDefault}
         */
        this.withDefault = schemaStaticsAlpha.withDefault;
        /**
         * {@inheritdoc SchemaStaticsAlpha.withDefault}
         */
        this.withDefaultRecursive = schemaStaticsAlpha.withDefaultRecursive;
        /**
         * {@inheritdoc SchemaStaticsAlpha.stagedOptional}
         */
        this.stagedOptional = schemaStaticsAlpha.stagedOptional;
        /**
         * {@inheritdoc SchemaStaticsAlpha.stagedOptionalRecursive}
         */
        this.stagedOptionalRecursive = schemaStaticsAlpha.stagedOptionalRecursive;
    }
    /**
     * Define a {@link TreeNodeSchemaClass} 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 options - Additional options for the schema.
     */
    objectAlpha(name, fields, options) {
        return objectSchema(scoped(this, name), fields, true, {
            ...defaultSchemaFactoryObjectOptions,
            ...options,
        });
    }
    /**
     * {@inheritdoc SchemaFactory.objectRecursive}
     */
    objectRecursive(name, t, options) {
        return this.objectAlpha(name, t, options);
    }
    /**
     * Alpha version of {@link SchemaFactory.objectRecursive} that supports field defaults via {@link SchemaStaticsAlpha.withDefaultRecursive}.
     * Use with {@link ValidateRecursiveSchema} for improved type safety.
     * @remarks
     * Use this instead of {@link SchemaFactory.objectRecursive} when fields use {@link SchemaStaticsAlpha.withDefaultRecursive}.
     * See {@link ValidateRecursiveSchema} for additional information about using recursive schema.
     */
    objectRecursiveAlpha(name, t, options) {
        return this.objectAlpha(name, t, options);
    }
    /**
     * Define a {@link TreeNodeSchema} for a {@link TreeMapNodeAlpha}.
     *
     * @param name - Unique identifier for this schema within this factory's scope.
     * @param allowedTypes - The types that may appear as values in the map.
     * @param options - Additional options for the schema.
     *
     * @example
     * ```typescript
     * class NamedMap extends factory.map("name", factory.number, {
     * 	metadata: { description: "A map of numbers" }
     * }) {}
     * ```
     */
    mapAlpha(name, allowedTypes, options) {
        return mapSchema(scoped(this, name), allowedTypes, true, true, options);
    }
    /**
     * {@inheritDoc SchemaFactory.objectRecursive}
     */
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
    mapRecursive(name, allowedTypes, options) {
        return this.mapAlpha(name, allowedTypes, options);
    }
    /**
     * Define a {@link TreeNodeSchemaClass} for a {@link (TreeArrayNode:interface)}.
     *
     * @param name - Unique identifier for this schema within this factory's scope.
     * @param allowedTypes - The types that may appear in the array.
     * @param options - Additional options for the schema.
     *
     * @example
     * ```typescript
     * class NamedArray extends factory.arrayAlpha("name", factory.number) {}
     * ```
     */
    arrayAlpha(name, allowedTypes, options) {
        return arraySchema(scoped(this, name), allowedTypes, true, true, options ?? {});
    }
    /**
     * {@link SchemaFactory.arrayRecursive} but with support for some alpha features.
     */
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
    arrayRecursive(name, allowedTypes, options) {
        return this.arrayAlpha(name, allowedTypes, options);
    }
    /**
     * Define (and add to this library) a {@link TreeNodeSchemaClass} for a {@link (TreeRecordNode:interface)}.
     *
     * @param name - Unique identifier for this schema within this factory's scope.
     * @param allowedTypes - The types that may appear in the record.
     * @param options - Additional options for the schema.
     *
     * @example
     * ```typescript
     * class NamedRecord extends factory.recordAlpha("name", factory.number) {}
     * ```
     */
    recordAlpha(name, allowedTypes, options) {
        return recordSchema({
            identifier: scoped(this, name),
            info: allowedTypes,
            customizable: true,
            implicitlyConstructable: true,
            nodeOptions: options,
        });
    }
    /**
     * {@link SchemaFactoryBeta.(record:2)} except tweaked to work better for recursive types.
     * Use with {@link ValidateRecursiveSchema} for improved type safety.
     * @remarks
     * This version of `SchemaFactory.record` uses the same workarounds as {@link SchemaFactory.objectRecursive}.
     * See {@link ValidateRecursiveSchema} for additional information about using recursive schema.
     */
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
    recordRecursive(name, allowedTypes, options) {
        const RecordSchema = recordSchema({
            identifier: scoped(this, name),
            info: allowedTypes,
            customizable: true,
            // Setting this to true seems to work ok currently, but not for other node kinds.
            // Supporting this could be fragile and might break other future changes, so it's being kept as false for now.
            implicitlyConstructable: false,
            nodeOptions: options,
        });
        return RecordSchema;
    }
    /**
     * {@inheritDoc SchemaFactoryBeta.scopedFactory}
     */
    scopedFactoryAlpha(name) {
        return new SchemaFactoryAlpha(scoped(this, name));
    }
}
/**
 * {@inheritDoc SchemaStatics.leaves}
 */
SchemaFactoryAlpha.leaves = schemaStatics.leaves;
/**
 * {@inheritDoc SchemaStatics.optional}
 */
SchemaFactoryAlpha.optional = schemaStatics.optional;
/**
 * {@inheritDoc SchemaStatics.required}
 */
SchemaFactoryAlpha.required = schemaStatics.required;
/**
 * {@inheritDoc SchemaStatics.optionalRecursive}
 */
SchemaFactoryAlpha.optionalRecursive = schemaStatics.optionalRecursive;
/**
 * {@inheritDoc SchemaStatics.requiredRecursive}
 */
SchemaFactoryAlpha.requiredRecursive = schemaStatics.requiredRecursive;
/**
 * Like {@link SchemaFactory.identifier} but static and a factory function that can be provided {@link FieldProps}.
 */
SchemaFactoryAlpha.identifier = schemaStatics.identifier;
/**
 * {@inheritdoc SchemaStaticsAlpha.withDefault}
 */
SchemaFactoryAlpha.withDefault = schemaStaticsAlpha.withDefault;
/**
 * {@inheritdoc SchemaStaticsAlpha.withDefault}
 */
SchemaFactoryAlpha.withDefaultRecursive = schemaStaticsAlpha.withDefaultRecursive;
/**
 * {@inheritdoc SchemaStaticsAlpha.stagedOptional}
 */
SchemaFactoryAlpha.stagedOptional = schemaStaticsAlpha.stagedOptional;
/**
 * {@inheritdoc SchemaStaticsAlpha.stagedOptionalRecursive}
 */
SchemaFactoryAlpha.stagedOptionalRecursive = schemaStaticsAlpha.stagedOptionalRecursive;
//# sourceMappingURL=schemaFactoryAlpha.js.map