"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.toJsonSchema = 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("../../util/index.js");
const schemaTypes_js_1 = require("../schemaTypes.js");
const index_js_3 = require("../core/index.js");
/**
 * Generates a JSON Schema representation from a simple tree schema.
 * @internal
 */
function toJsonSchema(schema) {
    const definitions = convertDefinitions(schema.definitions);
    const allowedTypes = [];
    for (const allowedType of schema.allowedTypes) {
        allowedTypes.push(createSchemaRef(allowedType));
    }
    return (0, index_js_2.hasSingle)(allowedTypes)
        ? {
            ...allowedTypes[0],
            $defs: definitions,
        }
        : {
            $defs: definitions,
            anyOf: allowedTypes,
        };
}
exports.toJsonSchema = toJsonSchema;
function convertDefinitions(definitions) {
    const result = {};
    for (const [key, value] of definitions) {
        result[key] = convertNodeSchema(value);
    }
    return result;
}
/**
 * Private symbol under which the results of {@link convertNodeSchema} are cached on an input {@link SimpleNodeSchema}.
 */
const nodeJsonSchemaCache = new WeakMap();
/**
 * Converts an input {@link SimpleNodeSchema} to a {@link JsonNodeSchema}.
 *
 * @remarks Caches the result on the input schema for future calls.
 */
function convertNodeSchema(schema) {
    return (0, index_js_2.getOrCreate)(nodeJsonSchemaCache, schema, () => {
        switch (schema.kind) {
            case index_js_3.NodeKind.Array:
                return convertArrayNodeSchema(schema);
            case index_js_3.NodeKind.Leaf:
                return convertLeafNodeSchema(schema);
            case index_js_3.NodeKind.Map:
                return convertMapNodeSchema(schema);
            case index_js_3.NodeKind.Object:
                return convertObjectNodeSchema(schema);
            default:
                throw new TypeError(`Unknown node schema kind: ${schema.kind}`);
        }
    });
}
function convertArrayNodeSchema(schema) {
    const allowedTypes = [];
    schema.allowedTypes.forEach((type) => {
        allowedTypes.push(createSchemaRef(type));
    });
    const items = (0, index_js_2.hasSingle)(allowedTypes)
        ? allowedTypes[0]
        : { anyOf: allowedTypes };
    const output = {
        type: "array",
        _treeNodeSchemaKind: index_js_3.NodeKind.Array,
        items,
    };
    (0, index_js_2.copyProperty)(schema.metadata, "description", output);
    return output;
}
function convertLeafNodeSchema(schema) {
    let type;
    switch (schema.leafKind) {
        case index_js_1.ValueSchema.String:
            type = "string";
            break;
        case index_js_1.ValueSchema.Number:
            type = "number";
            break;
        case index_js_1.ValueSchema.Boolean:
            type = "boolean";
            break;
        case index_js_1.ValueSchema.Null:
            type = "null";
            break;
        case index_js_1.ValueSchema.FluidHandle:
            throw new internal_2.UsageError("Fluid handles are not supported via JSON Schema.");
        default:
            (0, internal_1.unreachableCase)(schema.leafKind);
    }
    return {
        type,
        _treeNodeSchemaKind: index_js_3.NodeKind.Leaf,
    };
}
function convertObjectNodeSchema(schema) {
    const properties = {};
    const required = [];
    for (const [key, fieldSchema] of Object.entries(schema.fields)) {
        const allowedTypes = [];
        for (const allowedType of fieldSchema.allowedTypes) {
            allowedTypes.push(createSchemaRef(allowedType));
        }
        const output = (0, index_js_2.hasSingle)(allowedTypes)
            ? allowedTypes[0]
            : {
                anyOf: allowedTypes,
            };
        (0, index_js_2.copyProperty)(fieldSchema.metadata, "description", output);
        properties[key] = output;
        if (fieldSchema.kind === schemaTypes_js_1.FieldKind.Required) {
            required.push(key);
        }
    }
    const transformedNode = {
        type: "object",
        _treeNodeSchemaKind: index_js_3.NodeKind.Object,
        properties,
        required,
        additionalProperties: false,
    };
    (0, index_js_2.copyProperty)(schema.metadata, "description", transformedNode);
    return transformedNode;
}
function convertMapNodeSchema(schema) {
    const allowedTypes = [];
    schema.allowedTypes.forEach((type) => {
        allowedTypes.push(createSchemaRef(type));
    });
    const output = {
        type: "object",
        _treeNodeSchemaKind: index_js_3.NodeKind.Map,
        patternProperties: {
            "^.*$": (0, index_js_2.hasSingle)(allowedTypes)
                ? allowedTypes[0]
                : {
                    anyOf: allowedTypes,
                },
        },
    };
    (0, index_js_2.copyProperty)(schema.metadata, "description", output);
    return output;
}
function createSchemaRef(schemaId) {
    return {
        "$ref": createRefPath(schemaId),
    };
}
function createRefPath(schemaId) {
    return `#/$defs/${schemaId}`;
}
//# sourceMappingURL=simpleSchemaToJsonSchema.js.map