jQuery( function ( $ ) {
	const $modals = $( '.wcs-modal' );
	let $currentModal;
	let $triggerElement;

	// Resize all open modals on window resize.
	$( window ).on( 'resize', resizeModals );

	// Initialize modals
	$( $modals ).each( function () {
		trigger = $( this ).data( 'modal-trigger' );
		$( trigger ).on( 'click', { modal: this }, show_modal );
	} );

	/**
	 * Displays the modal linked to a click event.
	 *
	 * Attaches all close callbacks and resizes to fit.
	 *
	 * @param {JQuery event} event
	 */
	function show_modal( event ) {
		$triggerElement = $( event.target );
		$currentModal   = $( event.data.modal );

		if ( ! should_show_modal( $currentModal ) ) {
			return;
		}

		// Prevent the trigger element event being triggered.
		event.preventDefault();

		const $contentWrapper = $currentModal.find( '.content-wrapper' );
		const $close = $currentModal.find( '.close' );

		$currentModal.addClass( 'open' );
		resizeModal( $currentModal );

		$( document.body ).toggleClass( 'wcs-modal-open', true );
		$currentModal.trigger( 'focus' );
		document.addEventListener( 'focusin', keepFocusInModal );

		// Attach callbacks to handle closing the modal.
		$close.on( 'click', () => close_modal( $currentModal ) );
		$currentModal.on( 'click', () => close_modal( $currentModal ) );
		$contentWrapper.on( 'click', ( e ) => e.stopPropagation() );

		// Close the modal if the escape key is pressed.
		$currentModal.on( 'keyup', function ( e ) {
			if ( 27 === e.keyCode ) {
				close_modal( $currentModal );
			}
		} );
	}

	/**
	 * Closes a modal and resets any forced height styles.
	 *
	 * @param {JQuery Object} $modal
	 */
	function close_modal( $modal ) {
		$modal.removeClass( 'open' );
		$( $modal ).find( '.content-wrapper' ).css( 'height', '' );

		if ( 0 === $modals.filter( '.open' ).length ) {
			$( document.body ).removeClass( 'wcs-modal-open' );
			$currentModal = false;
			document.removeEventListener( 'focusin', keepFocusInModal );
			$triggerElement.trigger( 'focus' );
		}
	}

	/**
	 * Determines if a modal should be displayed.
	 *
	 * A custom trigger is called to allow third-parties to filter whether the modal should be displayed or not.
	 *
	 * @param {JQuery Object} modal
	 */
	function should_show_modal( modal ) {
		// Allow third-parties to filter whether the modal should be displayed.
		var event = jQuery.Event( 'wcs_show_modal' );
		event.modal = modal;

		$( document ).trigger( event );

		// Fallback to true (show modal) if the result is undefined.
		return undefined === event.result ? true : event.result;
	}

	/**
	 * Resize all open modals to fit the display.
	 */
	function resizeModals() {
		$( $modals ).each( function () {
			if ( ! $( this ).hasClass( 'open' ) ) {
				return;
			}

			resizeModal( this );
		} );
	}

	/**
	 * Resize a modal to fit the display.
	 *
	 * @param {JQuery Object} $modal
	 */
	function resizeModal( $modal ) {
		const $modal_container = $( $modal ).find( '.content-wrapper' );

		// On smaller displays the height is already forced to be 100% in CSS. We just clear any height we might set previously.
		if ( $( window ).width() <= 414 ) {
			$modal_container.css( 'height', '' );
		} else if ( $modal_container.height() > $( window ).height() ) {
			// Force the container height to trigger scroll etc if it doesn't fit on the screen.
			$modal_container.css( 'height', '90%' );
		}
	}

	/**
	 * If focus moves out of the open modal, return focus to it.
	 *
	 * @param event
	 */
	function keepFocusInModal( event ) {
		if ( $currentModal && ! $currentModal[0].contains( event.target ) ) {
			$currentModal.trigger( 'focus' );
		}
	}
} );
