"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const constants_1 = require("../constants");
const Ajv = require("ajv");
const utils_1 = require("../utils/utils");
const jif_schema_1 = require("./jif-schema");
const types_1 = require("@deepstream/types");
const ajv = new Ajv();
const validateJIF = ajv.compile(jif_schema_1.jifSchema);
// jif -> message lookup table
function getJifToMsg() {
    const JIF_TO_MSG = {};
    JIF_TO_MSG.event = {};
    JIF_TO_MSG.event.emit = (msg) => ({
        done: true,
        message: {
            topic: constants_1.TOPIC.EVENT,
            action: constants_1.EVENT_ACTION.EMIT,
            name: msg.eventName,
            parsedData: msg.data,
        },
    });
    JIF_TO_MSG.rpc = {};
    JIF_TO_MSG.rpc.make = (msg) => ({
        done: false,
        message: {
            topic: constants_1.TOPIC.RPC,
            action: constants_1.RPC_ACTION.REQUEST,
            name: msg.rpcName,
            correlationId: utils_1.getUid(),
            parsedData: msg.data,
        },
    });
    JIF_TO_MSG.record = {};
    JIF_TO_MSG.record.read = (msg) => ({
        done: false,
        message: {
            topic: constants_1.TOPIC.RECORD,
            action: constants_1.RECORD_ACTION.READ,
            name: msg.recordName,
        },
    });
    JIF_TO_MSG.record.write = (msg) => (msg.path ? JIF_TO_MSG.record.patch(msg) : JIF_TO_MSG.record.update(msg));
    JIF_TO_MSG.record.patch = (msg) => ({
        done: false,
        message: {
            topic: constants_1.TOPIC.RECORD,
            action: constants_1.RECORD_ACTION.CREATEANDPATCH,
            name: msg.recordName,
            version: msg.version || -1,
            path: msg.path,
            parsedData: msg.data,
            isWriteAck: true,
            correlationId: 0
        },
    });
    JIF_TO_MSG.record.update = (msg) => ({
        done: false,
        message: {
            topic: constants_1.TOPIC.RECORD,
            action: constants_1.RECORD_ACTION.CREATEANDUPDATE,
            name: msg.recordName,
            version: msg.version || -1,
            parsedData: msg.data,
            isWriteAck: true,
            correlationId: 0
        },
    });
    JIF_TO_MSG.record.head = (msg) => ({
        done: false,
        message: {
            topic: constants_1.TOPIC.RECORD,
            action: constants_1.RECORD_ACTION.HEAD,
            name: msg.recordName,
        },
    });
    JIF_TO_MSG.record.delete = (msg) => ({
        done: false,
        message: {
            topic: constants_1.TOPIC.RECORD,
            action: constants_1.RECORD_ACTION.DELETE,
            name: msg.recordName,
        },
    }),
        JIF_TO_MSG.record.notify = (msg) => ({
            done: false,
            message: {
                topic: constants_1.TOPIC.RECORD,
                action: constants_1.RECORD_ACTION.NOTIFY,
                names: msg.recordNames
            },
        });
    JIF_TO_MSG.list = {};
    JIF_TO_MSG.list.read = (msg) => ({
        done: false,
        message: {
            topic: constants_1.TOPIC.RECORD,
            action: constants_1.RECORD_ACTION.READ,
            name: msg.listName,
        },
    });
    JIF_TO_MSG.list.write = (msg) => ({
        done: false,
        message: {
            topic: constants_1.TOPIC.RECORD,
            action: constants_1.RECORD_ACTION.CREATEANDUPDATE,
            name: msg.listName,
            version: msg.version || -1,
            parsedData: msg.data,
            isWriteAck: true,
            correlationId: 0
        },
    });
    JIF_TO_MSG.list.delete = (msg) => ({
        done: false,
        message: {
            topic: constants_1.TOPIC.RECORD,
            action: constants_1.RECORD_ACTION.DELETE,
            name: msg.listName,
        },
    });
    JIF_TO_MSG.presence = {};
    JIF_TO_MSG.presence.query = (msg) => (msg.parsedData ? JIF_TO_MSG.presence.queryUsers(msg) : JIF_TO_MSG.presence.queryAll(msg));
    JIF_TO_MSG.presence.queryAll = () => ({
        done: false,
        message: {
            topic: constants_1.TOPIC.PRESENCE,
            action: constants_1.PRESENCE_ACTION.QUERY_ALL,
        },
    });
    JIF_TO_MSG.presence.queryUsers = (msg) => ({
        done: false,
        message: {
            topic: constants_1.TOPIC.PRESENCE,
            action: constants_1.PRESENCE_ACTION.QUERY,
            data: msg.parsedData,
        },
    });
    return utils_1.deepFreeze(JIF_TO_MSG);
}
// message type enumeration
const TYPE = { ACK: 'A', NORMAL: 'N' };
function getMsgToJif() {
    // message -> jif lookup table
    const MSG_TO_JIF = {};
    MSG_TO_JIF[constants_1.TOPIC.RPC] = {};
    MSG_TO_JIF[constants_1.TOPIC.RPC][constants_1.RPC_ACTION.RESPONSE] = {};
    MSG_TO_JIF[constants_1.TOPIC.RPC][constants_1.RPC_ACTION.RESPONSE][TYPE.NORMAL] = (message) => ({
        done: true,
        message: {
            data: message.parsedData,
            success: true,
        },
    });
    MSG_TO_JIF[constants_1.TOPIC.RPC][constants_1.RPC_ACTION.REQUEST_ERROR] = {};
    MSG_TO_JIF[constants_1.TOPIC.RPC][constants_1.RPC_ACTION.REQUEST_ERROR][TYPE.NORMAL] = (message) => ({
        done: true,
        message: {
            errorTopic: 'rpc',
            error: message.parsedData,
            success: false,
        },
    });
    MSG_TO_JIF[constants_1.TOPIC.RPC][constants_1.RPC_ACTION.ACCEPT] = {};
    MSG_TO_JIF[constants_1.TOPIC.RPC][constants_1.RPC_ACTION.ACCEPT][TYPE.NORMAL] = () => ({ done: false });
    MSG_TO_JIF[constants_1.TOPIC.RPC][constants_1.RPC_ACTION.REQUEST] = {};
    MSG_TO_JIF[constants_1.TOPIC.RPC][constants_1.RPC_ACTION.REQUEST][TYPE.ACK] = () => ({ done: false });
    MSG_TO_JIF[constants_1.TOPIC.RECORD] = {};
    MSG_TO_JIF[constants_1.TOPIC.RECORD][constants_1.RECORD_ACTION.READ_RESPONSE] = {};
    MSG_TO_JIF[constants_1.TOPIC.RECORD][constants_1.RECORD_ACTION.READ_RESPONSE][TYPE.NORMAL] = (message) => ({
        done: true,
        message: {
            version: message.version,
            data: message.parsedData,
            success: true,
        },
    });
    MSG_TO_JIF[constants_1.TOPIC.RECORD][constants_1.RECORD_ACTION.WRITE_ACKNOWLEDGEMENT] = {};
    MSG_TO_JIF[constants_1.TOPIC.RECORD][constants_1.RECORD_ACTION.WRITE_ACKNOWLEDGEMENT][TYPE.NORMAL] = (message) => ({
        done: true,
        message: {
            success: true,
        },
    });
    MSG_TO_JIF[constants_1.TOPIC.RECORD][constants_1.RECORD_ACTION.DELETE] = {};
    MSG_TO_JIF[constants_1.TOPIC.RECORD][constants_1.RECORD_ACTION.DELETE][TYPE.NORMAL] = () => ({
        done: true,
        message: {
            success: true,
        },
    });
    MSG_TO_JIF[constants_1.TOPIC.RECORD][constants_1.RECORD_ACTION.DELETE_SUCCESS] = {};
    MSG_TO_JIF[constants_1.TOPIC.RECORD][constants_1.RECORD_ACTION.DELETE_SUCCESS][TYPE.NORMAL] = () => ({
        done: true,
        message: {
            success: true,
        },
    });
    MSG_TO_JIF[constants_1.TOPIC.RECORD][constants_1.RECORD_ACTION.NOTIFY] = {};
    MSG_TO_JIF[constants_1.TOPIC.RECORD][constants_1.RECORD_ACTION.NOTIFY][TYPE.ACK] = (message) => ({
        done: true,
        message: {
            success: true,
        },
    });
    MSG_TO_JIF[constants_1.TOPIC.RECORD][constants_1.RECORD_ACTION.HEAD_RESPONSE] = {};
    MSG_TO_JIF[constants_1.TOPIC.RECORD][constants_1.RECORD_ACTION.HEAD_RESPONSE][TYPE.NORMAL] = (message) => ({
        done: true,
        message: {
            version: message.version,
            success: true,
        },
    });
    MSG_TO_JIF[constants_1.TOPIC.PRESENCE] = {};
    MSG_TO_JIF[constants_1.TOPIC.PRESENCE][constants_1.PRESENCE_ACTION.QUERY_ALL_RESPONSE] = {};
    MSG_TO_JIF[constants_1.TOPIC.PRESENCE][constants_1.PRESENCE_ACTION.QUERY_ALL_RESPONSE][TYPE.NORMAL] = (message) => ({
        done: true,
        message: {
            users: message.names,
            success: true,
        },
    });
    MSG_TO_JIF[constants_1.TOPIC.PRESENCE][constants_1.PRESENCE_ACTION.QUERY_RESPONSE] = {};
    MSG_TO_JIF[constants_1.TOPIC.PRESENCE][constants_1.PRESENCE_ACTION.QUERY_RESPONSE][TYPE.NORMAL] = (message) => ({
        done: true,
        message: {
            users: message.parsedData,
            success: true,
        },
    });
    return utils_1.deepFreeze(MSG_TO_JIF);
}
class JIFHandler {
    constructor(services) {
        this.services = services;
        this.JIF_TO_MSG = getJifToMsg();
        this.MSG_TO_JIF = getMsgToJif();
        this.topicToKey = utils_1.reverseMap(constants_1.TOPIC);
    }
    /*
     * Validate and convert a JIF message to a deepstream message
     */
    fromJIF(jifMessage) {
        if (!validateJIF(jifMessage)) {
            let error = validateJIF.errors[0];
            switch (error.keyword) {
                // case 'additionalProperties':
                //   error = `property '${error.params.additionalProperty}'
                //   not permitted for topic '${jifMessage.topic}'`
                //   break
                case 'required':
                    error = `property '${error.params.missingProperty}' is required for topic '${jifMessage.topic}'`;
                    break;
                case 'type':
                case 'minLength':
                    error = `property '${error.dataPath}' ${error.message}`;
                    break;
                // case 'const':
                //   error = `value for property '${error.dataPath}' not valid for topic '${jifMessage.topic}'`
                //   break
                default:
                    error = null;
            }
            return {
                success: false,
                error,
                done: true,
            };
        }
        const result = this.JIF_TO_MSG[jifMessage.topic][jifMessage.action](jifMessage);
        result.success = true;
        return result;
    }
    /*
     * Convert a deepstream response/ack message to a JIF message response
     * @param {Object}  message     deepstream message
     *
     * @returns {Object} {
     *    {Object}  message   jif message
     *    {Boolean} done      false iff message should await another result/acknowledgement
     * }
     */
    toJIF(message) {
        let type;
        if (message.isAck) {
            type = TYPE.ACK;
        }
        else {
            type = TYPE.NORMAL;
        }
        if (message.isError) {
            return this.errorToJIF(message, message.action);
        }
        return this.MSG_TO_JIF[message.topic][message.action][type](message);
    }
    /*
     * Convert a deepstream error message to a JIF message response
     */
    errorToJIF(message, event) {
        // convert topic enum to human-readable key
        const topicKey = this.topicToKey[message.topic];
        const result = {
            errorTopic: topicKey && topicKey.toLowerCase(),
            errorEvent: event,
            success: false,
        };
        if (event === constants_1.ACTIONS[message.topic].MESSAGE_DENIED) {
            result.action = message.originalAction;
            result.error = `Message denied. Action "${constants_1.ACTIONS[message.topic][message.originalAction]}" is not permitted.`;
        }
        else if (message.topic === constants_1.TOPIC.RECORD && event === constants_1.RECORD_ACTION.VERSION_EXISTS) {
            result.error = `Record update failed. Version ${message.version} exists for record "${message.name}".`;
            result.currentVersion = message.version;
            result.currentData = message.parsedData;
        }
        else if (message.topic === constants_1.TOPIC.RECORD && event === constants_1.RECORD_ACTION.INVALID_VERSION) {
            result.error = `Record update failed. Version ${message.version} is not valid for record "${message.name}".`;
            result.currentVersion = message.version;
            result.currentData = message.parsedData;
        }
        else if (message.topic === constants_1.TOPIC.RECORD && event === constants_1.RECORD_ACTION.RECORD_NOT_FOUND) {
            result.error = `Record read failed. Record "${message.name}" could not be found.`;
            result.errorEvent = message.action;
        }
        else if (message.topic === constants_1.TOPIC.RPC && event === constants_1.RPC_ACTION.NO_RPC_PROVIDER) {
            result.error = `No provider was available to handle the RPC "${message.name}".`;
            // message.correlationId = data[1]
        }
        else if (message.topic === constants_1.TOPIC.RPC && message.action === constants_1.RPC_ACTION.RESPONSE_TIMEOUT) {
            result.error = 'The RPC response timeout was exceeded by the provider.';
        }
        else {
            this.services.logger.warn(types_1.EVENT.INFO, `Unhandled request error occurred: ${constants_1.TOPIC[message.topic]} ${event} ${JSON.stringify(message)}`, { message });
            result.error = `An error occurred: ${constants_1.RPC_ACTION[event]}.`;
            result.errorParams = message.name;
        }
        return {
            message: result,
            done: true,
        };
    }
}
exports.default = JIFHandler;
//# sourceMappingURL=jif-handler.js.map