"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());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EventBus = void 0;
const threads_core_1 = require("@textile/threads-core");
const async_retry_1 = __importDefault(require("async-retry"));
const deepmerge_1 = __importDefault(require("deepmerge"));
const loglevel_1 = __importDefault(require("loglevel"));
const tsee_1 = require("tsee");
const queue_1 = require("./queue");
const logger = loglevel_1.default.getLogger('store:eventbus');
const retryOpts = {
    retries: 5,
    onRetry: err => logger.warn(`create record failed (${err.message}), retrying...`),
};
class EventBus extends tsee_1.EventEmitter {
    constructor(queue, network, opts = {}) {
        super();
        this.network = network;
        this.isStarted = false;
        this.queue = queue instanceof queue_1.Queue ? queue : new queue_1.Queue(queue);
        this.queue.on('next', (task) => __awaiter(this, void 0, void 0, function* () {
            if (task === undefined)
                return;
            const { job } = task;
            const { id, body } = job;
            const threadID = threads_core_1.ThreadID.fromBytes(id);
            try {
                yield async_retry_1.default((_bail, _num) => __awaiter(this, void 0, void 0, function* () {
                    // @todo: We could use bail here to bail if the network errors out with a 'headers closed error'
                    // This would mean that the gRPC network isn't running, i.e., we are in 'offline' mode
                    yield this.network.createRecord(threadID, body);
                    // @todo: Add debugging outputs here
                    return this.queue.done();
                }), deepmerge_1.default(retryOpts, opts));
            }
            catch (err) {
                // Skip it for now, we've already retried
                this.queue.done(true);
            }
        }));
    }
    networkWatcher(id, start = true) {
        if (start) {
            const func = (rec) => __awaiter(this, void 0, void 0, function* () {
                if (rec)
                    this.emit('record', rec);
            });
            this.closer = id ? this.network.subscribe(func, [id]) : this.network.subscribe(func);
        }
        else if (this.closer) {
            return this.closer.close();
        }
    }
    start(id) {
        return __awaiter(this, void 0, void 0, function* () {
            this.isStarted = true;
            yield this.queue.open();
            this.networkWatcher(id);
            return this.queue.start();
        });
    }
    stop() {
        return __awaiter(this, void 0, void 0, function* () {
            this.isStarted = false;
            this.networkWatcher(undefined, false);
            this.queue.stop();
            yield this.queue.close();
        });
    }
    /**
     * Push an event onto to the queue
     * @param event Object to be serialized and pushed to queue via JSON.stringify().
     */
    push(event) {
        return this.queue.push(event);
    }
}
exports.EventBus = EventBus;
//# sourceMappingURL=eventbus.js.map