"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.moveFunds = exports.getAuthorization = exports.prepareMoveFunds = void 0;
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const utils_1 = require("./utils");
const internal_1 = require("../../internal");
const types_1 = require("../../types");
const conversion_1 = require("../../utils/conversion");
const internal_2 = require("../../utils/internal");
/**
 * @hidden
 */
function prepareMoveFunds(args) {
    return __awaiter(this, void 0, void 0, function* () {
        const { context: { polymeshApi: { tx: { portfolio }, }, }, context, } = this;
        const { from: fromPortfolio, to, items } = args;
        const { owner: { did: fromDid }, } = fromPortfolio;
        let toPortfolio;
        if (!to) {
            toPortfolio = new internal_1.DefaultPortfolio({ did: fromDid }, context);
        }
        else if (to instanceof bignumber_js_1.default) {
            toPortfolio = new internal_1.NumberedPortfolio({ did: fromDid, id: to }, context);
        }
        else {
            toPortfolio = to;
        }
        const { owner: { did: toDid }, } = toPortfolio;
        const fromPortfolioId = conversion_1.portfolioLikeToPortfolioId(fromPortfolio);
        const toPortfolioId = conversion_1.portfolioLikeToPortfolioId(toPortfolio);
        yield Promise.all([
            utils_1.assertPortfolioExists(fromPortfolioId, context),
            utils_1.assertPortfolioExists(toPortfolioId, context),
        ]);
        if (fromDid !== toDid) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.ValidationError,
                message: 'Both portfolios should have the same owner',
            });
        }
        if (fromPortfolioId.number === toPortfolioId.number) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.ValidationError,
                message: 'Origin and destination should be different Portfolios',
            });
        }
        const portfolioBalances = yield fromPortfolio.getAssetBalances({
            assets: items.map(({ asset }) => asset),
        });
        const balanceExceeded = [];
        portfolioBalances.forEach(({ asset: { ticker }, free }) => {
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            const transferItem = items.find(({ asset: itemAsset }) => internal_2.getTicker(itemAsset) === ticker);
            if (transferItem.amount.gt(free)) {
                balanceExceeded.push(Object.assign(Object.assign({}, transferItem), { free }));
            }
        });
        if (balanceExceeded.length) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.InsufficientBalance,
                message: "Some of the amounts being transferred exceed the Portfolio's balance",
                data: {
                    balanceExceeded,
                },
            });
        }
        const rawFrom = conversion_1.portfolioIdToMeshPortfolioId(fromPortfolioId, context);
        const rawTo = conversion_1.portfolioIdToMeshPortfolioId(toPortfolioId, context);
        const rawMovePortfolioItems = items.map(item => conversion_1.portfolioMovementToMovePortfolioItem(item, context));
        this.addTransaction({
            transaction: portfolio.movePortfolioFunds,
            args: [rawFrom, rawTo, rawMovePortfolioItems],
        });
    });
}
exports.prepareMoveFunds = prepareMoveFunds;
/**
 * @hidden
 */
function getAuthorization({ from, to }) {
    const { context } = this;
    const { owner: { did }, } = from;
    let portfolioId = { did };
    if (from instanceof internal_1.NumberedPortfolio) {
        portfolioId = Object.assign(Object.assign({}, portfolioId), { number: from.id });
    }
    let toPortfolio;
    if (!to) {
        toPortfolio = new internal_1.DefaultPortfolio({ did }, context);
    }
    else if (to instanceof bignumber_js_1.default) {
        toPortfolio = new internal_1.NumberedPortfolio({ did, id: to }, context);
    }
    else {
        toPortfolio = to;
    }
    return {
        permissions: {
            transactions: [types_1.TxTags.portfolio.MovePortfolioFunds],
            assets: [],
            portfolios: [from, toPortfolio],
        },
        roles: [{ type: types_1.RoleType.PortfolioCustodian, portfolioId }],
    };
}
exports.getAuthorization = getAuthorization;
/**
 * @hidden
 */
const moveFunds = () => new internal_1.Procedure(prepareMoveFunds, getAuthorization);
exports.moveFunds = moveFunds;
//# sourceMappingURL=moveFunds.js.map