import { __assign, __extends } from "tslib";
import assert from "assert";
import { random } from "lodash";
import { EMPTY_MESSAGE, ZERO_ADDRESS, ZERO_BYTES } from "../../constants";
import { FillType, } from "../../interfaces";
import { toBN, toBNWei, getCurrentTime, getMessageHash, randomAddress, BigNumber, bnZero, bnOne, toBytes32, spreadEventWithBlockNumber, toAddressType, isDefined, } from "../../utils";
import { EVMSpokePoolClient } from "../SpokePoolClient";
import { getEventManager } from "./MockEvents";
// This class replaces internal SpokePoolClient functionality, enabling
// the user to bypass on-chain queries and inject Log objects directly.
var MockSpokePoolClient = /** @class */ (function (_super) {
    __extends(MockSpokePoolClient, _super);
    function MockSpokePoolClient(logger, spokePool, chainId, deploymentBlock, opts) {
        if (opts === void 0) { opts = { hubPoolClient: null }; }
        var _this = _super.call(this, logger, spokePool, opts.hubPoolClient, chainId, deploymentBlock) || this;
        _this.destinationTokenForChainOverride = {};
        // Allow tester to set the numberOfDeposits() returned by SpokePool at a block height.
        _this.depositIdAtBlock = [];
        _this.numberOfDeposits = bnZero;
        // Event signatures. Not strictly required, but they make generated events more recognisable.
        _this.eventSignatures = {
            EnabledDepositRoute: "address,uint256,bool",
        };
        _this.latestHeightSearched = deploymentBlock;
        _this.eventManager = getEventManager(chainId, _this.eventSignatures, deploymentBlock);
        return _this;
    }
    MockSpokePoolClient.prototype.setConfigStoreClient = function (configStore) {
        this.configStoreClient = configStore;
    };
    MockSpokePoolClient.prototype.setDestinationTokenForChain = function (chainId, token) {
        this.destinationTokenForChainOverride[chainId] = token;
    };
    MockSpokePoolClient.prototype.getDestinationTokenForDeposit = function (deposit) {
        var override = this.destinationTokenForChainOverride[deposit.originChainId];
        return isDefined(override)
            ? toAddressType(override, deposit.destinationChainId)
            : _super.prototype.getDestinationTokenForDeposit.call(this, deposit);
    };
    MockSpokePoolClient.prototype.setLatestBlockNumber = function (blockNumber) {
        this.latestHeightSearched = blockNumber;
    };
    MockSpokePoolClient.prototype.setDepositIds = function (_depositIds) {
        this.depositIdAtBlock = [];
        if (_depositIds.length === 0) {
            return;
        }
        var lastDepositId = _depositIds[0];
        for (var i = 0; i < _depositIds.length; i++) {
            if (_depositIds[i].lt(lastDepositId)) {
                throw new Error("deposit ID must be equal to or greater than previous");
            }
            this.depositIdAtBlock[i] = _depositIds[i];
            lastDepositId = _depositIds[i];
        }
    };
    MockSpokePoolClient.prototype._getDepositIdAtBlock = function (blockTag) {
        return Promise.resolve(this.depositIdAtBlock[blockTag]);
    };
    MockSpokePoolClient.prototype._update = function (eventsToQuery) {
        // Generate new "on chain" responses.
        var latestBlockSearched = this.eventManager.blockNumber;
        var currentTime = getCurrentTime();
        // Ensure an array for every requested event exists, in the requested order.
        // All requested event types must be populated in the array (even if empty).
        var events = eventsToQuery.map(function () { return []; });
        this.eventManager
            .getEvents()
            .flat()
            .forEach(function (event) {
            var idx = eventsToQuery.indexOf(event.event);
            if (idx !== -1) {
                events[idx].push(event);
            }
        });
        var eventsWithBlockNumber = events.map(function (eventList) {
            return eventList.map(function (event) { return spreadEventWithBlockNumber(event); });
        });
        return Promise.resolve({
            success: true,
            firstDepositId: bnZero,
            currentTime: currentTime,
            events: eventsWithBlockNumber,
            searchEndBlock: this.eventSearchConfig.to || latestBlockSearched,
        });
    };
    MockSpokePoolClient.prototype.deposit = function (deposit) {
        return this._deposit("FundsDeposited", deposit);
    };
    MockSpokePoolClient.prototype._deposit = function (event, deposit) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
        var blockNumber = deposit.blockNumber, txnIndex = deposit.txnIndex;
        var depositId = deposit.depositId, destinationChainId = deposit.destinationChainId, inputAmount = deposit.inputAmount, outputAmount = deposit.outputAmount;
        depositId !== null && depositId !== void 0 ? depositId : (depositId = this.numberOfDeposits);
        this.numberOfDeposits = depositId.add(bnOne);
        destinationChainId !== null && destinationChainId !== void 0 ? destinationChainId : (destinationChainId = random(1, 42161, false));
        var depositor = (_b = (_a = deposit.depositor) === null || _a === void 0 ? void 0 : _a.toBytes32()) !== null && _b !== void 0 ? _b : toBytes32(randomAddress());
        var recipient = (_d = (_c = deposit.recipient) === null || _c === void 0 ? void 0 : _c.toBytes32()) !== null && _d !== void 0 ? _d : toBytes32(depositor);
        var inputToken = (_f = (_e = deposit.inputToken) === null || _e === void 0 ? void 0 : _e.toBytes32()) !== null && _f !== void 0 ? _f : toBytes32(randomAddress());
        var outputToken = (_h = (_g = deposit.outputToken) === null || _g === void 0 ? void 0 : _g.toBytes32()) !== null && _h !== void 0 ? _h : inputToken;
        var exclusiveRelayer = (_k = (_j = deposit.exclusiveRelayer) === null || _j === void 0 ? void 0 : _j.toBytes32()) !== null && _k !== void 0 ? _k : toBytes32(ZERO_ADDRESS);
        inputAmount !== null && inputAmount !== void 0 ? inputAmount : (inputAmount = toBNWei(random(1, 1000, false)));
        outputAmount !== null && outputAmount !== void 0 ? outputAmount : (outputAmount = inputAmount.mul(toBN("0.95")));
        var message = (_l = deposit.message) !== null && _l !== void 0 ? _l : "0x";
        var topics = [destinationChainId, depositId, depositor];
        var quoteTimestamp = (_m = deposit.quoteTimestamp) !== null && _m !== void 0 ? _m : getCurrentTime();
        var args = {
            depositId: depositId,
            originChainId: (_o = deposit.originChainId) !== null && _o !== void 0 ? _o : this.chainId,
            destinationChainId: destinationChainId,
            depositor: depositor,
            recipient: recipient,
            inputToken: inputToken,
            inputAmount: inputAmount,
            outputToken: outputToken,
            outputAmount: outputAmount,
            quoteTimestamp: quoteTimestamp,
            fillDeadline: (_p = deposit.fillDeadline) !== null && _p !== void 0 ? _p : quoteTimestamp + 3600,
            exclusiveRelayer: exclusiveRelayer,
            exclusivityDeadline: (_q = deposit.exclusivityDeadline) !== null && _q !== void 0 ? _q : quoteTimestamp + 600,
            message: message,
        };
        return this.eventManager.generateEvent({
            event: event,
            address: this.spokePool.address,
            topics: topics.map(function (topic) { return topic.toString(); }),
            args: args,
            blockNumber: blockNumber,
            transactionIndex: txnIndex,
        });
    };
    MockSpokePoolClient.prototype.fillRelay = function (fill) {
        return this._fillRelay("FilledRelay", fill);
    };
    MockSpokePoolClient.prototype._fillRelay = function (event, fill) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2;
        var blockNumber = fill.blockNumber, txnIndex = fill.txnIndex;
        var originChainId = fill.originChainId, depositId = fill.depositId, inputAmount = fill.inputAmount, outputAmount = fill.outputAmount, fillDeadline = fill.fillDeadline;
        originChainId !== null && originChainId !== void 0 ? originChainId : (originChainId = random(1, 42161, false));
        depositId !== null && depositId !== void 0 ? depositId : (depositId = BigNumber.from(random(1, 100000, false)));
        inputAmount !== null && inputAmount !== void 0 ? inputAmount : (inputAmount = toBNWei(random(1, 1000, false)));
        outputAmount !== null && outputAmount !== void 0 ? outputAmount : (outputAmount = inputAmount);
        fillDeadline !== null && fillDeadline !== void 0 ? fillDeadline : (fillDeadline = getCurrentTime() + 60);
        var depositor = (_b = (_a = fill.depositor) === null || _a === void 0 ? void 0 : _a.toBytes32()) !== null && _b !== void 0 ? _b : toBytes32(randomAddress());
        var recipient = (_d = (_c = fill.recipient) === null || _c === void 0 ? void 0 : _c.toBytes32()) !== null && _d !== void 0 ? _d : toBytes32(depositor);
        var inputToken = (_f = (_e = fill.inputToken) === null || _e === void 0 ? void 0 : _e.toBytes32()) !== null && _f !== void 0 ? _f : toBytes32(randomAddress());
        var outputToken = (_h = (_g = fill.outputToken) === null || _g === void 0 ? void 0 : _g.toBytes32()) !== null && _h !== void 0 ? _h : toBytes32(ZERO_ADDRESS);
        var exclusiveRelayer = (_k = (_j = fill.exclusiveRelayer) === null || _j === void 0 ? void 0 : _j.toBytes32()) !== null && _k !== void 0 ? _k : toBytes32(ZERO_ADDRESS);
        var relayer = (_m = (_l = fill.relayer) === null || _l === void 0 ? void 0 : _l.toBytes32()) !== null && _m !== void 0 ? _m : toBytes32(randomAddress());
        var topics = [originChainId, depositId, relayer];
        var message = (_o = fill.message) !== null && _o !== void 0 ? _o : EMPTY_MESSAGE;
        var updatedMessage = (_q = (_p = fill.relayExecutionInfo) === null || _p === void 0 ? void 0 : _p.updatedMessage) !== null && _q !== void 0 ? _q : message;
        var updatedRecipient = (_s = (_r = fill.relayExecutionInfo) === null || _r === void 0 ? void 0 : _r.updatedRecipient.toBytes32()) !== null && _s !== void 0 ? _s : recipient;
        var relayExecutionInfo = {
            updatedRecipient: updatedRecipient,
            updatedOutputAmount: (_u = (_t = fill.relayExecutionInfo) === null || _t === void 0 ? void 0 : _t.updatedOutputAmount) !== null && _u !== void 0 ? _u : outputAmount,
            fillType: (_w = (_v = fill.relayExecutionInfo) === null || _v === void 0 ? void 0 : _v.fillType) !== null && _w !== void 0 ? _w : FillType.FastFill,
        };
        var _args = {
            inputToken: inputToken,
            outputToken: outputToken,
            inputAmount: fill.inputAmount,
            outputAmount: fill.outputAmount,
            repaymentChainId: (_x = fill.repaymentChainId) !== null && _x !== void 0 ? _x : this.chainId,
            originChainId: originChainId,
            depositId: depositId,
            fillDeadline: fillDeadline,
            exclusivityDeadline: (_y = fill.exclusivityDeadline) !== null && _y !== void 0 ? _y : fillDeadline,
            exclusiveRelayer: exclusiveRelayer,
            relayer: relayer,
            depositor: depositor,
            recipient: recipient,
            relayExecutionInfo: {
                updatedRecipient: updatedRecipient,
                updatedOutputAmount: (_0 = (_z = fill.relayExecutionInfo) === null || _z === void 0 ? void 0 : _z.updatedOutputAmount) !== null && _0 !== void 0 ? _0 : outputAmount,
                fillType: (_2 = (_1 = fill.relayExecutionInfo) === null || _1 === void 0 ? void 0 : _1.fillType) !== null && _2 !== void 0 ? _2 : FillType.FastFill,
            },
        };
        var args = __assign(__assign({}, _args), { messageHash: getMessageHash(message), relayExecutionInfo: __assign(__assign({}, relayExecutionInfo), { updatedMessageHash: getMessageHash(updatedMessage) }) });
        return this.eventManager.generateEvent({
            event: event,
            address: this.spokePool.address,
            topics: topics.map(function (topic) { return topic.toString(); }),
            args: args,
            blockNumber: blockNumber,
            transactionIndex: txnIndex,
        });
    };
    MockSpokePoolClient.prototype.speedUpDeposit = function (speedUp) {
        return this._speedUpDeposit("RequestedSpeedUpDeposit", speedUp);
    };
    MockSpokePoolClient.prototype._speedUpDeposit = function (event, speedUp) {
        var depositor = speedUp.depositor.toBytes32();
        var topics = [speedUp.depositId, depositor];
        var args = __assign({}, speedUp);
        return this.eventManager.generateEvent({
            event: event,
            address: this.spokePool.address,
            topics: topics.map(function (topic) { return topic.toString(); }),
            args: __assign(__assign({}, args), { depositor: depositor, updatedRecipient: speedUp.updatedRecipient.toBytes32() }),
        });
    };
    MockSpokePoolClient.prototype.setTokensBridged = function (tokensBridged) {
        var event = "TokensBridged";
        var topics = [tokensBridged.chainId, tokensBridged.leafId, tokensBridged.l2TokenAddress];
        var args = __assign({}, tokensBridged);
        return this.eventManager.generateEvent({
            event: event,
            address: this.spokePool.address,
            topics: topics.map(function (topic) { return topic.toString(); }),
            args: args,
        });
    };
    MockSpokePoolClient.prototype.requestSlowFill = function (request) {
        return this._requestSlowFill("RequestedSlowFill", request);
    };
    MockSpokePoolClient.prototype._requestSlowFill = function (event, request) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
        var originChainId = request.originChainId, depositId = request.depositId;
        var topics = [originChainId, depositId];
        var args = __assign({}, request);
        var depositor = (_a = args.depositor.toBytes32()) !== null && _a !== void 0 ? _a : toBytes32(randomAddress());
        return this.eventManager.generateEvent({
            event: event,
            address: this.spokePool.address,
            topics: topics.map(function (topic) { return topic.toString(); }),
            args: __assign(__assign({}, args), { destinationChainId: this.chainId, depositor: depositor, recipient: (_c = (_b = args.recipient) === null || _b === void 0 ? void 0 : _b.toBytes32()) !== null && _c !== void 0 ? _c : depositor, inputToken: (_e = (_d = args.inputToken) === null || _d === void 0 ? void 0 : _d.toBytes32()) !== null && _e !== void 0 ? _e : toBytes32(randomAddress()), outputToken: (_g = (_f = args.outputToken) === null || _f === void 0 ? void 0 : _f.toBytes32()) !== null && _g !== void 0 ? _g : toBytes32(ZERO_ADDRESS), exclusiveRelayer: (_j = (_h = args.exclusiveRelayer) === null || _h === void 0 ? void 0 : _h.toBytes32()) !== null && _j !== void 0 ? _j : toBytes32(ZERO_ADDRESS), messageHash: (_k = args.messageHash) !== null && _k !== void 0 ? _k : ZERO_BYTES }),
            blockNumber: request.blockNumber,
            transactionIndex: request.txnIndex,
        });
    };
    // This is a simple wrapper around fillRelay().
    // rootBundleId and proof are discarded here - we have no interest in verifying that.
    MockSpokePoolClient.prototype.executeSlowRelayLeaf = function (leaf) {
        var destinationChainId = this.chainId;
        var fill = __assign(__assign({}, leaf.relayData), { destinationChainId: destinationChainId, relayer: toAddressType(ZERO_ADDRESS, destinationChainId), repaymentChainId: 0, relayExecutionInfo: {
                updatedRecipient: leaf.relayData.recipient,
                updatedOutputAmount: leaf.updatedOutputAmount,
                updatedMessage: leaf.relayData.message,
                updatedMessageHash: getMessageHash(leaf.relayData.message),
                fillType: FillType.SlowFill,
            } });
        return this.fillRelay(fill);
    };
    MockSpokePoolClient.prototype.executeRelayerRefundLeaf = function (refund) {
        var _a;
        var event = "ExecutedRelayerRefundRoot";
        var chainId = (_a = refund.chainId) !== null && _a !== void 0 ? _a : this.chainId;
        assert(chainId === this.chainId);
        var rootBundleId = refund.rootBundleId, leafId = refund.leafId;
        var topics = [chainId, rootBundleId, leafId];
        var args = {
            chainId: chainId,
            rootBundleId: rootBundleId,
            leafId: leafId,
            amountToReturn: refund.amountToReturn,
            l2TokenAddress: refund.l2TokenAddress,
            refundAddresses: refund.refundAddresses,
            refundAmounts: refund.refundAmounts,
        };
        return this.eventManager.generateEvent({
            event: event,
            address: this.spokePool.address,
            topics: topics.map(function (topic) { return topic.toString(); }),
            args: args,
            blockNumber: refund.blockNumber,
        });
    };
    MockSpokePoolClient.prototype.setEnableRoute = function (originToken, destinationChainId, enabled, overrides) {
        if (overrides === void 0) { overrides = {}; }
        var event = "EnabledDepositRoute";
        var topics = [originToken, destinationChainId];
        var args = { originToken: originToken, destinationChainId: destinationChainId, enabled: enabled };
        return this.eventManager.generateEvent({
            event: event,
            address: this.spokePool.address,
            topics: topics.map(function (topic) { return topic.toString(); }),
            args: args,
            blockNumber: overrides.blockNumber,
        });
    };
    return MockSpokePoolClient;
}(EVMSpokePoolClient));
export { MockSpokePoolClient };
//# sourceMappingURL=MockSpokePoolClient.js.map