"use strict";
// Copyright 2017-2020 @polkadot/api-derive authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const bn_js_1 = __importDefault(require("bn.js"));
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const types_1 = require("../../types");
const util_1 = require("@polkadot/util");
const util_2 = require("../util");
// groups the supplied chunks by era, i.e. { [era]: BN(total of values) }
function groupByEra(list) {
    return list.reduce((map, { era, value }) => {
        const key = era.toString();
        map[key] = !map[key]
            ? value.unwrap()
            : map[key].add(value.unwrap());
        return map;
    }, {});
}
// calculate the remaining blocks in a specific unlock era
function remainingBlocks(api, era, sessionInfo) {
    const remaining = era.sub(sessionInfo.currentEra);
    // on the Rust side the current-era >= era-for-unlock (removal done on >)
    return types_1.createType(api.registry, 'BlockNumber', remaining.gtn(0)
        ? remaining
            .subn(1)
            .mul(sessionInfo.eraLength)
            .add(sessionInfo.eraLength.sub(sessionInfo.eraProgress))
        : 0);
}
function calculateUnlocking(api, stakingLedger, sessionInfo) {
    if (util_1.isUndefined(stakingLedger)) {
        return undefined;
    }
    const unlockingChunks = stakingLedger.unlocking.filter(({ era }) => remainingBlocks(api, era.unwrap(), sessionInfo).gtn(0));
    if (!unlockingChunks.length) {
        return undefined;
    }
    // group the unlock chunks that have the same era and sum their values
    const groupedResult = groupByEra(unlockingChunks);
    const results = Object.entries(groupedResult).map(([eraString, value]) => ({
        value: types_1.createType(api.registry, 'Balance', value),
        remainingBlocks: remainingBlocks(api, new bn_js_1.default(eraString), sessionInfo)
    }));
    return results.length ? results : undefined;
}
function redeemableSum(api, stakingLedger, sessionInfo) {
    if (util_1.isUndefined(stakingLedger)) {
        return types_1.createType(api.registry, 'Balance');
    }
    return types_1.createType(api.registry, 'Balance', stakingLedger.unlocking.reduce((total, { era, value }) => {
        return remainingBlocks(api, era.unwrap(), sessionInfo).eqn(0)
            ? total.add(value.unwrap())
            : total;
    }, new bn_js_1.default(0)));
}
function parseResult(api, sessionInfo, query) {
    return Object.assign(Object.assign({}, query), { redeemable: redeemableSum(api, query.stakingLedger, sessionInfo), unlocking: calculateUnlocking(api, query.stakingLedger, sessionInfo) });
}
/**
 * @description From a stash, retrieve the controllerId and fill in all the relevant staking details
 */
function account(api) {
    return util_2.memo((accountId) => rxjs_1.combineLatest([
        api.derive.session.info(),
        api.derive.staking.query(accountId)
    ]).pipe(operators_1.map(([sessionInfo, query]) => parseResult(api, sessionInfo, query))));
}
exports.account = account;
