"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TaskSendWaiting = void 0;
const Monitor_1 = require("../Monitor");
const WalletMonitorTask_1 = require("./WalletMonitorTask");
const attemptToPostReqsToNetwork_1 = require("../../storage/methods/attemptToPostReqsToNetwork");
const aggregateResults_1 = require("../../utility/aggregateResults");
const utilityHelpers_1 = require("../../utility/utilityHelpers");
const EntityProvenTxReq_1 = require("../../storage/schema/entities/EntityProvenTxReq");
class TaskSendWaiting extends WalletMonitorTask_1.WalletMonitorTask {
    constructor(monitor, triggerMsecs = Monitor_1.Monitor.oneSecond * 8, agedMsecs = Monitor_1.Monitor.oneSecond * 7, sendingMsecs = Monitor_1.Monitor.oneMinute * 5) {
        super(monitor, TaskSendWaiting.taskName);
        this.triggerMsecs = triggerMsecs;
        this.agedMsecs = agedMsecs;
        this.sendingMsecs = sendingMsecs;
        this.includeSending = true;
    }
    trigger(nowMsecsSinceEpoch) {
        this.includeSending =
            !this.lastSendingRunMsecsSinceEpoch || nowMsecsSinceEpoch > this.lastSendingRunMsecsSinceEpoch + this.sendingMsecs;
        if (this.includeSending)
            this.lastSendingRunMsecsSinceEpoch = nowMsecsSinceEpoch;
        return {
            run: nowMsecsSinceEpoch > this.lastRunMsecsSinceEpoch + this.triggerMsecs
        };
    }
    async runTask() {
        let log = '';
        const limit = 100;
        let offset = 0;
        const agedLimit = new Date(Date.now() - this.agedMsecs);
        const status = this.includeSending ? ['unsent', 'sending'] : ['unsent'];
        for (;;) {
            let reqs = await this.storage.findProvenTxReqs({
                partial: {},
                status,
                paged: { limit, offset }
            });
            const count = reqs.length;
            if (reqs.length === 0)
                break;
            log += `${reqs.length} reqs with status ${status.join(' or ')}\n`;
            const agedReqs = reqs.filter(req => (0, utilityHelpers_1.verifyTruthy)(req.updated_at) < agedLimit);
            log += `  Of those reqs, ${agedReqs.length} where last updated before ${agedLimit.toISOString()}.\n`;
            log += await this.processUnsent(agedReqs, 2);
            if (count < limit)
                break;
            offset += limit;
        }
        return log;
    }
    /**
     * Process an array of 'unsent' status table.ProvenTxReq
     *
     * Send rawTx to transaction processor(s), requesting proof callbacks when possible.
     *
     * Set status 'invalid' if req is invalid.
     *
     * Set status to 'callback' on successful network submission with callback service.
     *
     * Set status to 'unmined' on successful network submission without callback service.
     *
     * Add mapi responses to database table if received.
     *
     * Increments attempts if sending was attempted.
     *
     * @param reqApis
     */
    async processUnsent(reqApis, indent = 0) {
        const txids = reqApis.map(r => r.txid);
        const logs = {};
        for (let i = 0; i < reqApis.length; i++) {
            const reqApi = reqApis[i];
            logs[reqApi.txid] = `${i} reqId=${reqApi.provenTxReqId} attempts=${reqApi.attempts} txid=${reqApi.txid}:`;
        }
        for (let i = 0; i < reqApis.length; i++) {
            const reqApi = reqApis[i];
            if (reqApi.status !== 'unsent' && reqApi.status !== 'sending') {
                logs[reqApi.txid] += ` status now ${reqApi.status}`;
                continue;
            }
            const req = new EntityProvenTxReq_1.EntityProvenTxReq(reqApi);
            const reqs = [];
            if (req.batch) {
                logs[reqApi.txid] += ` batch ${req.batch}`;
                // Make sure wew process entire batch together for efficient beef generation
                const batchReqApis = await this.storage.findProvenTxReqs({
                    partial: { batch: req.batch, status: 'unsent' }
                });
                for (const bra of batchReqApis) {
                    // Remove any matching batchReqApis from reqApis
                    const index = reqApis.findIndex(ra => ra.provenTxReqId === bra.provenTxReqId);
                    if (index > -1)
                        reqApis.slice(index, index + 1);
                    // And add to reqs being processed now:
                    reqs.push(new EntityProvenTxReq_1.EntityProvenTxReq(bra));
                }
            }
            else {
                // Just a single non-batched req...
                reqs.push(req);
            }
            const r = await this.storage.runAsStorageProvider(async (sp) => {
                return (0, attemptToPostReqsToNetwork_1.attemptToPostReqsToNetwork)(sp, reqs);
            });
            for (const rd of r.details) {
                logs[rd.txid] += ` req.status ${rd.req.status} post.status ${rd.status}`;
            }
            if (this.monitor.onTransactionBroadcasted) {
                const rar = await this.storage.runAsStorageProvider(async (sp) => {
                    const ars = [{ txid: req.txid, status: 'sending' }];
                    const { rar } = await (0, aggregateResults_1.aggregateActionResults)(sp, ars, r);
                    return rar;
                });
                this.monitor.callOnBroadcastedTransaction(rar[0]);
            }
        }
        let log = '';
        for (const txid of txids) {
            log += `${' '.repeat(indent)}${logs[txid]}\n`;
        }
        return log;
    }
}
exports.TaskSendWaiting = TaskSendWaiting;
TaskSendWaiting.taskName = 'SendWaiting';
//# sourceMappingURL=TaskSendWaiting.js.map