"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
const validateJs = require("validate.js");
const { cloneDeep } = require("lodash/fp");
const { InternalTables } = require("../../../db/utils");
const userController = require("../user");
const { FieldTypes } = require("../../../constants");
const { makeExternalQuery } = require("../../../integrations/base/utils");
const { getAppDB } = require("@devlego/backend-core/context");
validateJs.extend(validateJs.validators.datetime, {
    parse: function (value) {
        return new Date(value).getTime();
    },
    // Input is a unix timestamp
    format: function (value) {
        return new Date(value).toISOString();
    },
});
exports.getDatasourceAndQuery = (json) => __awaiter(void 0, void 0, void 0, function* () {
    const datasourceId = json.endpoint.datasourceId;
    const db = getAppDB();
    const datasource = yield db.get(datasourceId);
    return makeExternalQuery(datasource, json);
});
exports.findRow = (ctx, tableId, rowId) => __awaiter(void 0, void 0, void 0, function* () {
    const db = getAppDB();
    let row;
    // TODO remove special user case in future
    if (tableId === InternalTables.USER_METADATA) {
        ctx.params = {
            id: rowId,
        };
        yield userController.findMetadata(ctx);
        row = ctx.body;
    }
    else {
        row = yield db.get(rowId);
    }
    if (row.tableId !== tableId) {
        throw "Supplied tableId does not match the rows tableId";
    }
    return row;
});
exports.validate = ({ tableId, row, table }) => __awaiter(void 0, void 0, void 0, function* () {
    if (!table) {
        const db = getAppDB();
        table = yield db.get(tableId);
    }
    const errors = {};
    for (let fieldName of Object.keys(table.schema)) {
        const constraints = cloneDeep(table.schema[fieldName].constraints);
        const type = table.schema[fieldName].type;
        // formulas shouldn't validated, data will be deleted anyway
        if (type === FieldTypes.FORMULA) {
            continue;
        }
        // special case for options, need to always allow unselected (null)
        if (type === FieldTypes.OPTIONS && constraints.inclusion) {
            constraints.inclusion.push(null);
        }
        let res;
        // Validate.js doesn't seem to handle array
        if (type === FieldTypes.ARRAY && row[fieldName]) {
            if (row[fieldName].length) {
                if (!Array.isArray(row[fieldName])) {
                    row[fieldName] = row[fieldName].split(",");
                }
                row[fieldName].map(val => {
                    if (!constraints.inclusion.includes(val) &&
                        constraints.inclusion.length !== 0) {
                        errors[fieldName] = "Field not in list";
                    }
                });
            }
            else if (constraints.presence && row[fieldName].length === 0) {
                // non required MultiSelect creates an empty array, which should not throw errors
                errors[fieldName] = [`${fieldName} is required`];
            }
        }
        else if (type === FieldTypes.JSON && typeof row[fieldName] === "string") {
            // this should only happen if there is an error
            try {
                JSON.parse(row[fieldName]);
            }
            catch (err) {
                errors[fieldName] = [`Contains invalid JSON`];
            }
        }
        else {
            res = validateJs.single(row[fieldName], constraints);
        }
        if (res)
            errors[fieldName] = res;
    }
    return { valid: Object.keys(errors).length === 0, errors };
});
