/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { assert } from "@fluidframework/core-utils";
import { compareSets, fail } from "../../util/index.js";
import { storedEmptyFieldSchema, LeafNodeStoredSchema, MapNodeStoredSchema, ObjectNodeStoredSchema, } from "../../core/index.js";
import { withEditor } from "./fieldKindWithEditor.js";
import { isNeverTree } from "./isNeverTree.js";
/**
 * @returns true iff `superset` is a superset of `original`.
 *
 * This does not require a strict (aka proper) superset: equivalent schema will return true.
 *
 * `undefined` TreeNodeStoredSchema means the schema is not present (and thus treated as a NeverTree).
 */
export function allowsTreeSuperset(policy, originalData, original, superset) {
    if (isNeverTree(policy, originalData, original)) {
        return true;
    }
    if (isNeverTree(policy, originalData, superset)) {
        return false;
    }
    assert(original !== undefined, 0x716 /* only never trees have undefined schema */);
    assert(superset !== undefined, 0x717 /* only never trees have undefined schema */);
    if (original instanceof LeafNodeStoredSchema) {
        if (superset instanceof LeafNodeStoredSchema) {
            return allowsValueSuperset(original.leafValue, superset.leafValue);
        }
        return false;
    }
    assert(original instanceof MapNodeStoredSchema || original instanceof ObjectNodeStoredSchema, 0x893 /* unsupported node kind */);
    assert(superset instanceof MapNodeStoredSchema || superset instanceof ObjectNodeStoredSchema, 0x894 /* unsupported node kind */);
    if (original instanceof MapNodeStoredSchema) {
        if (superset instanceof MapNodeStoredSchema) {
            return allowsFieldSuperset(policy, originalData, normalizeField(original.mapFields), normalizeField(superset.mapFields));
        }
        return false;
    }
    assert(original instanceof ObjectNodeStoredSchema, 0x895 /* unsupported node kind */);
    if (superset instanceof MapNodeStoredSchema) {
        for (const [_key, field] of original.objectNodeFields) {
            if (!allowsFieldSuperset(policy, originalData, normalizeField(field), normalizeField(superset.mapFields))) {
                return false;
            }
        }
        return true;
    }
    assert(superset instanceof ObjectNodeStoredSchema, 0x896 /* unsupported node kind */);
    return compareSets({
        a: original.objectNodeFields,
        b: superset.objectNodeFields,
        aExtra: (originalField) => allowsFieldSuperset(policy, originalData, original.objectNodeFields.get(originalField) ?? fail("missing expected field"), normalizeField(undefined)),
        bExtra: (supersetField) => allowsFieldSuperset(policy, originalData, normalizeField(undefined), superset.objectNodeFields.get(supersetField) ?? fail("missing expected field")),
        same: (sameField) => allowsFieldSuperset(policy, originalData, original.objectNodeFields.get(sameField) ?? fail("missing expected field"), superset.objectNodeFields.get(sameField) ?? fail("missing expected field")),
    });
}
/**
 * @returns true iff `superset` is a superset of `original`.
 *
 * This does not require a strict (aka proper) superset: equivalent schema will return true.
 */
export function allowsValueSuperset(original, superset) {
    return original === superset;
}
/**
 * @returns true iff `superset` is a superset of `original`.
 *
 * This does not require a strict (aka proper) superset: equivalent schema will return true.
 */
export function allowsFieldSuperset(policy, originalData, original, superset) {
    return withEditor(policy.fieldKinds.get(original.kind.identifier) ?? fail("missing kind")).allowsFieldSuperset(policy, originalData, original.types, superset);
}
/**
 * @returns true iff `superset` is a superset of `original`.
 *
 * This does not require a strict (aka proper) superset: equivalent schema will return true.
 */
export function allowsTreeSchemaIdentifierSuperset(original, superset) {
    if (superset === undefined) {
        return true;
    }
    if (original === undefined) {
        return false;
    }
    for (const originalType of original) {
        if (!superset.has(originalType)) {
            return false;
        }
    }
    return true;
}
/**
 * @returns true iff `superset` is a superset of `original`.
 *
 * This does not require a strict (aka proper) superset: equivalent schema will return true.
 *
 * A version of this that assumes a specific root field could be slightly more permissive in some simple cases,
 * however if any extra fields and fields with unconstrained types are reachable,
 * it would have to compare everything anyway.
 */
export function allowsRepoSuperset(policy, original, superset) {
    {
        // TODO: I think its ok to use the field from superset here, but I should confirm it is, and document why.
        if (!allowsFieldSuperset(policy, original, original.rootFieldSchema, superset.rootFieldSchema)) {
            return false;
        }
    }
    for (const [key, schema] of original.nodeSchema) {
        // TODO: I think its ok to use the tree from superset here, but I should confirm it is, and document why.
        if (!allowsTreeSuperset(policy, original, schema, superset.nodeSchema.get(key))) {
            return false;
        }
    }
    return true;
}
export function normalizeField(schema) {
    return schema ?? storedEmptyFieldSchema;
}
//# sourceMappingURL=comparison.js.map