"use strict";
/* TODO auto-generated eslint ignore, please fix! */
/* eslint prefer-const: "off" */
/**
 * Delta Processor
 *
 *  Copyright (C) 2010-2019 R-T Specialty, LLC.
 *
 *  This file is part of the Liza Data Collection Framework.
 *
 *  liza is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.DeltaProcessor = void 0;
var ContextError_1 = require("../error/ContextError");
/**
 * Process deltas for a quote and publish to a queue
 */
var DeltaProcessor = /** @class */ (function () {
    /**
     * Initialize processor
     *
     * @param _dao         - Delta dao
     * @param _publisher   - Amqp Publisher
     * @param _emitter     - Event emiter instance
     * @param _applyDelta  - A function to apply deltas on a bucket
     * @param _mergeDeltas - A function to merge similar deltas
     */
    function DeltaProcessor(_dao, _publisher, _emitter, _applyDelta, _mergeDeltas) {
        this._dao = _dao;
        this._publisher = _publisher;
        this._emitter = _emitter;
        this._applyDelta = _applyDelta;
        this._mergeDeltas = _mergeDeltas;
        /** The ratedata delta type */
        this.DELTA_RATEDATA = 'ratedata';
        /** The data delta type */
        this.DELTA_DATA = 'data';
    }
    /**
     * Process unpublished deltas
     */
    DeltaProcessor.prototype.process = function () {
        var _this = this;
        return this._dao
            .getUnprocessedDocuments()
            .then(function (docs) { return _this._processNext(docs); });
    };
    /**
     * Process the next document
     *
     * @param docs - list of documents to process
     */
    DeltaProcessor.prototype._processNext = function (docs) {
        var _this = this;
        var doc = docs.shift();
        if (!doc) {
            return Promise.resolve();
        }
        return this._processDocument(doc).then(function (_) { return _this._processNext(docs); });
    };
    /**
     * Process an individual document
     *
     * @param doc - individual document to process
     */
    DeltaProcessor.prototype._processDocument = function (doc) {
        var _this = this;
        var deltas = this._getTimestampSortedDeltas(doc);
        var bucket = doc.data;
        var ratedata = doc.ratedata || {};
        var meta = {
            id: doc.id,
            program: doc.programId,
            entity_name: doc.agentName,
            entity_id: +doc.agentEntityId,
            startDate: doc.startDate,
            lastUpdate: doc.lastUpdate,
            expDate: doc.quoteExpDate,
            quoteSetId: doc.quoteSetId,
            topSavedStepId: doc.topSavedStepId,
        };
        var history = this._applyDeltas(deltas, bucket, ratedata);
        return this._processNextDelta(meta, history)
            .then(function (_) { return _this._dao.markDocumentAsProcessed(meta.id, meta.lastUpdate); })
            .then(function (_) {
            _this._emitter.emit('document-processed', {
                doc_id: meta.id,
                quote_id: meta.id,
                quote_set_id: meta.quoteSetId,
            });
        })
            .catch(function (e) {
            if (ContextError_1.hasContext(e)) {
                var combined_context_1 = {};
                var error_context_1 = e.context;
                Object.keys(error_context_1).forEach(function (key) {
                    combined_context_1[key] = error_context_1[key];
                });
                combined_context_1['doc_id'] = meta.id;
                combined_context_1['quote_id'] = meta.id;
                e = ContextError_1.context(e, combined_context_1);
            }
            _this._emitter.emit('error', e);
            return _this._dao.setErrorFlag(meta.id);
        });
    };
    /**
     * Produce states of buckets at each point in history
     *
     * For bucket data, each tuple will contain the state of the bucket
     * prior to the corresponding delta having been applied.  For rate data,
     * the tuple will also contain the state of the bucket at the point of
     * rating.
     *
     * @param deltas   - deltas to apply
     * @param bucket   - current state of bucket prior to deltas
     * @param ratedata - current state of ratedata prior to deltas
     *
     * @return deltas paired with state prior to its application
     */
    DeltaProcessor.prototype._applyDeltas = function (deltas, bucket, ratedata) {
        var pairs = [];
        var bucket_state = bucket;
        var ratedata_state = ratedata;
        var i = deltas.length;
        while (i--) {
            var delta = deltas[i];
            pairs[i] = [delta, bucket_state, ratedata_state];
            // Don't apply the final delta, since we won't use it
            if (i === 0) {
                break;
            }
            if (delta.type === this.DELTA_DATA) {
                bucket_state = this._applyDelta(bucket_state, deltas[i].data);
            }
            else {
                ratedata_state = this._applyDelta(ratedata_state, deltas[i].data);
            }
        }
        return pairs;
    };
    /**
     * Process the next delta from the history
     *
     * @param meta    - document meta data
     * @param history - a history of deltas and their buckets (data, ratedata)
     */
    DeltaProcessor.prototype._processNextDelta = function (meta, history) {
        var _this = this;
        if (history.length === 0) {
            return Promise.resolve();
        }
        var _a = history[0], delta = _a[0], bucket = _a[1], ratedata = _a[2];
        // We only want to restict data deltas to the highest saved step
        if (delta.type === this.DELTA_DATA && delta.step_id > meta.topSavedStepId) {
            return this._processNextDelta(meta, history.filter(function (_a) {
                var delta = _a[0], _ = _a[1], __ = _a[2];
                return delta.type === _this.DELTA_RATEDATA;
            }));
        }
        var delta_uid = meta.id + '_' + delta.timestamp + '_' + delta.type;
        this._emitter.emit('delta-process-start', delta_uid);
        return this._publisher
            .publish(meta, delta, bucket, ratedata)
            .then(function (_) { return _this._dao.setPublishedTs(meta.id, delta.type, delta.timestamp); })
            .then(function (_) { return _this._emitter.emit('delta-process-end', delta_uid); })
            .then(function (_) { return _this._processNextDelta(meta, history.slice(1)); });
    };
    /**
     * Get sorted list of deltas
     *
     * @param doc - the document
     *
     * @return a list of deltas sorted by timestamp
     */
    DeltaProcessor.prototype._getTimestampSortedDeltas = function (doc) {
        var data_deltas = this._getDeltas(doc, this.DELTA_RATEDATA);
        var ratedata_deltas = this._getDeltas(doc, this.DELTA_DATA);
        var deltas = data_deltas.concat(ratedata_deltas);
        deltas.sort(this._sortByTimestamp);
        return this._mergeDeltas(deltas);
    };
    /**
     * Get trimmed delta list
     *
     * @param doc  - the document
     * @param type - the delta type to get
     *
     * @return a trimmed list of deltas
     */
    DeltaProcessor.prototype._getDeltas = function (doc, type) {
        var deltas_obj = doc.rdelta || {};
        var deltas = deltas_obj[type] || [];
        var filtered_deltas = [];
        // Mark each delta with its type
        deltas.forEach(function (delta) {
            delta.type = type;
            if ((doc === null || doc === void 0 ? void 0 : doc.deltaPublishedTs) &&
                doc.deltaPublishedTs[type] &&
                delta.timestamp <= doc.deltaPublishedTs[type]) {
                return;
            }
            filtered_deltas.push(delta);
        });
        return filtered_deltas;
    };
    /**
     * Sort an array of deltas by timestamp
     *
     * @param a - The first delta to compare
     * @param b - The second delta to compare
     *
     * @return a sort value
     */
    DeltaProcessor.prototype._sortByTimestamp = function (a, b) {
        if (a.timestamp < b.timestamp) {
            return -1;
        }
        if (a.timestamp > b.timestamp) {
            return 1;
        }
        return 0;
    };
    return DeltaProcessor;
}());
exports.DeltaProcessor = DeltaProcessor;
//# sourceMappingURL=data:application/json;base64, 