"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.setTransferRestrictions = exports.prepareStorage = exports.getAuthorization = exports.prepareSetTransferRestrictions = void 0;
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const internal_1 = require("../../internal");
const types_1 = require("../../types");
const internal_2 = require("../../types/internal");
const conversion_1 = require("../../utils/conversion");
const internal_3 = require("../../utils/internal");
/**
 * @hidden
 */
function isSameCondition(transferCondition, inputValue, type) {
    if (transferCondition.isMaxInvestorCount && type === types_1.TransferRestrictionType.Count) {
        const currentCount = (0, conversion_1.u64ToBigNumber)(transferCondition.asMaxInvestorCount);
        return currentCount.eq(inputValue);
    }
    else if (transferCondition.isMaxInvestorOwnership &&
        type === types_1.TransferRestrictionType.Percentage) {
        const currentOwnership = (0, conversion_1.permillToBigNumber)(transferCondition.asMaxInvestorOwnership);
        return currentOwnership.eq(inputValue);
    }
    else {
        return false;
    }
}
/**
 * @hidden
 */
function transformRestrictions(restrictions, currentRestrictions, type, context) {
    const exemptions = [];
    let someDifference = restrictions.length !== currentRestrictions.length;
    const conditions = [];
    restrictions.forEach(r => {
        let value;
        if ('count' in r) {
            value = r.count;
        }
        else {
            value = r.percentage;
        }
        const compareConditions = (transferCondition) => isSameCondition(transferCondition, value, type);
        if (!someDifference) {
            someDifference = !currentRestrictions.find(compareConditions);
        }
        const condition = { type, value };
        const rawCondition = (0, conversion_1.transferRestrictionToPolymeshTransferCondition)(condition, context);
        if (r.exemptedIdentities) {
            r.exemptedIdentities.forEach(e => {
                exemptions.push(e);
            });
        }
        conditions.push(rawCondition);
    });
    if (!someDifference) {
        throw new internal_1.PolymeshError({
            code: types_1.ErrorCode.NoDataChange,
            message: restrictions.length
                ? 'The supplied restrictions are already in place'
                : 'There are no restrictions to remove',
        });
    }
    return [conditions, exemptions];
}
/**
 * @hidden
 */
function prepareSetTransferRestrictions(args) {
    return __awaiter(this, void 0, void 0, function* () {
        const { context: { polymeshApi: { tx: { statistics }, query, consts, }, }, storage: { currentRestrictions, occupiedSlots, needStat, currentStats }, context, } = this;
        const { restrictions: { length: newRestrictionAmount }, restrictions, type, ticker, } = args;
        const tickerKey = (0, conversion_1.stringToTickerKey)(ticker, context);
        const [conditions, exemptions] = transformRestrictions(restrictions, currentRestrictions, type, context);
        const maxTransferConditions = (0, conversion_1.u32ToBigNumber)(consts.statistics.maxTransferConditionsPerAsset);
        const finalCount = occupiedSlots.plus(newRestrictionAmount);
        if (finalCount.gte(maxTransferConditions)) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.LimitExceeded,
                message: 'Cannot set more Transfer Restrictions than there are slots available',
                data: {
                    availableSlots: maxTransferConditions.minus(occupiedSlots),
                },
            });
        }
        const transactions = [];
        const op = type === types_1.TransferRestrictionType.Count
            ? (0, conversion_1.statisticsOpTypeToStatOpType)(internal_2.StatisticsOpType.Count, context)
            : (0, conversion_1.statisticsOpTypeToStatOpType)(internal_2.StatisticsOpType.Balance, context);
        if (needStat) {
            const newStat = (0, conversion_1.statisticsOpTypeToStatType)(op, context);
            currentStats.push(newStat);
            currentStats.sort().reverse();
            transactions.push((0, internal_3.checkTxType)({
                transaction: statistics.setActiveAssetStats,
                args: [tickerKey, currentStats],
            }));
            // Count stats need to be initialized manually
            if (type === types_1.TransferRestrictionType.Count) {
                const { Ticker: rawTicker } = tickerKey;
                const holders = yield query.asset.balanceOf.entries(rawTicker);
                const holderCount = new bignumber_js_1.default(holders.length);
                const secondKey = (0, conversion_1.createStat2ndKey)(context);
                const stat = (0, conversion_1.statUpdate)(secondKey, (0, conversion_1.bigNumberToU128)(holderCount, context), context);
                const statValue = (0, conversion_1.statUpdatesToBtreeStatUpdate)([stat], context);
                transactions.push((0, internal_3.checkTxType)({
                    transaction: statistics.batchUpdateAssetStats,
                    args: [tickerKey, newStat, statValue],
                }));
            }
        }
        transactions.push((0, internal_3.checkTxType)({
            transaction: statistics.setAssetTransferCompliance,
            args: [tickerKey, (0, conversion_1.complianceConditionsToBtreeSet)(conditions, context)],
        }));
        if (exemptions.length) {
            const exemptedIds = yield (0, internal_3.getExemptedIds)(exemptions, context, ticker);
            const exemptedScopeIds = exemptedIds.map(entityId => (0, conversion_1.stringToIdentityId)(entityId, context));
            const btreeIds = (0, conversion_1.scopeIdsToBtreeSetIdentityId)(exemptedScopeIds, context);
            const exemptKey = (0, conversion_1.toExemptKey)(tickerKey, op);
            transactions.push((0, internal_3.checkTxType)({
                transaction: statistics.setEntitiesExempt,
                feeMultiplier: new bignumber_js_1.default(exemptions.length),
                args: [true, exemptKey, btreeIds],
            }));
        }
        this.addBatchTransaction({ transactions });
        return finalCount;
    });
}
exports.prepareSetTransferRestrictions = prepareSetTransferRestrictions;
/**
 * @hidden
 */
