"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 () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __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.default = default_1;
const _ = __importStar(require("lodash"));
const validator = __importStar(require("./validationWrapper.js"));
const joi_1 = __importDefault(require("joi"));
const hdbUtils = __importStar(require("../utility/common_utils.js"));
const common_validators_ts_1 = require("./common_validators.js");
const hdbError_ts_1 = require("../utility/errors/hdbError.js");
const databases_ts_1 = require("../resources/databases.js");
const { HTTP_STATUS_CODES } = hdbError_ts_1.hdbErrors;
const searchByValueSchema = joi_1.default.object({
    database: common_validators_ts_1.hdbDatabase,
    schema: common_validators_ts_1.hdbDatabase,
    table: common_validators_ts_1.hdbTable,
    attribute: common_validators_ts_1.hdbSchemaTable,
    value: joi_1.default.any().required(),
    get_attributes: joi_1.default.array().min(1).items(joi_1.default.alternatives(common_validators_ts_1.hdbSchemaTable, joi_1.default.object())).optional(),
    desc: joi_1.default.bool(),
    limit: joi_1.default.number().integer().min(1),
    offset: joi_1.default.number().integer().min(0),
});
const searchByConditionsSchema = joi_1.default.object({
    database: common_validators_ts_1.hdbDatabase,
    schema: common_validators_ts_1.hdbDatabase,
    table: common_validators_ts_1.hdbTable,
    operator: joi_1.default.string().valid('and', 'or').default('and').lowercase(),
    offset: joi_1.default.number().integer().min(0),
    limit: joi_1.default.number().integer().min(1),
    get_attributes: joi_1.default.array().min(1).items(joi_1.default.alternatives(common_validators_ts_1.hdbSchemaTable, joi_1.default.object())).optional(),
    sort: joi_1.default.object({
        attribute: joi_1.default.alternatives(common_validators_ts_1.hdbSchemaTable, joi_1.default.array().min(1)),
        descending: joi_1.default.bool().optional(),
    }).optional(),
    conditions: joi_1.default.array()
        .min(1)
        .items(joi_1.default.alternatives(joi_1.default.object({ operator: joi_1.default.string().valid('and', 'or').default('and').lowercase(), conditions: joi_1.default.array() }), joi_1.default.object({
        attribute: joi_1.default.alternatives(common_validators_ts_1.hdbSchemaTable, joi_1.default.array().min(1)),
        comparator: joi_1.default.string()
            .valid('equals', 'contains', 'starts_with', 'ends_with', 'greater_than', 'greater_than_equal', 'less_than', 'less_than_equal', 'between', 'not_equal')
            .optional(),
        value: joi_1.default.when('comparator', {
            switch: [
                { is: 'equals', then: joi_1.default.any() },
                {
                    is: 'between',
                    then: joi_1.default.array()
                        .items(joi_1.default.alternatives([joi_1.default.string(), joi_1.default.number()]))
                        .length(2),
                },
            ],
            otherwise: joi_1.default.alternatives(joi_1.default.string(), joi_1.default.number()),
        }).required(),
    })))
        .required(),
});
function default_1(searchObject, type) {
    let validationError = null;
    switch (type) {
        case 'value':
            validationError = validator.validateBySchema(searchObject, searchByValueSchema);
            break;
        case 'hashes':
            let errors;
            addError((0, common_validators_ts_1.checkValidTable)('database', searchObject.schema));
            addError((0, common_validators_ts_1.checkValidTable)('table', searchObject.table));
            if (!searchObject.hash_values)
                addError(`'hash_values' is required`);
            else if (!Array.isArray(searchObject.hash_values))
                addError(`'hash_values' must be an array`);
            else if (!searchObject.hash_values.every((value) => typeof value === 'string' || typeof value === 'number'))
                addError(`'hash_values' must be strings or numbers`);
            if (!searchObject.get_attributes)
                addError(`'get_attributes' is required`);
            else if (!Array.isArray(searchObject.get_attributes))
                addError(`'get_attributes' must be an array`);
            else if (searchObject.get_attributes.length === 0)
                addError(`'get_attributes' must contain at least 1 item`);
            else if (!searchObject.get_attributes.every((value) => typeof value === 'string' || typeof value === 'number'))
                addError(`'get_attributes' must be strings or numbers`);
            function addError(error) {
                if (errors)
                    errors += '. ' + error;
                else
                    errors = error;
            }
            if (errors)
                validationError = new Error(errors.trim());
            break;
        case 'conditions':
            validationError = validator.validateBySchema(searchObject, searchByConditionsSchema);
            break;
        default:
            throw new Error(`Error validating search, unknown type: ${type}`);
    }
    // validate table and attribute if format validation is valid
    if (!validationError && searchObject.schema !== 'system') {
        // skip validation for system schema
        //check if schema.table does not exist throw error
        let checkSchemaTable = hdbUtils.checkGlobalSchemaTable(searchObject.schema, searchObject.table);
        if (checkSchemaTable) {
            return (0, hdbError_ts_1.handleHDBError)(new Error(), checkSchemaTable, HTTP_STATUS_CODES.NOT_FOUND);
        }
        let tableSchema = (0, databases_ts_1.getDatabases)()[searchObject.schema][searchObject.table];
        let allTableAttributes = tableSchema.attributes;
        //this clones the get_attributes array
        let checkAttributes = searchObject.get_attributes ? [...searchObject.get_attributes] : [];
        if (type === 'value') {
            checkAttributes.push(searchObject.attribute);
        }
        //if search type is conditions add conditions fields to see if the fields exist
        const addConditions = (searchObject) => {
            //this is used to validate condition attributes exist in the schema
            for (const condition of searchObject.conditions) {
                if (condition.conditions)
                    addConditions(condition);
                else
                    checkAttributes.push(condition.attribute);
            }
        };
        if (type === 'conditions') {
            addConditions(searchObject);
        }
        let unknownAttributes = _.filter(checkAttributes, (attribute) => attribute !== '*' &&
            !attribute.startsWith?.('$') && // meta attributes
            attribute.attribute !== '*' && // skip check for asterisk attribute
            !Array.isArray(attribute) &&
            !attribute.name && // nested attribute
            !_.some(allTableAttributes, (tableAttribute // attribute should match one of the attribute in global
            ) => tableAttribute === attribute ||
                tableAttribute.attribute === attribute ||
                tableAttribute.attribute === attribute.attribute));
        // if any unknown attributes present in the search request then list all indicated as unknown attribute to error message at once split in well format
        // for instance "unknown attribute a, b and c" or "unknown attribute a"
        if (unknownAttributes && unknownAttributes.length > 0) {
            // return error with proper message - replace last comma with and
            let errorMsg = unknownAttributes.join(', ');
            errorMsg = errorMsg.replace(/,([^,]*)$/, ' and$1');
            return new Error(`unknown attribute '${errorMsg}'`);
        }
    }
    return validationError;
}
//# sourceMappingURL=searchValidator.js.map