"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildConfigValidator = exports.buildValidator = exports.errorFormatter = void 0;
var ajv_1 = __importDefault(require("ajv"));
var errors_1 = require("./errors");
var environment_1 = require("./utils/environment");
var prepend = function (prefix, message) {
    return "".concat(prefix, " ").concat(message);
};
var schemaPathPropNameRegex = /properties\/(.+)\//;
var schemaPathArrayPropNameRegex = /properties\/(.+)\/items/;
var schemaPathGroupNumberRegex = /anyOf\/(\d+)\/(.+)/;
var instancePathItemIndexRegex = /(\d+)$/;
// If there are more than maxErrors errors in a group, they
// will get summarized with an error count.
var maxErrors = 3;
var formatIndividualError = function (e, formattedMessageList) {
    if (e.schemaPath.indexOf('properties') > -1) {
        // property of an object
        if (e.schemaPath.indexOf('items') > -1) {
            // property is an array
            var propNameMatch = schemaPathArrayPropNameRegex.exec(e.schemaPath);
            var propName = propNameMatch ? propNameMatch[1] : 'unknown';
            var itemIndexMatch = instancePathItemIndexRegex.exec(e.instancePath);
            var itemIndex = itemIndexMatch ? itemIndexMatch[1] : 'unknown';
            formattedMessageList.push("item at index ".concat(itemIndex, " of the '").concat(propName, "' array ").concat(e.message));
        }
        else {
            // property is not an array
            var propNameMatch = schemaPathPropNameRegex.exec(e.schemaPath);
            var propName = propNameMatch ? propNameMatch[1] : 'unknown';
            formattedMessageList.push("property '".concat(propName, "' ").concat(e.message));
        }
    }
    else if (e.schemaPath.indexOf('items') > -1) {
        // item in an array
        var itemIndexMatch = instancePathItemIndexRegex.exec(e.instancePath);
        var itemIndex = itemIndexMatch ? itemIndexMatch[1] : 'unknown';
        formattedMessageList.push("item at index ".concat(itemIndex, " of the array ").concat(e.message));
    }
    else if (e.instancePath === '') {
        // parameter is something other than an object, e.g. string
        formattedMessageList.push("argument ".concat(e.message));
    }
};
var missingPropertiesErrors = function (subject, errors, messageParts) {
    var missingPropertyNames = errors
        .filter(function (error) { return error.keyword === 'required'; })
        .map(function (error) {
        return error.params.missingProperty !== undefined
            ? error.params.missingProperty
            : 'unknown';
    });
    if (missingPropertyNames.length > 0) {
        var missingMessage = prepend(subject, "".concat(messageParts.length > 0 ? 'M' : 'm', "ust have required ").concat(missingPropertyNames.length > 1 ? 'properties' : 'property', ": ").concat(missingPropertyNames.join(', '), "."));
        messageParts.push(missingMessage);
    }
};
var neverErrors = function (subject, errors, messageParts) {
    var neverPropertyErrors = errors
        .filter(function (error) { return error.keyword === 'not'; })
        .map(function (error) {
        return error.instancePath.slice(1);
    });
    if (neverPropertyErrors.length > 0) {
        var neverMessage = prepend(subject, "must not have ".concat(neverPropertyErrors.length > 1 ? 'properties' : 'property', ": ").concat(neverPropertyErrors.join(', '), "."));
        messageParts.push(neverMessage);
    }
};
var typeErrors = function (subject, errors, messageParts) {
    var typeErrorsList = [];
    var anyOfConstPropErrors = errors.filter(function (error) {
        return error.schemaPath.indexOf('anyOf') > -1 &&
            error.keyword === 'const' &&
            error.instancePath.length > 0;
    });
    var errorCount = 0;
    // handle possible literal types first
    var propErrorGroups = {};
    if (anyOfConstPropErrors.length > 0) {
        for (var _i = 0, anyOfConstPropErrors_1 = anyOfConstPropErrors; _i < anyOfConstPropErrors_1.length; _i++) {
            var error = anyOfConstPropErrors_1[_i];
            var constValue = error.instancePath.slice(1);
            if (propErrorGroups[constValue]) {
                propErrorGroups[constValue].push(error);
            }
            else {
                propErrorGroups[constValue] = [error];
            }
        }
        var properties = Object.keys(propErrorGroups);
        properties.forEach(function (property) {
            var constValueErrors = propErrorGroups[property];
            typeErrorsList.push("property '".concat(property, "' must be equal to one of: ") +
                Object.values(constValueErrors)
                    .map(function (group) { return "'".concat(group.params.allowedValue, "'"); })
                    .join(', '));
        });
    }
    // typebox also emits type errors for each value of a literal so we want to exclude these
    var anyOfKeys = Object.keys(propErrorGroups);
    for (var i = 0; i < errors.length; i++) {
        var e = errors[i];
        if (e.keyword === 'type' && !anyOfKeys.includes(e.instancePath.slice(1))) {
            errorCount += 1;
            if (errorCount <= maxErrors) {
                formatIndividualError(e, typeErrorsList);
            }
        }
    }
    if (errorCount > maxErrors) {
        typeErrorsList.push("and ".concat(errorCount - maxErrors, " other errors"));
    }
    if (typeErrorsList.length > 0) {
        var prefix = messageParts.length > 0
            ? 'There were also type errors:'
            : "".concat(subject, " had type errors:");
        var typeErrorMessage = prepend(prefix, typeErrorsList.join(', ')) + '.';
        messageParts.push(typeErrorMessage);
    }
};
var validationErrors = function (subject, errors, messageParts) {
    var validationErrors = [];
    var errorCount = 0;
    // List of error keywords from https://ajv.js.org/api.html#validation-errors
    for (var _i = 0, errors_2 = errors; _i < errors_2.length; _i++) {
        var e = errors_2[_i];
        if (e.keyword === 'minLength' && e.params.limit === 1) {
            e.message = 'must not be blank';
        }
        switch (e.keyword) {
            case 'minimum':
            case 'maximum':
            case 'exclusiveMinimum':
            case 'exclusiveMaximum':
            case 'minLength':
            case 'maxLength':
            case 'maxProperties':
            case 'minProperties':
            case 'minItems':
            case 'maxItems':
            case 'additionalItems':
            case 'additionalProperties':
                errorCount += 1;
                if (errorCount > maxErrors) {
                    continue;
                }
                else {
                    formatIndividualError(e, validationErrors);
                }
                break;
            default:
            // noop, other non-validation error handled elsewhere
        }
    }
    if (errorCount > maxErrors) {
        validationErrors.push("and ".concat(errorCount - maxErrors, " other errors"));
    }
    if (validationErrors.length > 0) {
        var prefix = messageParts.length > 0
            ? 'There were also validation errors:'
            : "".concat(subject, " had validation errors:");
        var validationErrorMessage = prepend(prefix, validationErrors.join(', ')) + '.';
        messageParts.push(validationErrorMessage);
    }
};
var errorFormatter = function (subject, errors) {
    var anyOfArgumentErrors = errors.filter(function (error) {
        return error.schemaPath.indexOf('anyOf') > -1 &&
            error.keyword !== 'anyOf' &&
            error.keyword !== 'const' &&
            error.keyword !== 'type';
    });
    if (anyOfArgumentErrors.length > 0) {
        var groups = {};
        for (var _i = 0, anyOfArgumentErrors_1 = anyOfArgumentErrors; _i < anyOfArgumentErrors_1.length; _i++) {
            var error = anyOfArgumentErrors_1[_i];
            var schemaPathMatch = schemaPathGroupNumberRegex.exec(error.schemaPath);
            var groupNumber = schemaPathMatch ? schemaPathMatch[1] : 'unknown';
            // Remove the anyOf portion of the schema path to avoid infinite loop
            // when building message for each error group
            error.schemaPath = schemaPathMatch ? schemaPathMatch[2] : 'unknown';
            if (groups[groupNumber]) {
                groups[groupNumber].push(error);
            }
            else {
                groups[groupNumber] = [error];
            }
        }
        // concat errors for each error group
        return ("".concat(subject, " accepts multiple types. Either ") +
            Object.entries(groups)
                .map(function (_a) {
                var key = _a[0], group = _a[1];
                return "".concat(parseInt(key) + 1, ")") +
                    (0, exports.errorFormatter)('', group);
            })
                .join(' '));
    }
    var messageParts = [];
    neverErrors(subject, errors, messageParts);
    missingPropertiesErrors(subject, errors, messageParts);
    typeErrors(subject, errors, messageParts);
    validationErrors(subject, errors, messageParts);
    return messageParts.join(' ');
};
exports.errorFormatter = errorFormatter;
var buildValidator = function (errorMessagePrefix, schema) {
    if ((0, environment_1.isEdge)()) {
        // Ajv schema compilation does not work in the Edge Runtime.
        return function (data) { }; // eslint-disable-line
    }
    if (typeof process !== 'undefined' &&
        process &&
        process.env &&
        process.env.PINECONE_DISABLE_RUNTIME_VALIDATIONS) {
        // Runtime method validations are most useful when learning to use the client
        // in an interactive REPL or when developing an application that does not use
        // Typescript to provide the benefits of static type-checking. However, if your
        // application is using Typescript and/or you have gained confidence of correct
        // usage through testing, you may want to disable these runtime validations
        // to improve performance.
        //
        // The PINECONE_DISABLE_RUNTIME_VALIDATIONS env var provides a way to disable
        // all runtime validation. If it is set, all validator functions will immediately
        // return without performing any validation.
        return function (data) { }; // eslint-disable-line
    }
    var ajv = new ajv_1.default({ allErrors: true });
    var validate = ajv.compile(schema);
    return function (data) {
        var valid = validate(data);
        if (!valid) {
            var errors = validate.errors || [];
            var msg = (0, exports.errorFormatter)(errorMessagePrefix, errors);
            throw new errors_1.PineconeArgumentError(msg);
        }
        return data;
    };
};
exports.buildValidator = buildValidator;
var buildConfigValidator = function (schema, methodName) {
    var prefix = "The argument to ".concat(methodName);
    return (0, exports.buildValidator)(prefix, schema);
};
exports.buildConfigValidator = buildConfigValidator;
//# sourceMappingURL=validator.js.map