function getAuthorization({ ticker, restrictions }) {
    const { needStat } = this.storage;
    const transactions = [types_1.TxTags.statistics.SetAssetTransferCompliance];
    const needExemptionsPermission = restrictions.some(r => { var _a; return (_a = r.exemptedIdentities) === null || _a === void 0 ? void 0 : _a.length; });
    if (needExemptionsPermission) {
        transactions.push(types_1.TxTags.statistics.SetEntitiesExempt);
    }
    if (needStat) {
        transactions.push(types_1.TxTags.statistics.SetActiveAssetStats);
    }
    return {
        permissions: {
            assets: [new internal_1.Asset({ ticker }, this.context)],
            transactions,
            portfolios: [],
        },
    };
}
exports.getAuthorization = getAuthorization;
/**
 * @hidden
 */
function prepareStorage(args) {
    return __awaiter(this, void 0, void 0, function* () {
        const { context, context: { polymeshApi: { query: { statistics }, }, }, } = this;
        const { ticker, type } = args;
        const tickerKey = (0, conversion_1.stringToTickerKey)(ticker, context);
        const currentStats = yield statistics.activeAssetStats(tickerKey);
        const needStat = !currentStats.find(s => {
            const stat = (0, conversion_1.meshStatToStatisticsOpType)(s);
            const cmpStat = stat === internal_2.StatisticsOpType.Balance
                ? types_1.TransferRestrictionType.Percentage
                : types_1.TransferRestrictionType.Count;
            return cmpStat === type;
        });
        const { transferRestrictions: { count, percentage }, } = new internal_1.Asset({ ticker }, context);
        const [{ restrictions: currentCountRestrictions }, { restrictions: currentPercentageRestrictions },] = yield Promise.all([count.get(), percentage.get()]);
        const currentRestrictions = [];
        // take the count of the type of restrictions not being changed
        const occupiedSlots = type === types_1.TransferRestrictionType.Percentage
            ? currentCountRestrictions.length
            : currentPercentageRestrictions.length;
        if (type === types_1.TransferRestrictionType.Count) {
            currentCountRestrictions.forEach(({ count: value }) => {
                const restriction = { type: types_1.TransferRestrictionType.Count, value };
                currentRestrictions.push(restriction);
            });
        }
        else {
            currentPercentageRestrictions.forEach(({ percentage: value }) => {
                const restriction = { type: types_1.TransferRestrictionType.Percentage, value };
                currentRestrictions.push(restriction);
            });
        }
        const transformRestriction = (restriction) => (0, conversion_1.transferRestrictionToPolymeshTransferCondition)(restriction, context);
        return {
            occupiedSlots: new bignumber_js_1.default(occupiedSlots),
            currentRestrictions: currentRestrictions.map(transformRestriction),
            needStat,
            currentStats,
        };
    });
}
exports.prepareStorage = prepareStorage;
/**
 * @hidden
 */
const setTransferRestrictions = () => new internal_1.Procedure(prepareSetTransferRestrictions, getAuthorization, prepareStorage);
exports.setTransferRestrictions = setTransferRestrictions;
//# sourceMappingURL=setTransferRestrictions.js.map