"use strict";
/* istanbul ignore file */
/* eslint-disable @typescript-eslint/naming-convention */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.updateTransactionStatus = exports.setupNextTransactions = exports.reset = exports.initMocks = exports.mockPolymeshTransactionModule = void 0;
const lodash_1 = require("lodash");
const sinon_1 = __importDefault(require("sinon"));
const types_1 = require("../../types");
let polymeshTransactionConstructorStub;
const MockPolymeshTransactionClass = class {
    /**
     * @hidden
     */
    constructor(...args) {
        return polymeshTransactionConstructorStub(...args);
    }
};
const mockPolymeshTransactionModule = (path) => () => (Object.assign(Object.assign({}, jest.requireActual(path)), { PolymeshTransaction: MockPolymeshTransactionClass }));
exports.mockPolymeshTransactionModule = mockPolymeshTransactionModule;
const transactionMocksData = new Map();
/**
 * @hidden
 *
 * Initialize the factory by adding default all-purpose functionality to the mock manager
 */
function initMocks() {
    transactionMocksData.clear();
    polymeshTransactionConstructorStub = sinon_1.default.stub();
    polymeshTransactionConstructorStub.callsFake(args => {
        const value = lodash_1.merge({}, args);
        Object.setPrototypeOf(value, require("../../internal").PolymeshTransaction.prototype);
        return value;
    });
}
exports.initMocks = initMocks;
/**
 * @hidden
 * Reinitialize mocks
 */
function reset() {
    initMocks();
}
exports.reset = reset;
/**
 * @hidden
 * Determine the next set of transactions that will be created when the PolymeshTransaction
 * constructor is used
 *
 * @param specs - transactions that will be constructed
 */
function setupNextTransactions(specs) {
    const receipt = 'someReceipt';
    const error = 'Transaction Error';
    const updateStatusStub = sinon_1.default.stub();
    const instances = specs.map(({ isCritical, autoResolve, fees = null, payingAccount = null, supportsSubsidy = true }) => {
        const instance = {};
        if (autoResolve === types_1.TransactionStatus.Failed) {
            instance.run = sinon_1.default.stub().rejects(new Error(error));
        }
        else if (autoResolve === types_1.TransactionStatus.Succeeded) {
            instance.run = sinon_1.default.stub().resolves(receipt);
        }
        else {
            const runStub = sinon_1.default.stub().returns(new Promise((resolve, reject) => {
                updateStatusStub.callsFake((newStatus) => {
                    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                    const { statusChangeListener } = transactionMocksData.get(instance);
                    statusChangeListener(instance);
                    if (newStatus === types_1.TransactionStatus.Succeeded) {
                        resolve(receipt);
                    }
                    if ([
                        types_1.TransactionStatus.Aborted,
                        types_1.TransactionStatus.Failed,
                        types_1.TransactionStatus.Rejected,
                    ].includes(newStatus)) {
                        reject(new Error(error));
                    }
                });
            }));
            instance.run = runStub;
        }
        instance.isCritical = isCritical;
        instance.onStatusChange = sinon_1.default.stub().callsFake(listener => {
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            const mockData = transactionMocksData.get(instance);
            transactionMocksData.set(instance, Object.assign(Object.assign({}, mockData), { statusChangeListener: listener }));
        });
        instance.status = autoResolve || types_1.TransactionStatus.Idle;
        instance.getPayingAccount = sinon_1.default
            .stub()
            .resolves(payingAccount);
        instance.getFees = sinon_1.default.stub().resolves(fees);
        instance.supportsSubsidy = sinon_1.default
            .stub()
            .returns(supportsSubsidy);
        transactionMocksData.set(instance, {
            updateStatusStub,
            resolved: !!autoResolve,
            statusChangeListener: sinon_1.default.stub(),
        });
        return instance;
    });
    polymeshTransactionConstructorStub = sinon_1.default.stub();
    instances.forEach((instance, index) => {
        polymeshTransactionConstructorStub.onCall(index).returns(instance);
    });
    return instances;
}
exports.setupNextTransactions = setupNextTransactions;
/**
 * @hidden
 * Update the status of an existing mock transaction. Will throw an exception if the transaction has already been resolved
 *
 * @param transaction - transaction to update
 * @param status - new status
 */
function updateTransactionStatus(transaction, status) {
    const transactionMockData = transactionMocksData.get(transaction);
    if (!transactionMockData) {
        throw new Error('Invalid transaction object');
    }
    if (transactionMockData.resolved) {
        throw new Error('Cannot update status on an already resolved transaction');
    }
    if (status === transaction.status) {
        throw new Error(`Status is already ${status}`);
    }
    if ([types_1.TransactionStatus.Aborted, types_1.TransactionStatus.Failed, types_1.TransactionStatus.Succeeded].includes(status)) {
        transactionMocksData.set(transaction, Object.assign(Object.assign({}, transactionMockData), { resolved: true }));
    }
    transaction.status = status;
    transactionMockData.updateStatusStub(status);
}
exports.updateTransactionStatus = updateTransactionStatus;
//# sourceMappingURL=polymeshTransaction.js.map