"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const axios_1 = require("axios");
const log_1 = require("../../log/log");
const WiseCommand_1 = require("../../protocol/WiseCommand");
const SteemOperationNumber_1 = require("../../blockchain/SteemOperationNumber");
var WiseSQLProtocol;
(function (WiseSQLProtocol) {
    let Row;
    (function (Row) {
        /**
         * This is an TS 1.6+ TypeGuard as described here: https://www.typescriptlang.org/docs/handbook/advanced-types.html
         */
        function isRow(o) {
            return typeof o === "object"
                && typeof o.id === "number"
                && typeof o.block_num === "number"
                && typeof o.transaction_num === "number"
                && typeof o.transaction_id === "string"
                && typeof o.timestamp === "string"
                && typeof o.moment === "number"
                && typeof o.voter === "string"
                && typeof o.delegator === "string"
                && typeof o.operation_type === "string"
                && (["set_rules", "send_voteorder", "confirm_vote"].indexOf(o.operation_type) !== -1)
                && typeof o.json_str === "string";
        }
        Row.isRow = isRow;
    })(Row = WiseSQLProtocol.Row || (WiseSQLProtocol.Row = {}));
    class Handler {
        static query(params, loadNextPages = true, offset = 0) {
            return __awaiter(this, void 0, void 0, function* () {
                const queryConfig = {};
                queryConfig.url =
                    params.endpointUrl
                        + (params.endpointUrl.substr(-1) !== "/" && params.path.substr(0, 1) !== "/" ? "/" : "")
                        + params.path;
                queryConfig.method = params.method;
                if (params.params)
                    queryConfig.params = params.params;
                if (params.data)
                    queryConfig.data = params.data;
                queryConfig.headers = {
                    "Range-Unit": "items",
                    "Range": offset + "-" + (params.limit - 1)
                };
                log_1.Log.log().efficient(log_1.Log.level.http, () => "HTTP_REQUEST=" + JSON.stringify(queryConfig));
                const response = yield axios_1.default(queryConfig);
                log_1.Log.log().efficient(log_1.Log.level.http, () => "HTTP_RESPONSE_HEADERS=" + JSON.stringify(response.headers));
                log_1.Log.log().efficient(log_1.Log.level.http, () => "HTTP_RESPONSE_DATA_LENGTH=" + JSON.stringify(response.data.length));
                if (!response.data)
                    throw new Error("No response from server");
                if (!Array.isArray(response.data))
                    throw new Error("Malformed response from server");
                const protocolVersion = Handler.getProtocolVersionFromResponse(response);
                const out = response.data.map(row => Handler.handleRow(protocolVersion, row));
                if (loadNextPages && out.length > 0 && out.length + offset < params.limit) {
                    const nextPageOffset = Handler.nextPageOffset(response); // this method decides if next page exists
                    if (nextPageOffset > 0) {
                        const nextPageOut = yield Handler.query(params, true, nextPageOffset);
                        nextPageOut.forEach(responseOp => out.push(responseOp));
                    }
                }
                return out;
            });
        }
        static getProtocolVersionFromResponse(response) {
            if (!response.headers["wisesql-protocol-version"])
                throw new Error("Response is missing the \"wisesql-protocol-version\" header. Check if it is a wiseSQL endpoint.");
            return response.headers["wisesql-protocol-version"].trim();
        }
        static nextPageOffset(response) {
            if (!response.headers["content-range"])
                throw new Error("Response is missing the \"content-range\" header. Check if it is a wiseSQL endpoint.");
            if (!response.headers["wisesql-max-rows-per-page"])
                throw new Error("Response is missing the \"wisesql-max-rows-per-page\" header. Check if it is a wiseSQL endpoint.");
            const contentRangeStr = response.headers["content-range"].trim();
            const contentRangeRegExp = /^([0-9]+)-?([0-9]*)\/?([0-9]*|\*)?$/giu;
            const matches = contentRangeRegExp.exec(contentRangeStr);
            if (!matches || matches.length < 3)
                throw new Error("Malformed content-range header: '" + contentRangeStr + "'");
            const offset = parseInt(matches[1], 10);
            const lastIndex = parseInt(matches[2], 10);
            if (matches.length > 3 && matches[3] !== "*") { // this is the case when the server sends the full length of db query result
                const fullLength = parseInt(matches[3], 10);
                if (!((lastIndex + 1) === fullLength)) {
                    return lastIndex + 1;
                }
            }
            else {
                const maxRowsPerPage = parseInt(response.headers["wisesql-max-rows-per-page"], 10);
                const resultLength = lastIndex - offset + 1 /* we calculate length thats why add 1 */;
                if (resultLength === maxRowsPerPage) {
                    return lastIndex + 1;
                }
            }
            return -1;
        }
        static handleRow(protocolVersion, row) {
            if (protocolVersion !== "1.0")
                throw new Error("Unsupported protocol version " + protocolVersion + ". "
                    + "Consider updating steem-wise-core lib or contact wise-team. This version supports only v1.0 protocol.");
            if (!Row.isRow(row))
                throw new Error("Row is malformed: " + JSON.stringify(row));
            const cmd = JSON.parse(row.json_str);
            if (!WiseCommand_1.WiseCommand.isWiseCommand(cmd))
                throw new Error("Malformed WiseCommand object in row.json_str");
            const out = {
                command: cmd,
                moment: new SteemOperationNumber_1.SteemOperationNumber(row.block_num, row.transaction_num, 0),
                transaction_id: row.transaction_id,
                timestamp: new Date(row.timestamp),
                voter: row.voter,
                delegator: row.delegator
            };
            return out;
        }
    }
    WiseSQLProtocol.Handler = Handler;
})(WiseSQLProtocol = exports.WiseSQLProtocol || (exports.WiseSQLProtocol = {}));
//# sourceMappingURL=WiseSQLProtocol.js.map