"use strict";
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;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractSchema = void 0;
const lodash_1 = __importDefault(require("lodash"));
const json_schema_traverse_1 = __importDefault(require("json-schema-traverse"));
const jsonPaths = __importStar(require("./jsonPaths"));
const jsonPtr = __importStar(require("json-ptr"));
const json_schema_resolve_ref_1 = require("./json-schema-resolve-ref");
function extractSchemaPriv(subtreeRef, refResolver, options, context) {
    const subtreeObject = refResolver(subtreeRef);
    if (!subtreeObject) {
        throw new Error(`Could not find ref ${subtreeRef}`);
    }
    const result = lodash_1.default.cloneDeep(subtreeObject);
    const ctx = context || {
        result: result,
        replaced: {},
        replacements: [],
        schemaCount: 0,
        rootSubtreeRef: subtreeRef,
    };
    (0, json_schema_traverse_1.default)(result, (schema) => {
        if (schema.$ref && typeof schema.$ref === 'string') {
            if (ctx.replaced[schema.$ref]) {
                schema.$ref = ctx.replaced[schema.$ref];
            }
            else if (jsonPaths.jsonPointerStartsWith(schema.$ref, ctx.rootSubtreeRef + '/')) {
                ctx.replaced[schema.$ref] = jsonPaths.jsonPointerStripPrefix(schema.$ref, ctx.rootSubtreeRef);
                schema.$ref = ctx.replaced[schema.$ref];
            }
            else if (!refResolver(schema.$ref)) {
                // Don't know how to resolve this ref
                if (!options.skipUnknownRefs) {
                    throw new Error(`Can't find ref ${schema.$ref}`);
                }
            }
            else {
                ctx.result.definitions = ctx.result.definitions || {};
                // Find a name to store this under in 'definitions'.
                //
                // Because we try to pick a "sensible" name for the new definition,
                // when we recurse into `extractSchemaPriv` below, if there's a child
                // schema with the same name as the one we just picked, we could
                // end up accidentally giving two different schemas the same name
                // and clobbering one with the other.  To avoid this, we record
                // all the `newRefSuffix`es we pick in `ctx.replacements`, and
                // then we can make sure this doesn't happen.
                const origRef = schema.$ref;
                const jsonPath = jsonPtr.JsonPointer.decode(schema.$ref);
                let newRefSuffix = jsonPath.length > 0 ? `${jsonPath[jsonPath.length - 1]}` : undefined;
                while (!newRefSuffix ||
                    ctx.result.definitions[newRefSuffix] ||
                    ctx.replacements.includes(newRefSuffix)) {
                    newRefSuffix = `schema${ctx.schemaCount++}`;
                }
                ctx.replacements.push(newRefSuffix);
                // Do the replacement.
                schema.$ref = ctx.replaced[schema.$ref] = `#/definitions/${newRefSuffix}`;
                ctx.result.definitions[newRefSuffix] = extractSchemaPriv(origRef, refResolver, options, ctx);
            }
        }
    });
    return result;
}
/**
 * Extracts a subtree from a JSON document, fixing any "$ref" JSON refs so they
 * now
 *
 * @param document - The document to extract a subtree from.
 * @param subtree - A JSON ref to the subtree to extract, or a child node of `document`.
 * @param [options.resolveRef] - A function which, given a JSON reference, resolves the node
 *   it refers to.
 * @param [options.skipUnknownRefs] - If true, skip any unknown refs instead of
 *   throwing an error.
 * @returns the extracted document.  The returned document is a copy, and shares
 *   no children with the original document.
 */
function extractSchema(document, subtreeRef, options = {}) {
    const refResolver = options.resolveRef || json_schema_resolve_ref_1.resolveRef.bind(null, document);
    return extractSchemaPriv(subtreeRef, refResolver, options, undefined);
}
exports.extractSchema = extractSchema;
//# sourceMappingURL=jsonSchema.js.map