/**
 * Disabling this rule as it's only necessary for
 * combining multiple class names and documenting the output.
 * That doesn't happen in this file but the linter still throws an error.
 * https://github.com/wikimedia/eslint-plugin-mediawiki/blob/master/docs/rules/class-doc.md
 */

/** @interface VectorResourceLoaderVirtualConfig */
/** @interface MediaWikiPageReadyModule */

const /** @type {VectorResourceLoaderVirtualConfig} */
	config = require( /** @type {string} */ ( './config.json' ) ),
	// T251544: Collect search performance metrics to compare Vue search with
	// mediawiki.searchSuggest performance.
	CAN_TEST_SEARCH = !!(
		window.performance &&
		!!performance.mark &&
		!!performance.measure &&
		performance.getEntriesByName ),
	LOAD_START_MARK = 'mwVectorVueSearchLoadStart',
	LOAD_END_MARK = 'mwVectorVueSearchLoadEnd',
	LOAD_MEASURE = 'mwVectorVueSearchLoadStartToLoadEnd';

/**
 * Loads the search module via `mw.loader.using` on the element's
 * focus event. Or, if the element is already focused, loads the
 * search module immediately.
 * After the search module is loaded, executes a function to remove
 * the loading indicator.
 *
 * @param {Element} element search input.
 * @param {string} moduleName resourceLoader module to load.
 * @param {string|null} startMarker
 * @param {null|function(): void} afterLoadFn function to execute after search module loads.
 */
function loadSearchModule( element, moduleName, startMarker, afterLoadFn ) {
	const SHOULD_TEST_SEARCH = CAN_TEST_SEARCH &&
		moduleName === 'skins.vector.search';

	function requestSearchModule() {
		if ( SHOULD_TEST_SEARCH && startMarker !== null && afterLoadFn !== null ) {
			performance.mark( startMarker );
			mw.loader.using( moduleName, afterLoadFn );
		} else {
			mw.loader.load( moduleName );
		}
		element.removeEventListener( 'focus', requestSearchModule );
	}

	if ( document.activeElement === element ) {
		requestSearchModule();
	} else {
		element.addEventListener( 'focus', requestSearchModule );
	}
}

/**
 * Marks when the lazy load has completed.
 *
 * @param {string} startMarker
 * @param {string} endMarker
 * @param {string} measureMarker
 */
function markLoadEnd( startMarker, endMarker, measureMarker ) {
	if ( performance.getEntriesByName( startMarker ).length ) {
		performance.mark( endMarker );
		performance.measure( measureMarker, startMarker, endMarker );
	}
}

/**
 * Initialize the loading of the search module as well as the loading indicator.
 * Only initialize the loading indicator when not using the core search module.
 *
 * @param {Document} document
 */
function initSearchLoader( document ) {
	const searchBoxes = document.querySelectorAll( '.vector-search-box' );

	// Allow developers to defined $wgVectorSearchApiUrl in LocalSettings to target different APIs
	if ( config.VectorSearchApiUrl ) {
		mw.config.set( 'wgVectorSearchApiUrl', config.VectorSearchApiUrl );
	}

	if ( !searchBoxes.length ) {
		return;
	}

	/**
	 * If we are in a browser that doesn't support ES6 fall back to non-JS version.
	 */
	if ( mw.loader.getState( 'skins.vector.search' ) === null ) {
		document.body.classList.remove(
			'skin-vector-search-vue'
		);
		return;
	}

	Array.prototype.forEach.call( searchBoxes, ( searchBox ) => {
		const searchInner = searchBox.querySelector( 'form > div' ),
			searchInput = searchBox.querySelector( 'input[name="search"]' ),
			isPrimarySearch = searchInput && searchInput.getAttribute( 'id' ) === 'searchInput';

		if ( !searchInput || !searchInner ) {
			return;
		}
		// Remove tooltips while Vue search is still loading
		searchInput.setAttribute( 'autocomplete', 'off' );
		loadSearchModule(
			searchInput,
			'skins.vector.search',
			isPrimarySearch ? LOAD_START_MARK : null,
			// Note, loading Vue.js will remove the element from the DOM.
			() => {
				if ( isPrimarySearch ) {
					markLoadEnd( LOAD_START_MARK, LOAD_END_MARK, LOAD_MEASURE );
				}
			}
		);
	} );
}

module.exports = {
	initSearchLoader: initSearchLoader
};
