"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ForexPriceEndpoint = exports.CryptoPriceEndpoint = exports.PriceAdapter = exports.PriceEndpoint = exports.priceEndpointInputParametersDefinition = void 0;
const endpoint_1 = require("./endpoint");
const index_1 = require("./index");
/**
 * Base input parameter config that any [[PriceEndpoint]] must extend
 */
exports.priceEndpointInputParametersDefinition = {
    base: {
        aliases: ['from', 'coin'],
        type: 'string',
        description: 'The symbol of symbols of the currency to query',
        required: true,
    },
    quote: {
        aliases: ['to', 'market'],
        type: 'string',
        description: 'The symbol of the currency to convert to',
        required: true,
    },
};
/**
 * A PriceEndpoint is a specific type of AdapterEndpoint. Meant to comply with standard practices for
 * Data Feeds, its InputParameters must extend the basic ones (base/quote).
 */
class PriceEndpoint extends endpoint_1.AdapterEndpoint {
}
exports.PriceEndpoint = PriceEndpoint;
const buildIncludesMap = (includesFile) => {
    const includesMap = {};
    for (const { from, to, includes } of includesFile) {
        if (!includesMap[from]) {
            includesMap[from] = {};
        }
        includesMap[from][to] = includes[0];
    }
    return includesMap;
};
/**
 * A PriceAdapter is a specific kind of Adapter that includes at least one PriceEnpoint.
 */
class PriceAdapter extends index_1.Adapter {
    constructor(params) {
        const priceEndpoints = params.endpoints.filter((e) => e instanceof PriceEndpoint);
        if (!priceEndpoints.length) {
            throw new Error(`This PriceAdapter's list of endpoints does not contain a valid PriceEndpoint`);
        }
        super(params);
        if (params.includes) {
            // Build includes map for constant lookups
            this.includesMap = buildIncludesMap(params.includes);
            const requestTransform = (req) => {
                const priceRequest = req;
                const requestData = priceRequest.requestContext.data;
                if (!requestData.base || !requestData.quote) {
                    return;
                }
                const includesDetails = this.includesMap?.[requestData.base]?.[requestData.quote];
                if (includesDetails?.endpoints === undefined ||
                    includesDetails?.endpoints?.includes(req.requestContext.endpointName)) {
                    if (includesDetails) {
                        requestData.base = includesDetails.from || requestData.base;
                        requestData.quote = includesDetails.to || requestData.quote;
                    }
                    const inverse = includesDetails?.inverse || false;
                    priceRequest.requestContext.priceMeta = {
                        inverse,
                    };
                }
                else {
                    throw new Error(`Endpoint ${req.requestContext.endpointName} not supported for ${requestData.base}/${requestData.quote} in includes.json`);
                }
            };
            for (const endpoint of priceEndpoints) {
                endpoint.requestTransforms?.push(requestTransform);
            }
        }
    }
    async handleRequest(req, replySent) {
        const response = await super.handleRequest(req, replySent);
        if (this.includesMap && req.requestContext.priceMeta?.inverse) {
            // We need to search in the reverse order (quote -> base) because the request transform will have inverted the pair
            // Deep clone the response, as it may contain objects which won't be cloned by simply destructuring
            const cloneResponse = JSON.parse(JSON.stringify(response));
            const inverseResult = 1 / cloneResponse.result;
            cloneResponse.result = inverseResult;
            // Check if response data has a result within it
            const data = cloneResponse.data;
            if (data?.result) {
                data.result = inverseResult;
            }
            return cloneResponse;
        }
        return response;
    }
}
exports.PriceAdapter = PriceAdapter;
const DEFAULT_ALIASES = ['crypto', 'price'];
/**
 * A CryptoPriceEndpoint expands on the existing [[PriceEndpoint]], with the addition of adding
 * a set of common aliases that are used across EAs to specify endpoints that provide crypto prices.
 */
class CryptoPriceEndpoint extends PriceEndpoint {
    constructor(params) {
        if (!params.aliases) {
            params.aliases = [];
        }
        for (const alias of DEFAULT_ALIASES) {
            if (params.name !== alias && !params.aliases.includes(alias)) {
                params.aliases.push(alias);
            }
        }
        super(params);
    }
}
exports.CryptoPriceEndpoint = CryptoPriceEndpoint;
/**
 * A ForexPriceEndpoint expands on the existing [[PriceEndpoint]] and provides more descriptive name for
 *  endpoints that provide forex prices.
 */
class ForexPriceEndpoint extends PriceEndpoint {
}
exports.ForexPriceEndpoint = ForexPriceEndpoint;
//# sourceMappingURL=price.js.map