"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.getStoredSchema = exports.convertFieldKind = exports.convertField = exports.simpleToStoredSchema = exports.toStoredSchema = void 0;
const internal_1 = require("@fluidframework/core-utils/internal");
const internal_2 = require("@fluidframework/telemetry-utils/internal");
const index_js_1 = require("../core/index.js");
const index_js_2 = require("../feature-libraries/index.js");
const index_js_3 = require("../util/index.js");
const index_js_4 = require("./core/index.js");
const leafNodeSchema_js_1 = require("./leafNodeSchema.js");
const schemaTypes_js_1 = require("./schemaTypes.js");
const walkFieldSchema_js_1 = require("./walkFieldSchema.js");
const viewToStoredCache = new WeakMap();
/**
 * Converts a {@link ImplicitFieldSchema} into a {@link TreeStoredSchema}.
 * @throws
 * Throws a `UsageError` if multiple schemas are encountered with the same identifier.
 */
function toStoredSchema(root) {
    return (0, index_js_3.getOrCreate)(viewToStoredCache, root, () => {
        const normalized = (0, schemaTypes_js_1.normalizeFieldSchema)(root);
        const nodeSchema = new Map();
        (0, walkFieldSchema_js_1.walkFieldSchema)(normalized, {
            node(schema) {
                if (nodeSchema.has((0, index_js_3.brand)(schema.identifier))) {
                    // Use JSON.stringify to quote and escape identifier string.
                    throw new internal_2.UsageError(`Multiple schema encountered with the identifier ${JSON.stringify(schema.identifier)}. Remove or rename them to avoid the collision.`);
                }
                nodeSchema.set((0, index_js_3.brand)(schema.identifier), getStoredSchema(schema));
            },
        });
        const result = {
            nodeSchema,
            rootFieldSchema: convertField(normalized),
        };
        return result;
    });
}
exports.toStoredSchema = toStoredSchema;
/**
 * Converts a {@link SimpleTreeSchema} into a {@link TreeStoredSchema}.
 */
function simpleToStoredSchema(root) {
    const nodeSchema = new Map();
    for (const [identifier, schema] of root.definitions) {
        nodeSchema.set((0, index_js_3.brand)(identifier), getStoredSchema(schema));
    }
    const result = {
        nodeSchema,
        rootFieldSchema: convertField(root.root),
    };
    return result;
}
exports.simpleToStoredSchema = simpleToStoredSchema;
/**
 * Normalizes an {@link ImplicitFieldSchema} into a {@link TreeFieldSchema}.
 */
function convertField(schema) {
    const kind = exports.convertFieldKind.get(schema.kind)?.identifier ?? (0, internal_1.fail)(0xae3 /* Invalid field kind */);
    const types = schema.allowedTypesIdentifiers;
    return { kind, types, persistedMetadata: schema.persistedMetadata };
}
exports.convertField = convertField;
/**
 * A map that converts {@link FieldKind} to {@link FlexFieldKind}.
 */
exports.convertFieldKind = new Map([
    [schemaTypes_js_1.FieldKind.Optional, index_js_2.FieldKinds.optional],
    [schemaTypes_js_1.FieldKind.Required, index_js_2.FieldKinds.required],
    [schemaTypes_js_1.FieldKind.Identifier, index_js_2.FieldKinds.identifier],
]);
/**
 * Converts a {@link TreeNodeSchema} into a {@link TreeNodeStoredSchema}.
 * @privateRemarks
 * TODO: Persist node metadata once schema FormatV2 is supported.
 */
function getStoredSchema(schema) {
    const kind = schema.kind;
    switch (kind) {
        case index_js_4.NodeKind.Leaf: {
            (0, internal_1.assert)(schema instanceof leafNodeSchema_js_1.LeafNodeSchema, 0xa4a /* invalid kind */);
            return new index_js_1.LeafNodeStoredSchema(schema.leafKind);
        }
        case index_js_4.NodeKind.Map: {
            const types = schema.allowedTypesIdentifiers;
            return new index_js_1.MapNodeStoredSchema({
                kind: index_js_2.FieldKinds.optional.identifier,
                types,
                persistedMetadata: schema.persistedMetadata,
            }, 
            // TODO: Find a way to avoid injecting persistedMetadata twice in these constructor calls.
            schema.persistedMetadata);
        }
        case index_js_4.NodeKind.Array: {
            const types = schema.allowedTypesIdentifiers;
            const field = {
                kind: index_js_2.FieldKinds.sequence.identifier,
                types,
                persistedMetadata: schema.persistedMetadata,
            };
            const fields = new Map([[index_js_1.EmptyKey, field]]);
            return new index_js_1.ObjectNodeStoredSchema(fields, schema.persistedMetadata);
        }
        case index_js_4.NodeKind.Object: {
            const fields = new Map();
            for (const fieldSchema of schema.fields.values()) {
                fields.set((0, index_js_3.brand)(fieldSchema.storedKey), convertField(fieldSchema));
            }
            return new index_js_1.ObjectNodeStoredSchema(fields, schema.persistedMetadata);
        }
        default:
            (0, internal_1.unreachableCase)(kind);
    }
}
exports.getStoredSchema = getStoredSchema;
//# sourceMappingURL=toStoredSchema.js.map