"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InternalTxRelatedFilterTransform = void 0;
const walletAddress_1 = require("../../../../models/walletAddress");
const streamWithEventPipe_1 = require("../../../../utils/streamWithEventPipe");
class InternalTxRelatedFilterTransform extends streamWithEventPipe_1.TransformWithEventPipe {
    constructor(web3, walletId) {
        super({ objectMode: true });
        this.web3 = web3;
        this.walletId = walletId;
        this.walletAddresses = [];
    }
    /**
     * This creates a duplicate transaction object for each relevant
     * internal tx with the `value` field reset to the internal value.
     * @param tx Transaction object
     * @param _ Encoding (discarded)
     * @param done Callback
     * @returns
     */
    async _transform(tx, _, done) {
        const walletAddresses = await this.getWalletAddresses(tx);
        // TODO: rethink how we handle complex smart contracts. Creating objects w/ dup txid's doesn't seem right.
        let internalTxsToProcess = [];
        if (tx.effects && tx.effects.length) {
            const walletRelatedInternalTxs = tx.effects.filter((internalTx) => walletAddresses.includes(internalTx.to) && !internalTx.contractAddress);
            const refundTxs = walletRelatedInternalTxs.filter(i => i.to === tx.from);
            const nonRefundTxs = walletRelatedInternalTxs.filter(i => i.to != tx.from);
            const refundTotal = refundTxs.reduce((a, b) => a + Number(b.amount), 0);
            const txValue = Number(tx.value);
            // Only consider it a refund if the amount refunded is less than or equal to tx value
            const hasRefund = refundTotal <= txValue;
            if (hasRefund) {
                // Subtract refund from tx.value
                tx.value = txValue - refundTotal;
                // Handle any remaining internal txs
                internalTxsToProcess = nonRefundTxs;
            }
            else {
                // Treat all txs normal
                internalTxsToProcess = refundTxs.concat(nonRefundTxs);
            }
            for (const internalTx of internalTxsToProcess) {
                const _tx = Object.assign({}, tx);
                _tx.value = Number(internalTx.amount);
                _tx.to = internalTx.to;
                if (internalTx.from != tx.from) {
                    _tx.initialFrom = tx.from;
                    _tx.from = internalTx.from;
                }
                // This is how a requester can verify uniqueness in light of duplicated txids
                _tx.callStack = internalTx.callStack;
                this.push(_tx);
            }
        }
        // Discard original tx if original value is 0 - perhaps after refunds
        if (internalTxsToProcess.length === 0 || tx.value != 0) {
            this.push(tx);
        }
        return done();
    }
    async getWalletAddresses(tx) {
        if (!this.walletAddresses.length) {
            const cursor = walletAddress_1.WalletAddressStorage.collection
                .find({ chain: tx.chain, network: tx.network, wallet: this.walletId });
            try {
                this.walletAddresses = await cursor.toArray();
            }
            finally {
                await cursor.close();
            }
        }
        return this.walletAddresses.map(walletAddress => this.web3.utils.toChecksumAddress(walletAddress.address));
    }
}
exports.InternalTxRelatedFilterTransform = InternalTxRelatedFilterTransform;
//# sourceMappingURL=internalTxTransform.js.map