"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateSchemaFromSimpleSchema = void 0;
const internal_1 = require("@fluidframework/core-utils/internal");
const index_js_1 = require("../core/index.js");
const fieldSchema_js_1 = require("../fieldSchema.js");
const schemaFactoryAlpha_js_1 = require("./schemaFactoryAlpha.js");
const factory = new schemaFactoryAlpha_js_1.SchemaFactoryAlpha(undefined);
/**
 * Create {@link FieldSchema} from a SimpleTreeSchema.
 * @remarks
 * Only use this API if hand written schema (produced using {@link SchemaFactory} cannot be provided.
 *
 * Using generated schema with schema aware APIs (designed to work with strongly typed schema) like {@link TreeViewConfiguration}
 * will produce a poor TypeScript typing experience which is subject to change.
 *
 * Editing through a view produced using this schema can easily violate invariants other users of the document might expect and must be done with great care.
 *
 * This API bakes in some arbitrary policy choices for how to handle data that is not included in the SimpleTreeSchema API, for example the value of `allowUnknownOptionalFields`.
 * If any particular choice is required for such cases, this API should not be used.
 *
 * @privateRemarks
 * TODO: Add the ability for consumers to inject custom policy.
 * For example, allow nodes with table schema identifiers to dispatch to `TableSchema` factory APIs.
 *
 * @alpha
 */
function generateSchemaFromSimpleSchema(simple) {
    const context = new Map([...simple.definitions].map(([id, schema]) => [
        id,
        // This relies on the caching in evaluateLazySchema so that it only runs once.
        () => generateNode(id, schema, context),
    ]));
    const root = generateFieldSchema(simple.root, context, undefined);
    const definitions = new Map();
    for (const [id, lazy] of context) {
        definitions.set(id, lazy());
    }
    return {
        root,
        definitions,
    };
}
exports.generateSchemaFromSimpleSchema = generateSchemaFromSimpleSchema;
function generateFieldSchema(simple, context, storedKey) {
    const allowed = generateAllowedTypes(simple.simpleAllowedTypes, context);
    const props = {
        metadata: simple.metadata,
        key: storedKey,
    };
    // Using createFieldSchema could work, but would require setting up the default providers.
    switch (simple.kind) {
        case fieldSchema_js_1.FieldKind.Identifier: {
            return schemaFactoryAlpha_js_1.SchemaFactoryAlpha.identifier(props);
        }
        case fieldSchema_js_1.FieldKind.Optional: {
            return schemaFactoryAlpha_js_1.SchemaFactoryAlpha.optional(allowed, props);
        }
        case fieldSchema_js_1.FieldKind.Required: {
            return schemaFactoryAlpha_js_1.SchemaFactoryAlpha.required(allowed, props);
        }
        default: {
            return (0, internal_1.unreachableCase)(simple.kind);
        }
    }
}
function generateAllowedTypes(allowed, context) {
    const types = Array.from(allowed.entries(), ([id, attributes]) => {
        const schema = context.get(id) ?? (0, internal_1.fail)(0xb5a /* Missing schema */);
        return attributes.isStaged instanceof index_js_1.SchemaUpgrade ? factory.staged(schema) : schema;
    });
    // TODO: AB#53315: `AllowedTypesFullFromMixed` does not correctly handle the `(AnnotatedAllowedType | LazyItem<TreeNodeSchema>)[]` case.
    // We have to cast here in order to produce an allowed types list that can be used in tree node factory methods (e.g., `SchemaFactoryAlpha.objectAlpha`).
    return schemaFactoryAlpha_js_1.SchemaFactoryAlpha.types(types);
}
function generateNode(id, schema, context) {
    switch (schema.kind) {
        case index_js_1.NodeKind.Object: {
            const fields = {};
            for (const [key, field] of schema.fields) {
                fields[key] = generateFieldSchema(field, context, field.storedKey);
            }
            // Here allowUnknownOptionalFields is implicitly defaulting in the case where the input schema does not explicitly specify the value.
            // This is a subjective policy choice: users of this code are expected to handle what ever choice this code makes for cases like this.
            return factory.objectAlpha(id, fields, {
                metadata: schema.metadata,
                allowUnknownOptionalFields: schema.allowUnknownOptionalFields ?? false,
            });
        }
        case index_js_1.NodeKind.Array: {
            return factory.arrayAlpha(id, generateAllowedTypes(schema.simpleAllowedTypes, context), {
                metadata: schema.metadata,
            });
        }
        case index_js_1.NodeKind.Map: {
            return factory.mapAlpha(id, generateAllowedTypes(schema.simpleAllowedTypes, context), {
                metadata: schema.metadata,
            });
        }
        case index_js_1.NodeKind.Record: {
            return factory.recordAlpha(id, generateAllowedTypes(schema.simpleAllowedTypes, context), { metadata: schema.metadata });
        }
        case index_js_1.NodeKind.Leaf: {
            return (schemaFactoryAlpha_js_1.SchemaFactoryAlpha.leaves.find((leaf) => leaf.identifier === id) ??
                (0, internal_1.fail)(0xb5b /* Missing schema */));
        }
        default: {
            return (0, internal_1.unreachableCase)(schema);
        }
    }
}
//# sourceMappingURL=schemaFromSimple.js.map