Source: api/instant-search-api.js

import jQ from 'jquery';

import Globals from '../helpers/globals';
import Settings from '../helpers/settings';
import Utils from '../helpers/utils';
import Api from '../helpers/api';

const BoostPFSInstantSearchCallback = (result) => {
	InstantSearchApi.setDefaultValueForExcludedFields(result);

	if (typeof InstantSearchApi.afterCall == 'function') {
		InstantSearchApi.afterCall(result);
	}

	if (typeof InstantSearchApi.afterCallAsync == 'function') {
		InstantSearchApi.afterCallAsync(result, callbackInstantSearchApi);
		return;
	}
	callbackInstantSearchApi(result);
};

const getSuggestionData = (searchTerm, errorCount, eventType, previousTerm) => {
	// Execute before get instant search data (customization use)
	if (typeof InstantSearchApi.beforeCall == 'function') {
		InstantSearchApi.beforeCall(searchTerm);
	}

	// Execute before get instant search data (customization use - call async 3rd party)
	if (typeof InstantSearchApi.beforeCallAsync == 'function') {
		var callInstantSearchApiWrapper = () => {
			callInstantSearchApi(searchTerm, 0, eventType, previousTerm);
		}
		InstantSearchApi.beforeCallAsync(searchTerm, callInstantSearchApiWrapper);
		return;
	}

	callInstantSearchApi(searchTerm, 0, eventType, previousTerm);
};

const callInstantSearchApi = (searchTerm, errorCount, eventType, previousTerm) => {
	var errorCount = typeof errorCount !== 'undefined' ? errorCount : 0;
	var previousTerm = typeof previousTerm !== 'undefined' ? previousTerm : '';
	// Prepare request params
	var params = prepareSuggestionParams(searchTerm, eventType);
	if (params.q) {
		searchTerm = params.q;
		delete params.q;
	}
	if (previousTerm != '') params.prev_query = previousTerm;
	var script = document.createElement("script");
	script.type = 'text/javascript';
	script.src = Api.getApiUrl('suggestion') + '?q=' + searchTerm + '&' + jQ.param(params);
	script.async = true;
	script.addEventListener('error', function (e) {
		jQ(this).remove();
		// Resend API 3 times when got the error
		if (errorCount < 3) {
			errorCount++;
			callInstantSearchApi(params.q, eventType, previousTerm, errorCount);
		} else {
			// self.showError('Oops, we cannot fetch products at this moment. Please try again later.');
		}
	});
	// Append the script element to the HEAD section.
	document.getElementsByTagName('head')[0].appendChild(script);
	script.addEventListener('load', function (e) {
		jQ(this).remove();
	});
}

const callbackInstantSearchApi = (result) => {
	var cache = Globals.suggestionCache;
	if (result.hasOwnProperty('prev_query')) {
		var prevQuery = result.prev_query;
		if (cache.hasOwnProperty(prevQuery)) {
			var previousData = cache[prevQuery];
			// Customize the suggest_query data
			var fields = ['collections', 'did_you_mean', 'pages', 'suggestions', 'suggest_query'];
			var k;
			var fieldsLength = fields.length;
			for (k = 0; k < fieldsLength; k++) {
				result[fields[k]] = Utils.getValueInObjectArray(fields[k], previousData);
			}
			result['prev_total_product'] = Utils.getValueInObjectArray('total_product', previousData);
			var productIndex = Utils.findIndexArray('products', previousData, 'key');
			previousData[productIndex]['values'] = result['products'];
			previousData.push({
				key: 'local_cache',
				values: true
			});
			previousData.push({
				key: 'suggest_total_product',
				values: result.total_product
			});
			Globals.suggestionCache[prevQuery] = previousData;
		}
	}
	// Build elements
	window.suggestionCallback(jQ.map(result, function (item, index) {
		return {
			key: index,
			values: item
		};
	}));
}

const prepareSuggestionParams = (searchTerm, eventType) => {
	var params = {};
	params.shop = Globals.shopDomain;
	params.t = new Date().getTime();
	if (!Settings.getSettingValue('search.enableDefaultResult')) params.enable_default_result = false;
	var enableFuzzy = Settings.getSettingValue('search.enableFuzzy');
	if (enableFuzzy !== true) params.fuzzy = enableFuzzy;
	if (Settings.getSettingValue('search.fullMinMatch')) params.full_min_match = true;
	if (Settings.getSettingValue('search.reduceMinMatch') !== false) {
		params.reduce_min_match = Settings.getSettingValue('search.reduceMinMatch');
	}
	if (Settings.getSettingValue('search.enablePlusCharacterSearch')) params.enable_plus_character_search = true;
	// Get product/variant available or not
	if (Settings.getSettingValue('search.productAvailable') == true) params.product_available = true;
	// Limit
	var blocks = Settings.getSettingValue('search.suggestionBlocks');
	var k;
	var blocksLength = blocks.length;
	for (k = 0; k < blocksLength; k++) {
		var id = blocks[k]['type'].slice(0, -1);
		params[id + '_limit'] = blocks[k]['number'];
	}
	params['dym_limit'] = Settings.getSettingValue('search.suggestionDymLimit');
	// Skip fields
	var skipFields = Settings.getSettingValue('search.skipFields');
	if (skipFields.length > 0) param.skipFields = skipFields;
	params.callback = 'BoostPFSInstantSearchCallback';
	params.event_type = eventType;
	// Suggest types
	var suggestionTypes = eventType == 'suggest_dym' ? ['products'] : Settings.getSettingValue('search.suggestionTypes');
	params.suggest_types = suggestionTypes;
	// Set locale param
	params = Api.setApiLocaleParams(params);
	// Override with Globals.instantSearchQueryParams
	var mergedParams = Object.assign(params, Globals.instantSearchQueryParams);
	return mergedParams;
};

/**
 * The API exclude some product fields in the response.
 * We set the default values for these field.
 * @param apiResult
 */
const setDefaultValueForExcludedFields = (apiResult) => {
	if (Array.isArray(apiResult.products)){
		var currentDate = new Date();
		var currentDateString = currentDate.toISOString();

		apiResult.products.forEach((product) => {
			if (!product.hasOwnProperty('variants')) {
				product.variant = [];
			}
			if (!product.hasOwnProperty('images_info')) {
				product.images_info = [];
			}
			if (!product.hasOwnProperty('collections')) {
				product.collections = [];
			}
			if (!product.hasOwnProperty('tags')) {
				product.tags = [];
			}
			if (!product.hasOwnProperty('skus')) {
				product.skus = [];
			}
			if (!product.hasOwnProperty('options_with_values')) {
				product.options_with_values = [];
			}
			if (!product.hasOwnProperty('barcodes')) {
				product.barcodes = [];
			}
			if (!product.hasOwnProperty('created_at')) {
				product.created_at = currentDateString;
			}
			if (!product.hasOwnProperty('updated_at')) {
				product.updated_at = currentDateString;
			}
			if (!product.hasOwnProperty('published_at')) {
				product.published_at = currentDateString;
			}
		})
	}
}

const InstantSearchApi = {
	BoostPFSInstantSearchCallback: BoostPFSInstantSearchCallback,
	getSuggestionData: getSuggestionData,
	prepareSuggestionParams: prepareSuggestionParams,
	setDefaultValueForExcludedFields: setDefaultValueForExcludedFields,
	callInstantSearchApi: callInstantSearchApi,
	callbackInstantSearchApi: callbackInstantSearchApi,
	beforeCall: null,
	afterCall: null,
	beforeCallAsync: null,
	afterCallAsync: null
}

export default InstantSearchApi;