import network from "@ledgerhq/live-network/network";
import { BigNumber } from "bignumber.js";
import { getAccountCurrency } from "../../account";
import { formatCurrencyUnit } from "../../currencies";
import { SwapExchangeRateAmountTooHigh, SwapExchangeRateAmountTooLow, SwapExchangeRateAmountTooLowOrTooHigh, } from "../../errors";
import { getSwapAPIBaseURL, getSwapAPIError, getSwapUserIP } from "./";
import { mockGetExchangeRates } from "./mock";
import { isIntegrationTestEnv } from "./utils/isIntegrationTestEnv";
import { getSwapProvider } from "../providers/swap";
const getExchangeRates = async ({ exchange, transaction, currencyTo, providers = [], timeout, timeoutErrorMessage, }) => {
    if (isIntegrationTestEnv())
        return mockGetExchangeRates(exchange, transaction, currencyTo);
    const from = getAccountCurrency(exchange.fromAccount).id;
    const unitFrom = getAccountCurrency(exchange.fromAccount).units[0];
    const to = (currencyTo ?? getAccountCurrency(exchange.toAccount)).id;
    const unitTo = (currencyTo && currencyTo.units[0]) ?? getAccountCurrency(exchange.toAccount).units[0];
    const amountFrom = transaction.amount;
    const tenPowMagnitude = new BigNumber(10).pow(unitFrom.magnitude);
    const apiAmount = new BigNumber(amountFrom).div(tenPowMagnitude);
    const providerList = providers
        .filter(provider => provider.pairs.some(pair => pair.from === from && pair.to === to))
        .map(item => item.provider);
    const request = {
        from,
        to,
        amountFrom: apiAmount.toString(),
        providers: providerList,
    };
    const headers = getSwapUserIP();
    const res = await network({
        method: "POST",
        url: `${getSwapAPIBaseURL()}/rate`,
        ...(timeout ? { timeout } : {}),
        ...(timeoutErrorMessage ? { timeoutErrorMessage } : {}),
        data: request,
        ...(headers !== undefined ? headers : {}),
    });
    const rates = res.data.map(async (responseData) => {
        const { rate: maybeRate, payoutNetworkFees: maybePayoutNetworkFees, rateId, provider, providerType, amountFrom, amountTo, tradeMethod, providerURL, expirationTime, } = responseData;
        const error = await inferError(apiAmount, unitFrom, responseData);
        if (error) {
            return {
                provider,
                tradeMethod,
                error,
            };
        }
        const payoutNetworkFees = new BigNumber(maybePayoutNetworkFees || 0);
        const magnitudeAwarePayoutNetworkFees = payoutNetworkFees.times(new BigNumber(10).pow(unitTo.magnitude));
        const rate = maybeRate
            ? new BigNumber(maybeRate)
            : new BigNumber(amountTo).minus(payoutNetworkFees).div(amountFrom);
        // NB Allows us to simply multiply satoshi values from/to
        const magnitudeAwareRate = rate.div(new BigNumber(10).pow(unitFrom.magnitude - unitTo.magnitude));
        const toAmount = new BigNumber(amountTo).minus(payoutNetworkFees);
        const magnitudeAwareToAmount = toAmount.times(new BigNumber(10).pow(unitTo.magnitude));
        // Nb no longer need to break it down on UI
        const out = {
            magnitudeAwareRate,
            provider,
            providerType,
            rate,
            rateId,
            expirationTime,
            toAmount: magnitudeAwareToAmount,
            tradeMethod,
            providerURL,
        };
        if (tradeMethod === "fixed") {
            return {
                ...out,
                payoutNetworkFees: magnitudeAwarePayoutNetworkFees,
                rateId,
            };
        }
        else {
            return {
                ...out,
                payoutNetworkFees: magnitudeAwarePayoutNetworkFees,
            };
        }
    });
    return rates;
};
const inferError = async (apiAmount, unitFrom, responseData) => {
    const tenPowMagnitude = new BigNumber(10).pow(unitFrom.magnitude);
    const { amountTo, minAmountFrom, maxAmountFrom, errorCode, errorMessage, provider, status } = responseData;
    const isDex = (await getSwapProvider(provider)).type === "DEX";
    // DEX quotes are out of limits error. We do not know if it is a low or high limit, neither the amount.
    if ((!minAmountFrom || !maxAmountFrom) && status === "error" && errorCode !== 300 && isDex) {
        return new SwapExchangeRateAmountTooLowOrTooHigh(undefined, {
            message: "",
        });
    }
    if (!amountTo) {
        // We are in an error case regardless of api version.
        if (errorCode) {
            return getSwapAPIError(errorCode, errorMessage);
        }
        // For out of range errors we will have a min/max pairing
        const hasAmountLimit = minAmountFrom || maxAmountFrom;
        if (hasAmountLimit) {
            const isTooSmall = minAmountFrom ? new BigNumber(apiAmount).lte(minAmountFrom) : false;
            const MinOrMaxError = isTooSmall
                ? SwapExchangeRateAmountTooLow
                : SwapExchangeRateAmountTooHigh;
            const key = isTooSmall ? "minAmountFromFormatted" : "maxAmountFromFormatted";
            const amount = isTooSmall ? minAmountFrom : maxAmountFrom;
            return new MinOrMaxError(undefined, {
                [key]: formatCurrencyUnit(unitFrom, new BigNumber(amount).times(tenPowMagnitude), {
                    alwaysShowSign: false,
                    disableRounding: true,
                    showCode: true,
                }),
                amount: new BigNumber(amount),
            });
        }
    }
    return;
};
export default getExchangeRates;
//# sourceMappingURL=getExchangeRates.js.map