"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const network_1 = __importDefault(require("@ledgerhq/live-network/network"));
const bignumber_js_1 = require("bignumber.js");
const account_1 = require("../../account");
const currencies_1 = require("../../currencies");
const errors_1 = require("../../errors");
const _1 = require("./");
const mock_1 = require("./mock");
const isIntegrationTestEnv_1 = require("./utils/isIntegrationTestEnv");
const swap_1 = require("../providers/swap");
const getExchangeRates = async ({ exchange, transaction, currencyTo, providers = [], timeout, timeoutErrorMessage, }) => {
    if ((0, isIntegrationTestEnv_1.isIntegrationTestEnv)())
        return (0, mock_1.mockGetExchangeRates)(exchange, transaction, currencyTo);
    const from = (0, account_1.getAccountCurrency)(exchange.fromAccount).id;
    const unitFrom = (0, account_1.getAccountCurrency)(exchange.fromAccount).units[0];
    const to = (currencyTo ?? (0, account_1.getAccountCurrency)(exchange.toAccount)).id;
    const unitTo = (currencyTo && currencyTo.units[0]) ?? (0, account_1.getAccountCurrency)(exchange.toAccount).units[0];
    const amountFrom = transaction.amount;
    const tenPowMagnitude = new bignumber_js_1.BigNumber(10).pow(unitFrom.magnitude);
    const apiAmount = new bignumber_js_1.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 = (0, _1.getSwapUserIP)();
    const res = await (0, network_1.default)({
        method: "POST",
        url: `${(0, _1.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_js_1.BigNumber(maybePayoutNetworkFees || 0);
        const magnitudeAwarePayoutNetworkFees = payoutNetworkFees.times(new bignumber_js_1.BigNumber(10).pow(unitTo.magnitude));
        const rate = maybeRate
            ? new bignumber_js_1.BigNumber(maybeRate)
            : new bignumber_js_1.BigNumber(amountTo).minus(payoutNetworkFees).div(amountFrom);
        // NB Allows us to simply multiply satoshi values from/to
        const magnitudeAwareRate = rate.div(new bignumber_js_1.BigNumber(10).pow(unitFrom.magnitude - unitTo.magnitude));
        const toAmount = new bignumber_js_1.BigNumber(amountTo).minus(payoutNetworkFees);
        const magnitudeAwareToAmount = toAmount.times(new bignumber_js_1.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_js_1.BigNumber(10).pow(unitFrom.magnitude);
    const { amountTo, minAmountFrom, maxAmountFrom, errorCode, errorMessage, provider, status } = responseData;
    const isDex = (await (0, swap_1.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 errors_1.SwapExchangeRateAmountTooLowOrTooHigh(undefined, {
            message: "",
        });
    }
    if (!amountTo) {
        // We are in an error case regardless of api version.
        if (errorCode) {
            return (0, _1.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_js_1.BigNumber(apiAmount).lte(minAmountFrom) : false;
            const MinOrMaxError = isTooSmall
                ? errors_1.SwapExchangeRateAmountTooLow
                : errors_1.SwapExchangeRateAmountTooHigh;
            const key = isTooSmall ? "minAmountFromFormatted" : "maxAmountFromFormatted";
            const amount = isTooSmall ? minAmountFrom : maxAmountFrom;
            return new MinOrMaxError(undefined, {
                [key]: (0, currencies_1.formatCurrencyUnit)(unitFrom, new bignumber_js_1.BigNumber(amount).times(tenPowMagnitude), {
                    alwaysShowSign: false,
                    disableRounding: true,
                    showCode: true,
                }),
                amount: new bignumber_js_1.BigNumber(amount),
            });
        }
    }
    return;
};
exports.default = getExchangeRates;
//# sourceMappingURL=getExchangeRates.js.map