import { PlatformContext, Wormhole, chainToPlatform, decimals, isNative, nativeChainIds, networkPlatformConfigs, } from "@wormhole-foundation/connect-sdk";
import { Algodv2, decodeSignedTransaction, modelsv2, waitForConfirmation, } from "algosdk";
import { AlgorandAddress, AlgorandZeroAddress } from "./address";
import { AlgorandChain } from "./chain";
import { _platform } from "./types";
/**
 * @category Algorand
 */
export class AlgorandPlatform extends PlatformContext {
    static _platform = _platform;
    constructor(network, _config) {
        super(network, _config ?? networkPlatformConfigs(network, AlgorandPlatform._platform));
    }
    getRpc(chain) {
        if (chain in this.config)
            return new Algodv2("", this.config[chain].rpc);
        throw new Error("No configuration available for chain: " + chain);
    }
    getChain(chain) {
        if (chain in this.config)
            return new AlgorandChain(chain, this);
        throw new Error("No configuration available for chain: " + chain);
    }
    static nativeTokenId(network, chain) {
        if (!AlgorandPlatform.isSupportedChain(chain))
            throw new Error(`invalid chain for Algorand: ${chain}`);
        return Wormhole.chainAddress(chain, AlgorandZeroAddress);
    }
    static isNativeTokenId(network, chain, tokenId) {
        if (!AlgorandPlatform.isSupportedChain(chain))
            return false;
        if (tokenId.chain !== chain)
            return false;
        const native = this.nativeTokenId(network, chain);
        return native == tokenId;
    }
    static isSupportedChain(chain) {
        const platform = chainToPlatform(chain);
        return platform === AlgorandPlatform._platform;
    }
    static async getDecimals(chain, rpc, token) {
        // It may come in as a universal address
        const assetId = isNative(token) ? 0 : new AlgorandAddress(token).toInt();
        if (assetId === 0)
            return decimals.nativeDecimals(AlgorandPlatform._platform);
        const assetResp = await rpc.getAssetByID(assetId).do();
        const asset = modelsv2.Asset.from_obj_for_encoding(assetResp);
        if (!asset.params || !asset.params.decimals)
            throw new Error("Could not fetch token details");
        return Number(asset.params.decimals);
    }
    static async getBalance(chain, rpc, walletAddr, token) {
        const assetId = isNative(token) ? 0 : new AlgorandAddress(token).toInt();
        if (assetId === 0) {
            const resp = await rpc.accountInformation(walletAddr).do();
            const accountInfo = modelsv2.Account.from_obj_for_encoding(resp);
            return BigInt(accountInfo.amount);
        }
        const acctAssetInfoResp = await rpc.accountAssetInformation(walletAddr, assetId).do();
        const accountAssetInfo = modelsv2.AssetHolding.from_obj_for_encoding(acctAssetInfoResp);
        return BigInt(accountAssetInfo.amount);
    }
    static async getBalances(chain, rpc, walletAddr, tokens) {
        let native;
        if (tokens.includes("native")) {
            const acctInfoResp = await rpc.accountInformation(walletAddr).do();
            const accountInfo = modelsv2.Account.from_obj_for_encoding(acctInfoResp);
            native = BigInt(accountInfo.amount);
        }
        const balancesArr = tokens.map(async (token) => {
            if (isNative(token)) {
                return { ["native"]: native };
            }
            const asaId = new AlgorandAddress(token).toInt();
            const acctAssetInfoResp = await rpc.accountAssetInformation(walletAddr, asaId).do();
            const accountAssetInfo = modelsv2.AssetHolding.from_obj_for_encoding(acctAssetInfoResp);
            return BigInt(accountAssetInfo.amount);
        });
        return balancesArr.reduce((obj, item) => Object.assign(obj, item), {});
    }
    static async sendWait(chain, rpc, stxns) {
        const rounds = 4;
        const decodedStxns = stxns.map((val, idx) => {
            const decodedStxn = decodeSignedTransaction(val);
            return decodedStxn;
        });
        const txIds = decodedStxns.map((val, idx) => {
            const id = val.txn.txID();
            return id;
        });
        const { txId } = await rpc.sendRawTransaction(stxns).do();
        if (!txId) {
            throw new Error("Transaction(s) failed to send");
        }
        const confirmResp = await waitForConfirmation(rpc, txId, rounds);
        const ptr = modelsv2.PendingTransactionResponse.from_obj_for_encoding(confirmResp);
        if (!ptr.confirmedRound) {
            throw new Error(`Transaction(s) could not be confirmed in ${rounds} rounds`);
        }
        return txIds;
    }
    static async getLatestBlock(rpc) {
        const statusResp = await rpc.status().do();
        const status = modelsv2.NodeStatusResponse.from_obj_for_encoding(statusResp);
        if (!status.lastRound) {
            throw new Error("Error getting status from node");
        }
        return Number(status.lastRound);
    }
    static async getLatestFinalizedBlock(rpc) {
        const statusResp = await rpc.status().do();
        const status = modelsv2.NodeStatusResponse.from_obj_for_encoding(statusResp);
        if (!status.lastRound) {
            throw new Error("Error getting status from node");
        }
        return Number(status.lastRound);
    }
    static chainFromChainId(genesisId) {
        const networkChainPair = nativeChainIds.platformNativeChainIdToNetworkChain(AlgorandPlatform._platform, genesisId);
        if (networkChainPair === undefined)
            throw new Error(`Unknown native chain id ${genesisId}`);
        const [network, chain] = networkChainPair;
        return [network, chain];
    }
    static async chainFromRpc(rpc) {
        const versionResp = await rpc.versionsCheck().do();
        const version = modelsv2.Version.from_obj_for_encoding(versionResp);
        return this.chainFromChainId(version.genesisId);
    }
}
//# sourceMappingURL=platform.js.map