"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.modifyInstructionAffirmation = exports.prepareStorage = exports.getAuthorization = exports.prepareModifyInstructionAffirmation = void 0;
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const bluebird_1 = __importDefault(require("bluebird"));
const utils_1 = require("./utils");
const internal_1 = require("../../internal");
const types_1 = require("../../types");
const internal_2 = require("../../types/internal");
const utils_2 = require("../../types/utils");
const conversion_1 = require("../../utils/conversion");
/**
 * @hidden
 */
function prepareModifyInstructionAffirmation(args) {
    return __awaiter(this, void 0, void 0, function* () {
        const { context: { polymeshApi: { tx: { settlement: settlementTx }, query: { settlement }, }, }, context, storage: { portfolios, senderLegAmount, totalLegAmount }, } = this;
        const { operation, id } = args;
        const instruction = new internal_1.Instruction({ id }, context);
        yield utils_1.assertInstructionValid(instruction, context);
        if (!portfolios.length) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.UnmetPrerequisite,
                message: 'The signing Identity is not involved in this Instruction',
            });
        }
        const rawInstructionId = conversion_1.bigNumberToU64(id, context);
        const rawPortfolioIds = portfolios.map(portfolio => conversion_1.portfolioIdToMeshPortfolioId(conversion_1.portfolioLikeToPortfolioId(portfolio), context));
        const excludeCriteria = [];
        let errorMessage;
        let transaction = null;
        switch (operation) {
            case internal_2.InstructionAffirmationOperation.Affirm: {
                excludeCriteria.push(types_1.AffirmationStatus.Affirmed);
                errorMessage = 'The Instruction is already affirmed';
                transaction = settlementTx.affirmInstruction;
                break;
            }
            case internal_2.InstructionAffirmationOperation.Withdraw: {
                excludeCriteria.push(types_1.AffirmationStatus.Pending);
                errorMessage = 'The instruction is not affirmed';
                transaction = settlementTx.withdrawAffirmation;
                break;
            }
        }
        const multiArgs = rawPortfolioIds.map(portfolioId => utils_2.tuple(portfolioId, rawInstructionId));
        const rawAffirmationStatuses = yield settlement.userAffirmations.multi(multiArgs);
        const affirmationStatuses = rawAffirmationStatuses.map(conversion_1.meshAffirmationStatusToAffirmationStatus);
        const validPortfolioIds = rawPortfolioIds.filter((_, index) => !excludeCriteria.includes(affirmationStatuses[index]));
        if (!validPortfolioIds.length) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.NoDataChange,
                // As InstructionAffirmationOperation.Reject has no excludeCriteria, if this error is thrown
                // it means that the operation had to be either affirm or withdraw, and so the errorMessage was set
                // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                message: errorMessage,
            });
        }
        // rejection works a bit different
        if (transaction) {
            this.addTransaction({
                transaction,
                feeMultiplier: senderLegAmount,
                args: [rawInstructionId, validPortfolioIds, conversion_1.bigNumberToU32(senderLegAmount, context)],
            });
        }
        else {
            this.addTransaction({
                transaction: settlementTx.rejectInstruction,
                feeMultiplier: totalLegAmount,
                args: [rawInstructionId, validPortfolioIds[0], conversion_1.bigNumberToU32(totalLegAmount, context)],
            });
        }
        return instruction;
    });
}
exports.prepareModifyInstructionAffirmation = prepareModifyInstructionAffirmation;
/**
 * @hidden
 */
function getAuthorization({ operation }) {
    return __awaiter(this, void 0, void 0, function* () {
        const { storage: { portfolios }, } = this;
        let transactions;
        switch (operation) {
            case internal_2.InstructionAffirmationOperation.Affirm: {
                transactions = [types_1.TxTags.settlement.AffirmInstruction];
                break;
            }
            case internal_2.InstructionAffirmationOperation.Withdraw: {
                transactions = [types_1.TxTags.settlement.WithdrawAffirmation];
                break;
            }
            case internal_2.InstructionAffirmationOperation.Reject: {
                transactions = [types_1.TxTags.settlement.RejectInstruction];
                break;
            }
        }
        return {
            permissions: {
                portfolios,
                transactions,
                assets: [],
            },
        };
    });
}
exports.getAuthorization = getAuthorization;
/**
 * @hidden
 */
function prepareStorage({ id }) {
    return __awaiter(this, void 0, void 0, function* () {
        const { context } = this;
        const instruction = new internal_1.Instruction({ id }, context);
        const [{ data: legs }, { did }] = yield Promise.all([
            instruction.getLegs(),
            context.getSigningIdentity(),
        ]);
        const [portfolios, senderLegAmount] = yield bluebird_1.default.reduce(legs, (result, { from, to }) => __awaiter(this, void 0, void 0, function* () {
            const [fromIsCustodied, toIsCustodied] = yield Promise.all([
                from.isCustodiedBy({ identity: did }),
                to.isCustodiedBy({ identity: did }),
            ]);
            const [custodiedPortfolios, amount] = result;
            let res = [...custodiedPortfolios];
            let legAmount = amount;
            if (fromIsCustodied) {
                res = [...res, from];
                legAmount = legAmount.plus(1);
            }
            if (toIsCustodied) {
                res = [...res, to];
            }
            return utils_2.tuple(res, legAmount);
        }), [[], new bignumber_js_1.default(0)]);
        return { portfolios, senderLegAmount, totalLegAmount: new bignumber_js_1.default(legs.length) };
    });
}
exports.prepareStorage = prepareStorage;
/**
 * @hidden
 */
const modifyInstructionAffirmation = () => new internal_1.Procedure(prepareModifyInstructionAffirmation, getAuthorization, prepareStorage);
exports.modifyInstructionAffirmation = modifyInstructionAffirmation;
//# sourceMappingURL=modifyInstructionAffirmation.js.map