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());
    });
};
import { BigNumber, ethers } from "ethers";
function getUserTotals(assets) {
    let totalBorrow = BigNumber.from(0);
    let totalCollateral = BigNumber.from(0);
    for (const a of assets) {
        totalBorrow = totalBorrow.add(a.borrowBalance.mul(a.underlyingPrice).div(ethers.utils.parseEther("1")));
        if (a.membership) {
            totalCollateral = totalCollateral.add(a.supplyBalance
                .mul(a.underlyingPrice)
                .div(ethers.utils.parseEther("1"))
                .mul(a.collateralFactor)
                .div(ethers.utils.parseEther("1")));
        }
    }
    return { totalBorrow, totalCollateral };
}
function getPositionHealth(totalBorrow, totalCollateral) {
    return totalBorrow.gt(BigNumber.from(0))
        ? totalCollateral.mul(ethers.utils.parseEther("1")).div(totalBorrow)
        : BigNumber.from(10).pow(36);
}
function getFusePoolUsers(sdk, comptroller, maxHealth) {
    return __awaiter(this, void 0, void 0, function* () {
        const poolUsers = [];
        const comptrollerInstance = sdk.createComptroller(comptroller);
        const users = yield comptrollerInstance.callStatic.getAllBorrowers();
        for (const user of users) {
            const assets = yield sdk.contracts.PoolLens.callStatic.getPoolAssetsWithData(comptrollerInstance.address, {
                from: user
            });
            const { totalBorrow, totalCollateral } = getUserTotals(assets);
            const health = getPositionHealth(totalBorrow, totalCollateral);
            if (maxHealth.gt(health)) {
                poolUsers.push({ account: user, totalBorrow, totalCollateral, health });
            }
        }
        return {
            comptroller,
            users: poolUsers,
            closeFactor: yield comptrollerInstance.callStatic.closeFactorMantissa(),
            liquidationIncentive: yield comptrollerInstance.callStatic.liquidationIncentiveMantissa()
        };
    });
}
function getPoolsWithShortfall(sdk, comptroller) {
    return __awaiter(this, void 0, void 0, function* () {
        const comptrollerInstance = sdk.createComptroller(comptroller);
        const users = yield comptrollerInstance.callStatic.getAllBorrowers();
        const promises = users.map((user) => {
            return comptrollerInstance.callStatic.getAccountLiquidity(user);
        });
        const allResults = yield Promise.all(promises.map((p) => p.catch((e) => e)));
        const validResults = allResults.filter((r) => !(r instanceof Error));
        const erroredResults = allResults.filter((r) => r instanceof Error);
        if (erroredResults.length > 0) {
            sdk.logger.error("Errored results", { erroredResults });
        }
        const results = validResults.map((r, i) => {
            return { user: users[i], liquidity: r[1], shortfall: r[2] };
        });
        const minimumTransactionCost = yield sdk.provider.getGasPrice().then((g) => g.mul(BigNumber.from(500000)));
        return results.filter((user) => user.shortfall.gt(minimumTransactionCost));
    });
}
export default function getAllFusePoolUsers(sdk, maxHealth, excludedComptrollers) {
    return __awaiter(this, void 0, void 0, function* () {
        const [, allPools] = yield sdk.contracts.PoolDirectory.callStatic.getActivePools();
        const fusePoolUsers = [];
        const erroredPools = [];
        for (const pool of allPools) {
            const { comptroller, name } = pool;
            if (!excludedComptrollers.includes(comptroller)) {
                try {
                    const hasShortfall = yield getPoolsWithShortfall(sdk, comptroller);
                    if (hasShortfall.length > 0) {
                        const users = hasShortfall.map((user) => {
                            return `- user: ${user.user}, shortfall: ${ethers.utils.formatEther(user.shortfall)}\n`;
                        });
                        sdk.logger.info(`Pool ${name} (${comptroller}) has ${hasShortfall.length} users with shortfall: \n${users}`);
                        try {
                            const poolUserParams = yield getFusePoolUsers(sdk, comptroller, maxHealth);
                            fusePoolUsers.push(poolUserParams);
                        }
                        catch (e) {
                            const msg = `Error getting pool users for ${comptroller}` + e;
                            erroredPools.push({ comptroller, msg, error: e });
                        }
                    }
                    else {
                        sdk.logger.info(`Pool ${name} (${comptroller}) has no users with shortfall`);
                    }
                }
                catch (e) {
                    const msg = `Error getting shortfalled users for pool ${name} (${comptroller})` + e;
                    erroredPools.push({ comptroller, msg, error: e });
                }
            }
        }
        return [fusePoolUsers, erroredPools];
    });
}
