"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPossibleTypes = exports.unhydratedFlexTreeFromInsertableNode = exports.unhydratedFlexTreeFromInsertable = void 0;
const internal_1 = require("@fluidframework/telemetry-utils/internal");
const internal_2 = require("@fluidframework/core-utils/internal");
const index_js_1 = require("../util/index.js");
const fieldSchema_js_1 = require("./fieldSchema.js");
const index_js_2 = require("./core/index.js");
const createContext_js_1 = require("./createContext.js");
/**
 * Transforms an input {@link TypedNode} tree to an {@link UnhydratedFlexTreeNode}.
 * @param data - The input tree to be converted.
 * If the data is an unsupported value (e.g. NaN), a fallback value will be used when supported,
 * otherwise an error will be thrown.
 *
 * Fallbacks:
 *
 * * `NaN` =\> `null`
 *
 * * `+/-∞` =\> `null`
 *
 * * `-0` =\> `+0`
 *
 * For fields with a default value, the field may be omitted.
 * If `context` is not provided, defaults which require a context will be left empty which can be out of schema.
 *
 * @param allowedTypes - The set of types allowed by the parent context. Used to validate the input tree.
 * @remarks
 * The resulting tree will be populated with any defaults from {@link FieldProvider}s in the schema.
 *
 * Often throws UsageErrors for invalid data, but may miss some cases.
 *
 * Output should comply with the provided view schema, but this is not explicitly validated:
 * validation against stored schema (to guard against document corruption) is done elsewhere.
 */
function unhydratedFlexTreeFromInsertable(data, allowedTypes) {
    const normalizedFieldSchema = (0, fieldSchema_js_1.normalizeFieldSchema)(allowedTypes);
    if (data === undefined) {
        // TODO: this code-path should support defaults
        if (normalizedFieldSchema.kind !== fieldSchema_js_1.FieldKind.Optional) {
            throw new internal_1.UsageError("Got undefined for non-optional field.");
        }
        return undefined;
    }
    const flexTree = unhydratedFlexTreeFromInsertableNode(data, normalizedFieldSchema.allowedTypeSet);
    return flexTree;
}
exports.unhydratedFlexTreeFromInsertable = unhydratedFlexTreeFromInsertable;
/**
 * Copy content from `data` into a UnhydratedFlexTreeNode.
 */
function unhydratedFlexTreeFromInsertableNode(data, allowedTypes) {
    if ((0, index_js_2.isTreeNode)(data)) {
        const kernel = (0, index_js_2.getKernel)(data);
        const inner = kernel.getInnerNodeIfUnhydrated();
        if (inner === undefined) {
            // The node is already hydrated, meaning that it already got inserted into the tree previously
            throw new internal_1.UsageError("A node may not be inserted into the tree more than once");
        }
        else {
            if (!allowedTypes.has(kernel.schema)) {
                throw new internal_1.UsageError("Invalid schema for this context.");
            }
            return inner;
        }
    }
    const schema = getType(data, allowedTypes);
    const handler = (0, index_js_2.getTreeNodeSchemaPrivateData)(schema).idempotentInitialize();
    const result = handler.toFlexContent(data, allowedTypes);
    return new index_js_2.UnhydratedFlexTreeNode(...result, (0, createContext_js_1.getUnhydratedContext)(schema));
}
exports.unhydratedFlexTreeFromInsertableNode = unhydratedFlexTreeFromInsertableNode;
function getType(data, allowedTypes) {
    const possibleTypes = getPossibleTypes(allowedTypes, data);
    if (possibleTypes.length === 0) {
        throw new internal_1.UsageError(`The provided data is incompatible with all of the types allowed by the schema. The set of allowed types is: ${JSON.stringify([...allowedTypes].map((schema) => schema.identifier))}.`);
    }
    if (!(0, index_js_1.hasSingle)(possibleTypes)) {
        throw new internal_1.UsageError(`The provided data is compatible with more than one type allowed by the schema.
The set of possible types is ${JSON.stringify([
            ...possibleTypes.map((schema) => schema.identifier),
        ])}.
Explicitly construct an unhydrated node of the desired type to disambiguate.
For class-based schema, this can be done by replacing an expression like "{foo: 1}" with "new MySchema({foo: 1})".`);
    }
    return possibleTypes[0];
}
/**
 * Returns all types for which the data is schema-compatible.
 */
function getPossibleTypes(allowedTypes, data) {
    (0, internal_2.assert)(data !== undefined, 0x889 /* undefined cannot be used as FactoryContent. */);
    let best = index_js_2.CompatibilityLevel.None;
    const possibleTypes = [];
    for (const schema of allowedTypes) {
        const handler = (0, index_js_2.getTreeNodeSchemaPrivateData)(schema).idempotentInitialize();
        const level = handler.shallowCompatibilityTest(data);
        if (level > best) {
            possibleTypes.length = 0;
            best = level;
        }
        if (best === level) {
            possibleTypes.push(schema);
        }
    }
    return best === index_js_2.CompatibilityLevel.None ? [] : possibleTypes;
}
exports.getPossibleTypes = getPossibleTypes;
//# sourceMappingURL=unhydratedFlexTreeFromInsertable.js.map