"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FLAGON_NAME_DATA_FEEDS = exports.apiLookupLiquidKey = exports.apiLookupActionFields = exports.performApiLookup = exports.getCachedResponse = exports.getRequestId = void 0;
const crypto_1 = require("crypto");
const actions_core_1 = require("@segment/actions-core");
const liquidjs_1 = require("liquidjs");
const Liquid = new liquidjs_1.Liquid();
const renderLiquidFields = async ({ url, body }, profile, datafeedTags, logDataFeedError) => {
    let renderedUrl;
    let renderedBody;
    try {
        renderedUrl = await Liquid.parseAndRender(url, { profile });
    }
    catch (error) {
        logDataFeedError('URL liquid render failuere', error);
        datafeedTags.push('error:true', 'reason:rendering_failure', 'rendering:url');
        throw new actions_core_1.IntegrationError('Unable to parse data feed url', 'DATA_FEED_RENDERING_ERROR', 400);
    }
    try {
        renderedBody = body ? await Liquid.parseAndRender(body, { profile }) : undefined;
    }
    catch (error) {
        logDataFeedError('Body liquid render failure', error);
        datafeedTags.push('error:true', 'reason:rendering_failure', 'rendering:body');
        throw new actions_core_1.IntegrationError('Unable to parse email api lookup body', 'DATA_FEED_RENDERING_ERROR', 400);
    }
    return {
        renderedUrl,
        renderedBody
    };
};
const getRequestId = ({ method, url, body, headers }) => {
    const requestHash = (0, crypto_1.createHash)('sha256');
    requestHash.update(`${method}${url}${body}${JSON.stringify(headers)}`);
    const requestId = requestHash.digest('hex');
    return requestId;
};
exports.getRequestId = getRequestId;
const getCachedResponse = async ({ responseType }, requestId, engageDestinationCache, datafeedTags) => {
    const cachedResponse = await engageDestinationCache.getByKey(requestId);
    if (!cachedResponse) {
        datafeedTags.push('cache_hit:false');
        return;
    }
    datafeedTags.push('cache_hit:true');
    if (responseType === 'json') {
        return JSON.parse(cachedResponse);
    }
    return cachedResponse;
};
exports.getCachedResponse = getCachedResponse;
const performApiLookup = async (request, apiLookupConfig, profile, statsClient, tags, settings, logger, engageDestinationCache) => {
    const { id, method, headers, cacheTtl, name, shouldRetryOnRetryableError = true } = apiLookupConfig;
    const datafeedTags = [
        ...tags,
        `datafeed_id:${id}`,
        `datafeed_name:${name}`,
        `space_id:${settings.spaceId}`,
        `cache_ttl_greater_than_0:${cacheTtl > 0}`,
        `retry_enabled:${shouldRetryOnRetryableError}`
    ];
    const logDataFeedError = (message, error) => {
        logger?.error(`TE Messaging: Data feed error - message: ${message} - data feed name: ${name} - data feed id: ${id} - space id: ${settings.spaceId} - raw error: ${error}`);
    };
    try {
        const { renderedUrl, renderedBody } = await renderLiquidFields(apiLookupConfig, profile, datafeedTags, logDataFeedError);
        const requestId = (0, exports.getRequestId)({ ...apiLookupConfig, url: renderedUrl, body: renderedBody });
        if (cacheTtl > 0 && !engageDestinationCache) {
            logDataFeedError('Data feed cache not available and cache needed');
            datafeedTags.push('cache_set:false');
            throw new actions_core_1.IntegrationError('Data feed cache not available and cache needed', 'DATA_FEED_CACHE_NOT_AVAILABLE', 400);
        }
        if (cacheTtl > 0 && engageDestinationCache) {
            const cachedResponse = await (0, exports.getCachedResponse)(apiLookupConfig, requestId, engageDestinationCache, datafeedTags);
            if (cachedResponse) {
                datafeedTags.push('error:false');
                return cachedResponse;
            }
        }
        let data;
        try {
            const res = await request(renderedUrl, {
                headers: headers ?? undefined,
                timeout: 3000,
                method: method,
                body: renderedBody,
                skipResponseCloning: true
            });
            data = await res.data;
        }
        catch (error) {
            const respError = error.response;
            logDataFeedError('Data feed call failure', error);
            datafeedTags.push(`error:true`, `error_status:${respError?.status}`, 'reason:api_call_failure');
            if (shouldRetryOnRetryableError) {
                throw error;
            }
            return {};
        }
        if (cacheTtl > 0 && engageDestinationCache) {
            const dataString = JSON.stringify(data);
            const size = Buffer.byteLength(dataString, 'utf-8');
            if (size > engageDestinationCache.maxValueSizeBytes) {
                datafeedTags.push('error:true', 'reason:response_size_too_big');
                logDataFeedError('Data feed response size too big too cache and caching needed, failing send');
                throw new actions_core_1.IntegrationError('Data feed response size too big too cache and caching needed, failing send', 'DATA_FEED_RESPONSE_TOO_BIG', 400);
            }
            try {
                await engageDestinationCache.setByKey(requestId, dataString, cacheTtl / 1000);
                datafeedTags.push('cache_set:true');
            }
            catch (error) {
                logDataFeedError('Data feed cache set failure', error);
                datafeedTags.push('error:true', 'reason:cache_set_failure', 'cache_set:false');
                throw error;
            }
        }
        datafeedTags.push('error:false');
        return data;
    }
    catch (error) {
        const isErrorCapturedInTags = datafeedTags.find((str) => str.includes('error:true'));
        if (!isErrorCapturedInTags) {
            datafeedTags.push('error:true', 'reason:unknown');
        }
        tags.push('reason:datafeed_failure');
        logDataFeedError('Unexpected data feed error', error);
        throw error;
    }
    finally {
        statsClient?.incr('datafeed_execution', 1, datafeedTags);
    }
};
exports.performApiLookup = performApiLookup;
exports.apiLookupActionFields = {
    id: {
        label: 'ID',
        description: 'The id of the API lookup for use in logging & observability',
        type: 'string'
    },
    name: {
        label: 'Name',
        description: 'The name of the API lookup referenced in liquid syntax',
        type: 'string',
        required: true
    },
    url: {
        label: 'URL',
        description: 'The URL endpoint to call',
        type: 'string',
        required: true
    },
    method: {
        label: 'Request Method',
        description: 'The request method, e.g. GET/POST/etc.',
        type: 'string',
        required: true
    },
    cacheTtl: {
        label: 'Cache TTL',
        description: 'The cache TTL in ms',
        type: 'integer',
        required: true
    },
    body: {
        label: 'Request Body',
        description: 'The request body for use with POST/PUT/PATCH requests',
        type: 'string'
    },
    headers: {
        label: 'Request Headers',
        description: 'Headers in JSON to be sent with the request',
        type: 'object'
    },
    responseType: {
        label: 'Response Type',
        description: 'The response type of the request. Currently only supporting JSON.',
        type: 'string',
        required: true
    },
    shouldRetryOnRetryableError: {
        label: 'Should Retry',
        description: 'Whether the message should be retried (if the error code is retryable) when the data feed fails or if it should be sent with empty data instead',
        type: 'boolean'
    }
};
exports.apiLookupLiquidKey = 'datafeeds';
exports.FLAGON_NAME_DATA_FEEDS = 'is-datafeeds-enabled';
//# sourceMappingURL=apiLookups.js.map