"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.decodeSchemaCompatibilitySnapshot = exports.encodeSchemaCompatibilitySnapshot = void 0;
const internal_1 = require("@fluidframework/core-utils/internal");
const internal_2 = require("@fluidframework/telemetry-utils/internal");
const index_js_1 = require("../../codec/index.js");
const index_js_2 = require("../../util/index.js");
const index_js_3 = require("../core/index.js");
const Format = __importStar(require("../simpleSchemaFormatV1.js"));
/**
 * Encodes the compatibility impacting subset of simple schema (view or stored) into a serializable format.
 *
 * @remarks The JSON-compatible schema returned from this method is only intended for use in snapshots/comparisons of schemas.
 * It is not possible to reconstruct a full schema (including metadata and persistedMetadata) from the encoded format.
 * @param treeSchema - The tree schema to convert.
 * @returns A serializable representation of the schema.
 *
 * @privateRemarks
 * Encodes to {@link Format.SimpleTreeSchemaFormat}.
 *
 * TODO: a simple high level API for snapshot based schema compatibility checking should replace the need to export this.
 *
 * @alpha
 */
function encodeSchemaCompatibilitySnapshot(simpleSchema) {
    // Convert types to serializable forms
    const encodedDefinitions = {};
    for (const [identifier, schema] of simpleSchema.definitions) {
        const encodedDefinition = encodeNodeSchema(schema);
        encodedDefinitions[identifier] = encodedDefinition;
    }
    const encodedSchema = {
        version: Format.SimpleSchemaFormatVersion.v1,
        root: encodeField(simpleSchema.root),
        definitions: encodedDefinitions,
    };
    return encodedSchema;
}
exports.encodeSchemaCompatibilitySnapshot = encodeSchemaCompatibilitySnapshot;
/**
 * Decodes a JSON-compatible schema into a simple schema.
 * @param encodedSchema - The encoded schema to decode.
 * @param validator - The format validator to use to validate the encoded schema.
 * @returns A decoded simple schema.
 * @throws Will throw a usage error if the encoded schema is not in the expected format.
 *
 * @privateRemarks
 * If a validator is not provided, this implicitly performs an unsafe type conversion:
 * this is something our user facing APIs generally avoid doing, and should be reconsidered before stabilizing.
 *
 * TODO: a simple high level API for snapshot based schema compatibility checking should replace the need to export this.
 *
 * @alpha
 */
function decodeSchemaCompatibilitySnapshot(encodedSchema, validator) {
    const effectiveValidator = validator ?? index_js_1.FormatValidatorNoOp;
    const compiledValidator = (0, index_js_1.extractJsonValidator)(effectiveValidator).compile(Format.SimpleTreeSchemaFormat);
    if (!compiledValidator.check(encodedSchema)) {
        throw new internal_2.UsageError("The provided simple schema is not valid according to the schema format.");
    }
    return {
        root: decodeSimpleFieldSchema(encodedSchema.root),
        definitions: new Map((0, internal_1.transformMapValues)((0, index_js_2.objectToMap)(encodedSchema.definitions), (value, key) => {
            return decodeNodeSchema(value);
        })),
    };
}
exports.decodeSchemaCompatibilitySnapshot = decodeSchemaCompatibilitySnapshot;
/**
 * Encodes a node schema to a serializable object.
 * @param schema - The node schema to convert.
 * @returns A serializable representation of the node schema.
 */
function encodeNodeSchema(schema) {
    const kind = schema.kind;
    switch (kind) {
        case index_js_3.NodeKind.Leaf: {
            return { leaf: encodeLeafNode(schema) };
        }
        case index_js_3.NodeKind.Array: {
            return { array: encodeContainerNode(schema) };
        }
        case index_js_3.NodeKind.Map: {
            return { map: encodeContainerNode(schema) };
        }
        case index_js_3.NodeKind.Record: {
            return { record: encodeContainerNode(schema) };
        }
        case index_js_3.NodeKind.Object: {
            return { object: encodeObjectNode(schema) };
        }
        default: {
            (0, internal_1.unreachableCase)(kind);
        }
    }
}
/**
 * Encodes a leaf node schema to a serializable object.
 * @param schema - The leaf node schema to convert.
 * @returns A serializable representation of the leaf node schema.
 */
function encodeLeafNode(schema) {
    return {
        kind: schema.kind,
        leafKind: schema.leafKind,
    };
}
/**
 * Encodes a container node schema (a simple schema that is a Map, Array, or Record) to a serializable object.
 * @param schema - The container node schema to convert.
 * @returns A serializable representation of the container node schema. Includes the `kind` for disambiguation between different
 * container kinds.
 */
function encodeContainerNode(schema) {
    return {
        kind: schema.kind,
        simpleAllowedTypes: encodeSimpleAllowedTypes(schema.simpleAllowedTypes),
    };
}
/**
 * Encodes a simple allowed types map to a serializable object. Needed because JSON serialization does not support Maps.
 * @param simpleAllowedTypes - The simple allowed types map to convert.
 * @returns A serializable representation of the simple allowed types.
 */
function encodeSimpleAllowedTypes(simpleAllowedTypes) {
    const encodedAllowedTypes = {};
    for (const [identifier, attributes] of simpleAllowedTypes) {
        const isStaged = attributes.isStaged instanceof index_js_3.SchemaUpgrade ? true : attributes.isStaged;
        encodedAllowedTypes[identifier] = { isStaged };
    }
    return encodedAllowedTypes;
}
/**
 * Encodes an object node schema to a serializable object.
 * @param schema - The object node schema to convert.
 * @returns A serializable representation of the object node schema.
 */
