"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.computeReservesAllocation = computeReservesAllocation;
const decimal_js_1 = __importDefault(require("decimal.js"));
const farms_sdk_1 = require("@kamino-finance/farms-sdk");
const ZERO = new decimal_js_1.default(0);
/**
 * Computes the allocation of vault funds across reserves based on weights and caps.
 * @param vaultAUMInvestableInReserves - Total AUM of the vault, in tokens, that can be invested in reserves.
 * @param vaultUnallocatedWeight - Weight for unallocated funds
 * @param vaultUnallocatedCap - Maximum amount that can remain unallocated
 * @param initialVaultAllocations - Map of reserve addresses to their allocation configurations
 * @param vaultTokenDecimals - The number of decimals of the vault token, needed to compute the min amount
 * @returns Object containing target unallocated amount and target allocations per reserve, in tokens
 */
function computeReservesAllocation(vaultAUMInvestableInReserves, vaultUnallocatedWeight, vaultUnallocatedCap, initialVaultAllocations, vaultTokenDecimals) {
    let totalAllocation = new decimal_js_1.default(0);
    const reserves = Array.from(initialVaultAllocations.keys());
    const expectedHoldingsDistribution = new Map();
    reserves.forEach((reserve) => {
        expectedHoldingsDistribution.set(reserve, ZERO);
        totalAllocation = totalAllocation.add(initialVaultAllocations.get(reserve).targetWeight);
    });
    let totalLeftToInvest = vaultAUMInvestableInReserves;
    const totalAllocationsIncludingUnallocated = totalAllocation.add(vaultUnallocatedWeight);
    // Calculate initial unallocated amount
    let unallocatedAllocation = totalLeftToInvest.mul(vaultUnallocatedWeight).div(totalAllocationsIncludingUnallocated);
    if (unallocatedAllocation.gt(vaultUnallocatedCap)) {
        unallocatedAllocation = vaultUnallocatedCap;
    }
    totalLeftToInvest = totalLeftToInvest.sub(unallocatedAllocation);
    let currentAllocationSum = totalAllocation;
    const reservesCount = reserves.length;
    const maxRemainedUninvestedLamports = (0, farms_sdk_1.lamportsToCollDecimal)(new decimal_js_1.default(reservesCount), vaultTokenDecimals); // invest only if the AUM has more lamports than the number of reserves
    // Iteratively allocate funds to reserves based on weights and caps
    while (totalLeftToInvest.gt(maxRemainedUninvestedLamports) && currentAllocationSum.gt(ZERO)) {
        const totalLeftover = totalLeftToInvest;
        for (const reserve of reserves) {
            const reserveWithWeight = initialVaultAllocations.get(reserve);
            if (!reserveWithWeight)
                continue;
            const targetAllocation = reserveWithWeight.targetWeight.mul(totalLeftover).div(currentAllocationSum);
            const reserveCap = reserveWithWeight.tokenAllocationCap;
            let amountToInvest = decimal_js_1.default.min(targetAllocation, totalLeftToInvest);
            // Handle reserve caps
            if (reserveCap.gt(ZERO)) {
                const currentReserveAllocation = expectedHoldingsDistribution.get(reserve);
                const remainingCapacity = reserveCap.sub(currentReserveAllocation);
                amountToInvest = decimal_js_1.default.min(amountToInvest, remainingCapacity);
            }
            else if (reserveCap.eq(ZERO)) {
                // Zero cap means no investment allowed
                amountToInvest = ZERO;
            }
            totalLeftToInvest = totalLeftToInvest.sub(amountToInvest);
            // Check if reserve is now capped and should be removed from future allocations
            const newReserveAllocation = expectedHoldingsDistribution.get(reserve).add(amountToInvest);
            if (reserveCap.eq(ZERO) || (reserveCap.gt(ZERO) && newReserveAllocation.gte(reserveCap))) {
                currentAllocationSum = currentAllocationSum.sub(reserveWithWeight.targetWeight);
            }
            // Update reserve allocation
            expectedHoldingsDistribution.set(reserve, newReserveAllocation);
        }
    }
    return {
        targetUnallocatedAmount: unallocatedAllocation.add(totalLeftToInvest),
        targetReservesAllocation: expectedHoldingsDistribution,
    };
}
//# sourceMappingURL=vaultAllocation.js.map