import { getAlpacaApi } from "./alpaca";
import { transactionToIntent } from "./utils";
import BigNumber from "bignumber.js";
import { decodeTokenAccountId } from "@ledgerhq/coin-framework/account/index";
function bnEq(a, b) {
    return !a && !b ? true : !a || !b ? false : a.eq(b);
}
function assetInfosFallback(transaction) {
    return {
        assetReference: transaction.assetReference ?? "",
        assetOwner: transaction.assetOwner ?? "",
    };
}
function propagateField(estimation, field, dest) {
    const value = estimation?.parameters?.[field];
    if (typeof value !== "bigint" && typeof value !== "number" && typeof value !== "string")
        return;
    switch (field) {
        case "type":
            dest[field] = Number(value.toString());
            return;
        case "storageLimit":
        case "gasLimit":
        case "gasPrice":
        case "maxFeePerGas":
        case "maxPriorityFeePerGas":
        case "additionalFees":
            dest[field] = new BigNumber(value.toString());
            return;
        default:
            return;
    }
}
export function genericPrepareTransaction(network, kind) {
    return async (account, transaction) => {
        const { getAssetFromToken, computeIntentType, estimateFees, validateIntent } = getAlpacaApi(account.currency.id, kind);
        const { assetReference, assetOwner } = getAssetFromToken
            ? await getAssetInfos(transaction, account.freshAddress, getAssetFromToken)
            : assetInfosFallback(transaction);
        const customParametersFees = transaction.customFees?.parameters?.fees;
        /**
         * Ticking `useAllAmount` constantly resets the amount to 0. This is problematic
         * because some Blockchain need the actual transaction amount to compute the fees
         * (Example with EVM and ERC20 transactions)
         * In case of `useAllAmount` and token transaction, we read the token account spendable
         * balance instead.
         */
        let amount = transaction.amount;
        if (transaction.useAllAmount && transaction.subAccountId) {
            const subAccount = account.subAccounts?.find(acc => acc.id === transaction.subAccountId);
            amount = subAccount?.spendableBalance ?? amount;
        }
        // Pass any parameters that help estimating fees
        // This includes `assetOwner` and `assetReference` that are not used by some apps that only rely on `subAccountId`
        // TODO Remove `assetOwner` and `assetReference` in order to maintain one unique way of identifying the type of asset
        // https://ledgerhq.atlassian.net/browse/LIVE-24044
        const intent = transactionToIntent(account, {
            ...transaction,
            assetOwner,
            assetReference,
            amount,
        }, computeIntentType);
        const estimation = customParametersFees
            ? { value: BigInt(customParametersFees.toFixed()) }
            : await estimateFees(intent, {
                gasPrice: transaction.gasPrice,
                maxFeePerGas: transaction.maxFeePerGas,
                maxPriorityFeePerGas: transaction.maxPriorityFeePerGas,
                gasOptions: transaction.gasOptions,
            });
        const fees = new BigNumber(estimation.value.toString());
        if (!bnEq(transaction.fees, fees)) {
            const next = {
                ...transaction,
                fees,
                assetReference,
                assetOwner,
                customFees: {
                    parameters: {
                        fees: customParametersFees ? new BigNumber(customParametersFees.toString()) : undefined,
                    },
                },
            };
            // Propagate needed fields
            const fieldsToPropagate = [
                "type",
                "storageLimit",
                "gasLimit",
                "gasPrice",
                "maxFeePerGas",
                "maxPriorityFeePerGas",
                "additionalFees",
            ];
            for (const field of fieldsToPropagate) {
                propagateField(estimation, field, next);
            }
            // align with stellar/xrp: when send max (or staking intents), reflect validated amount in UI
            if (transaction.useAllAmount || ["stake", "unstake"].includes(transaction.mode ?? "")) {
                // TODO Remove the call to `validateIntent` https://ledgerhq.atlassian.net/browse/LIVE-22228
                const { amount } = await validateIntent(transactionToIntent(account, {
                    ...transaction,
                    assetOwner,
                    assetReference,
                }, computeIntentType));
                next.amount = new BigNumber(amount.toString());
            }
            return next;
        }
        return transaction;
    };
}
export async function getAssetInfos(tr, owner, getAssetFromToken) {
    if (tr.subAccountId) {
        const { token } = await decodeTokenAccountId(tr.subAccountId);
        if (!token)
            return assetInfosFallback(tr);
        const asset = getAssetFromToken(token, owner);
        return {
            assetOwner: ("assetOwner" in asset && asset.assetOwner) || "",
            assetReference: ("assetReference" in asset && asset.assetReference) || "",
        };
    }
    return assetInfosFallback(tr);
}
//# sourceMappingURL=prepareTransaction.js.map