function encodeObjectNode(schema) {
    const encodedFields = {};
    for (const [fieldKey, fieldSchema] of schema.fields) {
        encodedFields[fieldKey] = encodeObjectField(fieldSchema);
    }
    return {
        kind: schema.kind,
        fields: encodedFields,
        allowUnknownOptionalFields: schema.allowUnknownOptionalFields,
    };
}
/**
 * Encodes an object field schema to a serializable object.
 * @param fieldSchema - The object field schema to convert.
 * @returns A serializable representation of the object field schema.
 */
function encodeObjectField(fieldSchema) {
    const encodedField = encodeField(fieldSchema);
    return { ...encodedField, storedKey: fieldSchema.storedKey };
}
/**
 * Encodes a field schema to a serializable object.
 * @param fieldSchema - The field schema to convert.
 * @returns A serializable representation of the field schema.
 */
function encodeField(fieldSchema) {
    return {
        kind: fieldSchema.kind,
        simpleAllowedTypes: encodeSimpleAllowedTypes(fieldSchema.simpleAllowedTypes),
    };
}
const decodeNodeSchemaDispatcher = new index_js_1.DiscriminatedUnionDispatcher({
    leaf: decodeLeafNode,
    array: decodeContainerNode,
    map: decodeContainerNode,
    record: decodeContainerNode,
    object: decodeObjectNode,
});
/**
 * Decodes a node schema from a JSON-compatible object.
 * @param encodedNodeSchema - The encoded node schema to decode.
 * @returns The decoded node schema.
 */
function decodeNodeSchema(encodedNodeSchema) {
    return decodeNodeSchemaDispatcher.dispatch(encodedNodeSchema);
}
/**
 * Decodes a container node schema (array, map, record) from a JSON-compatible object.
 * @param encodedContainerSchema - The encoded schema to decode.
 * @returns The decoded container node schema.
 */
function decodeContainerNode(encodedContainerSchema) {
    return {
        kind: encodedContainerSchema.kind,
        simpleAllowedTypes: decodeSimpleAllowedTypes(encodedContainerSchema.simpleAllowedTypes),
        // We cannot encode persistedMetadata or metadata, so we explicitly set them to empty values.
        persistedMetadata: undefined,
        metadata: {},
    };
}
/**
 * Decodes a leaf node schema from a JSON-compatible object.
 * @param encodedLeafSchema - The encoded leaf node schema.
 * @returns The decoded leaf node schema.
 */
function decodeLeafNode(encodedLeafSchema) {
    return {
        kind: index_js_3.NodeKind.Leaf,
        leafKind: encodedLeafSchema.leafKind,
        // We cannot encode persistedMetadata or metadata, so we explicitly set them to empty values.
        persistedMetadata: undefined,
        metadata: {},
    };
}
/**
 * Decodes a object node schema from a JSON-compatible object.
 * @param encodedObjectSchema - The encoded object node schema.
 * @returns The decoded object node schema.
 */
function decodeObjectNode(encodedObjectSchema) {
    return {
        kind: index_js_3.NodeKind.Object,
        fields: decodeObjectFields(encodedObjectSchema.fields),
        // It is possible for allowUnknownOptionalFields to be undefined. This happens when serializing a Simple Schema derived
        // from a stored schema.
        allowUnknownOptionalFields: encodedObjectSchema.allowUnknownOptionalFields,
        // We cannot encode persistedMetadata or metadata, so we explicitly set them to empty values when decoding.
        persistedMetadata: undefined,
        metadata: {},
    };
}
/**
 * Decodes a map of object fields from a JSON-compatible object.
 * @param encodedFields - The encoded fields.
 * @returns A map of the decoded object fields.
 */
function decodeObjectFields(encodedFields) {
    const fields = new Map();
    for (const [fieldKey, fieldSchema] of Object.entries(encodedFields)) {
        fields.set(fieldKey, decodeObjectField(fieldSchema));
    }
    return fields;
}
/**
 * Decodes a {@link SimpleObjectFieldSchema} from a JSON-compatible object.
 * @param encodedField - The encoded field schema.
 * @returns The decoded simple object field schema.
 */
function decodeObjectField(encodedField) {
    const baseField = decodeSimpleFieldSchema(encodedField);
    return {
        ...baseField,
        storedKey: encodedField.storedKey,
    };
}
/**
 * Decodes a {@link SimpleFieldSchema} from a JSON-compatible object.
 * @param encodedField - The encoded field schema.
 * @returns The decoded simple field schema.
 */
function decodeSimpleFieldSchema(encodedField) {
    return {
        kind: encodedField.kind,
        simpleAllowedTypes: decodeSimpleAllowedTypes(encodedField.simpleAllowedTypes),
        // We cannot encode persistedMetadata or metadata, so we explicitly set them to empty values when decoding.
        persistedMetadata: undefined,
        metadata: {},
    };
}
/**
 * Decodes a simple allowed types map from a JSON-compatible object.
 * @param encodedAllowedTypes - The encoded simple allowed types.
 * @returns A map of the decoded simple allowed types.
 */
function decodeSimpleAllowedTypes(encodedAllowedTypes) {
    const untypedMap = (0, index_js_2.objectToMap)(encodedAllowedTypes);
    const simpleAllowedTypes = (0, internal_1.transformMapValues)(untypedMap, (value) => {
        const isStaged = value.isStaged === true ? (0, index_js_3.createSchemaUpgrade)() : value.isStaged;
        return { isStaged };
    });
    return simpleAllowedTypes;
}
//# sourceMappingURL=simpleSchemaCodec.js.map