const
	HEADER_CONTAINER_CLASS = 'vector-header-container',
	SEARCH_BOX_SELECTOR = '.vector-search-box',
	SEARCH_VISIBLE_CLASS = 'vector-header-search-toggled';

/**
 * Binds event handlers necessary for the searchBox to disappear when the user
 * clicks outside the searchBox.
 *
 * @param {HTMLElement} searchBox
 * @param {HTMLElement} header
 */
function bindSearchBoxHandler( searchBox, header ) {
	/**
	 * @param {Event} ev
	 * @ignore
	 */
	const clickHandler = ( ev ) => {
		if (
			ev.target instanceof HTMLElement &&
			// Check if the click target was a suggestion link. Codex clears the
			// suggestion elements from the DOM when a suggestion is clicked so we
			// can't test if the suggestion is a child of the searchBox.
			//
			// Note: The .closest API is feature detected in `initSearchToggle`.
			!ev.target.closest( '.cdx-typeahead-search .cdx-menu-item__content' ) &&
			!searchBox.contains( ev.target )
		) {
			header.classList.remove( SEARCH_VISIBLE_CLASS );

			document.removeEventListener( 'click', clickHandler );
		}
	};

	document.addEventListener( 'click', clickHandler );
}

/**
 * Binds event handlers necessary for the searchBox to show when the toggle is
 * clicked.
 *
 * @param {HTMLElement} searchBox
 * @param {HTMLElement} header
 * @param {Element} searchToggle
 */
function bindToggleClickHandler( searchBox, header, searchToggle ) {
	/**
	 * @param {Event} ev
	 * @ignore
	 */
	const handler = ( ev ) => {
		// The toggle is an anchor element. Prevent the browser from navigating away
		// from the page when clicked.
		ev.preventDefault();

		header.classList.add( SEARCH_VISIBLE_CLASS );

		// Defer binding the search box handler until after the event bubbles to the
		// top of the document so that the handler isn't called when the user clicks
		// the search toggle. Event bubbled callbacks execute within the same task
		// in the event loop.
		//
		// Also, defer focusing the input to another task in the event loop. At the time
		// of this writing, Safari 14.0.3 has trouble changing the visibility of the
		// element and focusing the input within the same task.
		setTimeout( () => {
			bindSearchBoxHandler( searchBox, header );

			const searchInput = /** @type {HTMLInputElement|null} */ ( searchBox.querySelector( 'input[type="search"]' ) );

			if ( searchInput ) {
				const beforeScrollX = window.scrollX;
				const beforeScrollY = window.scrollY;
				searchInput.focus();
				// For some reason, Safari 14,15 tends to undesirably change the scroll
				// position of `input` elements inside fixed position elements.
				// While an Internet search suggests similar problems with mobile Safari
				// it didn't yield any results for desktop Safari.
				// This line resets any unexpected scrolling that occurred while the
				// input received focus.
				// If you are in the future with a modern version of Safari, where 14 and 15
				// receive a low amount of page views, please reference T297636 and test
				// to see whether this line of code can be removed.
				// Additionally, these lines might become unnecessary when/if Safari
				// supports the `preventScroll` focus option [1] in the future:
				// https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus#parameters
				if ( beforeScrollX !== undefined && beforeScrollY !== undefined ) {
					window.scroll( beforeScrollX, beforeScrollY );
				}
			}
		} );
	};

	searchToggle.addEventListener( 'click', handler );
}

/**
 * Enables search toggling behavior in a header given a toggle element (e.g.
 * search icon).  When the toggle element is clicked, a class,
 * `SEARCH_VISIBLE_CLASS`, will be applied to a header matching the selector
 * `HEADER_SELECTOR` and the input inside the element, SEARCH_BOX_SELECTOR, will
 * be focused.  This class can be used in CSS to show/hide the necessary
 * elements. When the user clicks outside of SEARCH_BOX_SELECTOR, the class will
 * be removed.
 *
 * @param {HTMLElement|Element} searchToggle
 */
module.exports = function initSearchToggle( searchToggle ) {
	const headerContainer =
		/** @type {HTMLElement|null} */ ( searchToggle.closest( `.${ HEADER_CONTAINER_CLASS }` ) );
	const header =
		/** @type {HTMLElement|null} */ ( headerContainer && headerContainer.firstElementChild );

	if ( !header ) {
		return;
	}

	const searchBox =
	/** @type {HTMLElement|null} */ ( header.querySelector( SEARCH_BOX_SELECTOR ) );

	if ( !searchBox ) {
		return;
	}

	bindToggleClickHandler( searchBox, header, searchToggle );
};
