(function () {
	'use strict';

	/**
	 * Initialise the collapsible Booking Hub panels.
	 */
	function initialiseHubPanels() {
		const toggles = document.querySelectorAll(
			'.tutorhelper-hub-panel__toggle'
		);

		toggles.forEach(function (toggle) {
			toggle.addEventListener('click', function () {
				const panelId = toggle.getAttribute('aria-controls');
				const panel = document.getElementById(panelId);

				if (!panel) {
					return;
				}

				const isExpanded =
					toggle.getAttribute('aria-expanded') === 'true';

				const newExpandedState = !isExpanded;

				toggle.setAttribute(
					'aria-expanded',
					newExpandedState ? 'true' : 'false'
				);

				panel.hidden = !newExpandedState;

				const icon = toggle.querySelector('.dashicons');

				if (!icon) {
					return;
				}

				icon.classList.toggle(
					'dashicons-arrow-up-alt2',
					newExpandedState
				);

				icon.classList.toggle(
					'dashicons-arrow-down-alt2',
					!newExpandedState
				);
			});
		});
	}

	/**
	 * Return the weekday key for a Y-m-d date.
	 *
	 * Date.UTC prevents the browser's local timezone from changing the day.
	 *
	 * @param {string} dateValue Date in Y-m-d format.
	 *
	 * @returns {string|null}
	 */
	function getWeekdayKey(dateValue) {
		const parts = dateValue.split('-');

		if (parts.length !== 3) {
			return null;
		}

		const year = Number(parts[0]);
		const month = Number(parts[1]);
		const day = Number(parts[2]);

		if (
			!Number.isInteger(year)
			|| !Number.isInteger(month)
			|| !Number.isInteger(day)
		) {
			return null;
		}

		const selectedDate = new Date(
			Date.UTC(
				year,
				month - 1,
				day
			)
		);

		const weekdayMap = [
			'sun',
			'mon',
			'tue',
			'wed',
			'thu',
			'fri',
			'sat'
		];

		return weekdayMap[selectedDate.getUTCDay()];
	}

	/**
	 * Automatically select the weekday when start and end dates match.
	 */
	function updateSingleDateWeekday() {
		const startDateInput = document.getElementById(
			'tutorhelper-availability-start-date'
		);

		const endDateInput = document.getElementById(
			'tutorhelper-availability-end-date'
		);

		if (!startDateInput || !endDateInput) {
			return;
		}

		const startDate = startDateInput.value;
		const endDate = endDateInput.value;

		if (
			startDate === ''
			|| endDate === ''
			|| startDate !== endDate
		) {
			return;
		}

		const weekday = getWeekdayKey(startDate);

		if (!weekday) {
			return;
		}

		const weekdayCheckboxes = document.querySelectorAll(
			'.tutorhelper-weekday-options '
			+ 'input[name="weekdays[]"]'
		);

		weekdayCheckboxes.forEach(function (checkbox) {
			const shouldBeChecked = checkbox.value === weekday;
			const hasChanged = checkbox.checked !== shouldBeChecked;

			checkbox.checked = shouldBeChecked;

			if (hasChanged) {
				checkbox.dispatchEvent(
					new Event('change', {
						bubbles: true
					})
				);
			}
		});
	}

	/**
	 * Initialise automatic weekday selection.
	 */
	function initialiseSingleDateWeekday() {
		const startDateInput = document.getElementById(
			'tutorhelper-availability-start-date'
		);

		const endDateInput = document.getElementById(
			'tutorhelper-availability-end-date'
		);

		if (!startDateInput || !endDateInput) {
			return;
		}

		[
			startDateInput,
			endDateInput
		].forEach(function (input) {
			input.addEventListener(
				'input',
				updateSingleDateWeekday
			);

			input.addEventListener(
				'change',
				updateSingleDateWeekday
			);
		});

		/*
		 * Also synchronise an existing single-date rule when its edit form
		 * first loads.
		 */
		updateSingleDateWeekday();
	}

	/**
	 * Return all Booking Type checkboxes inside the custom picker.
	 *
	 * @returns {HTMLInputElement[]}
	 */
	function getAllowedBookingTypeCheckboxes() {
		return Array.from(
			document.querySelectorAll(
				'#tutorhelper-allowed-booking-type-options '
				+ 'input[name="allowed_booking_type_ids[]"]'
			)
		);
	}

	/**
	 * Update the Booking Type picker label, chips and option order.
	 */
	function updateAllowedBookingTypePicker() {
		const toggle = document.getElementById(
			'tutorhelper-allowed-booking-type-toggle'
		);

		const chips = document.getElementById(
			'tutorhelper-allowed-booking-type-chips'
		);

		const options = document.getElementById(
			'tutorhelper-allowed-booking-type-options'
		);

		if (!toggle || !chips) {
			return;
		}

		const checkboxes = getAllowedBookingTypeCheckboxes();

		const selected = checkboxes.filter(function (checkbox) {
			return checkbox.checked;
		});

		/*
		 * Update the dropdown button label.
		 */
		if (selected.length === 0) {
			toggle.textContent = 'All Booking Types';
		} else if (selected.length === 1) {
			const option = selected[0].closest(
				'[data-booking-type-option]'
			);

			toggle.textContent = option
				? option.dataset.bookingTypeTitle
				: '1 Booking Type selected';
		} else {
			toggle.textContent =
				selected.length + ' Booking Types selected';
		}

		/*
		 * Rebuild the selected chips.
		 */
		chips.innerHTML = '';

		if (selected.length === 0) {
			const chip = document.createElement('span');

			chip.className =
				'tutorhelper-booking-type-chip is-empty';

			chip.textContent = 'All Booking Types';

			chips.appendChild(chip);
		} else {
			selected.forEach(function (checkbox) {
				const option = checkbox.closest(
					'[data-booking-type-option]'
				);

				const title = option
					? option.dataset.bookingTypeTitle
					: checkbox.value;

				const chip = document.createElement('span');

				chip.className =
					'tutorhelper-booking-type-chip';

				chip.textContent = title;

				chips.appendChild(chip);
			});
		}

		/*
		 * Move selected options to the top, then sort each group by title.
		 */
		if (options) {
			const optionLabels = Array.from(
				options.querySelectorAll(
					'[data-booking-type-option]'
				)
			);

			optionLabels
				.sort(function (firstOption, secondOption) {
					const firstInput = firstOption.querySelector(
						'input[type="checkbox"]'
					);

					const secondInput = secondOption.querySelector(
						'input[type="checkbox"]'
					);

					const firstSelected =
						firstInput && firstInput.checked ? 1 : 0;

					const secondSelected =
						secondInput && secondInput.checked ? 1 : 0;

					if (firstSelected !== secondSelected) {
						return secondSelected - firstSelected;
					}

					return (
						firstOption.dataset.bookingTypeTitle || ''
					).localeCompare(
						secondOption.dataset.bookingTypeTitle || ''
					);
				})
				.forEach(function (option) {
					const input = option.querySelector(
						'input[type="checkbox"]'
					);

					option.classList.toggle(
						'is-selected',
						Boolean(input && input.checked)
					);

					options.appendChild(option);
				});
		}
	}

	/**
	 * Initialise the custom Booking Type multi-select.
	 */
	function initialiseAllowedBookingTypePicker() {
		const picker = document.getElementById(
			'tutorhelper-allowed-booking-type-picker'
		);

		const toggle = document.getElementById(
			'tutorhelper-allowed-booking-type-toggle'
		);

		const panel = document.getElementById(
			'tutorhelper-allowed-booking-type-panel'
		);

		const clearButton = document.getElementById(
			'tutorhelper-clear-allowed-booking-types'
		);

		if (!picker || !toggle || !panel) {
			return;
		}

		toggle.addEventListener('click', function () {
			const isOpen = !panel.hidden;

			panel.hidden = isOpen;

			toggle.setAttribute(
				'aria-expanded',
				isOpen ? 'false' : 'true'
			);
		});

		getAllowedBookingTypeCheckboxes().forEach(
			function (checkbox) {
				checkbox.addEventListener(
					'change',
					updateAllowedBookingTypePicker
				);
			}
		);

		if (clearButton) {
			clearButton.addEventListener('click', function () {
				getAllowedBookingTypeCheckboxes().forEach(
					function (checkbox) {
						checkbox.checked = false;
					}
				);

				updateAllowedBookingTypePicker();
			});
		}

		document.addEventListener('click', function (event) {
			if (
				!panel.hidden
				&& !picker.contains(event.target)
			) {
				panel.hidden = true;

				toggle.setAttribute(
					'aria-expanded',
					'false'
				);
			}
		});

		document.addEventListener('keydown', function (event) {
			if (
				event.key === 'Escape'
				&& !panel.hidden
			) {
				panel.hidden = true;

				toggle.setAttribute(
					'aria-expanded',
					'false'
				);

				toggle.focus();
			}
		});

		updateAllowedBookingTypePicker();
	}

	/**
	 * Initialise Availability edit-scope controls.
	 */
	function initialiseAvailabilityEditScope() {
		const scopeInputs = document.querySelectorAll(
			'input[name="edit_scope"]'
		);

		const rangeFields = document.querySelector(
			'[data-edit-range]'
		);

		const recurrenceFields = document.querySelector(
			'[data-recurrence-fields]'
		);

		if (scopeInputs.length === 0) {
			return;
		}

		function updateEditScope() {
			const selectedScope = document.querySelector(
				'input[name="edit_scope"]:checked'
			);

			if (!selectedScope) {
				return;
			}

			const isRange =
				selectedScope.value === 'date_range';

			const isCompleteRule =
				selectedScope.value === 'complete_rule';

			if (rangeFields) {
				rangeFields.hidden = !isRange;

				rangeFields
					.querySelectorAll('input[type="date"]')
					.forEach(function (input) {
						input.required = isRange;
						input.disabled = !isRange;
					});
			}

			if (recurrenceFields) {
				recurrenceFields.hidden = !isCompleteRule;

				recurrenceFields
					.querySelectorAll('input, select, textarea')
					.forEach(function (input) {
						input.disabled = !isCompleteRule;
					});
			}
		}

		scopeInputs.forEach(function (input) {
			input.addEventListener(
				'change',
				updateEditScope
			);
		});

		updateEditScope();
	}



	/**
	 * Initialise Availability removal-scope controls.
	 */
	function initialiseAvailabilityRemoval() {
		const scopeInputs = document.querySelectorAll(
			'input[name="remove_scope"]'
		);

		const rangeFields = document.querySelector(
			'[data-removal-range]'
		);

		if (scopeInputs.length === 0 || !rangeFields) {
			return;
		}

		function updateRangeVisibility() {
			const selectedScope = document.querySelector(
				'input[name="remove_scope"]:checked'
			);

			const rangeSelected =
				selectedScope
				&& selectedScope.value === 'date_range';

			rangeFields.hidden = !rangeSelected;

			rangeFields
				.querySelectorAll('input[type="date"]')
				.forEach(function (input) {
					input.required = rangeSelected;
				});
		}

		scopeInputs.forEach(function (input) {
			input.addEventListener(
				'change',
				updateRangeVisibility
			);
		});

		updateRangeVisibility();
	}

	/**
	 * Initialise Booking Type archive and restore confirmations.
	 */
	function initialiseBookingTypeStatusActions() {
		const forms = document.querySelectorAll(
			'.tutorhelper-booking-type-status-form'
		);

		forms.forEach(function (form) {
			form.addEventListener('submit', function (event) {
				const action = form.dataset.bookingTypeStatus;

				let message =
					'Restore this Booking Type?';

				if (action === 'archive') {
					message =
						'Archive this Booking Type? '
						+ 'It will be hidden from new bookings, '
						+ 'but existing bookings will retain it.';
				}

				if (!window.confirm(message)) {
					event.preventDefault();
				}
			});
		});
	}

	/**
	 * Allow the existing Booking Types list to be hidden independently
	 * from the complete Booking Types panel.
	 */
	function initialiseBookingTypeListToggle() {
		const button = document.querySelector(
			'.tutorhelper-booking-type-list-toggle'
		);

		const content = document.getElementById(
			'tutorhelper-booking-type-existing-content'
		);

		if (!button || !content) {
			return;
		}

		button.addEventListener('click', function () {
			const expanded =
				button.getAttribute('aria-expanded') === 'true';

			const nextExpanded = !expanded;

			button.setAttribute(
				'aria-expanded',
				nextExpanded ? 'true' : 'false'
			);

			content.hidden = !nextExpanded;

			const label = button.querySelector(
				'.tutorhelper-booking-type-list-toggle__label'
			);

			const icon = button.querySelector(
				'.dashicons'
			);

			if (label) {
				label.textContent = nextExpanded
					? button.dataset.hideLabel
					: button.dataset.showLabel;
			}

			if (icon) {
				icon.classList.toggle(
					'dashicons-arrow-up-alt2',
					nextExpanded
				);

				icon.classList.toggle(
					'dashicons-arrow-down-alt2',
					!nextExpanded
				);
			}
		});
	}

	/**
	 * Allow active Booking Types to be reordered by dragging.
	 */
	function initialiseBookingTypeSorting() {
		const list = document.querySelector(
			'#tutorhelper-booking-type-list.is-sortable'
		);

		const status = document.getElementById(
			'tutorhelper-booking-type-order-status'
		);

		if (!list) {

			return;
		}

		if (typeof window.jQuery === 'undefined') {
			console.warn(
				'TutorHelper: jQuery is not available.'
			);

			return;
		}

		const $ = window.jQuery;

		if (typeof $.fn.sortable !== 'function') {
			console.warn(
				'TutorHelper: jQuery UI Sortable is not available.'
			);

			return;
		}

		if (
			typeof tutorhelperBookingHub === 'undefined'
			|| !tutorhelperBookingHub.bookingTypeOrder
		) {
			console.warn(
				'TutorHelper: Booking Type reorder settings are unavailable.'
			);

			return;
		}



		$(list).sortable({
			items: '> .tutorhelper-booking-type-item',
			handle: '.tutorhelper-booking-type-item__drag',

			cancel:
				'input, textarea, select, option, '
				+ '.tutorhelper-booking-type-item__edit, '
				+ '.tutorhelper-booking-type-item__status',

			axis: 'y',
			tolerance: 'pointer',
			forcePlaceholderSize: true,
			forceHelperSize: true,
			placeholder:
				'tutorhelper-booking-type-item__placeholder',

			start: function (event, ui) {
				ui.placeholder.height(
					ui.item.outerHeight()
				);
			},

			update: function () {
				const ids = [];

				list.querySelectorAll(
					'.tutorhelper-booking-type-item'
				).forEach(function (item) {
					const id = parseInt(
						item.dataset.bookingTypeId,
						10
					);

					if (id > 0) {
						ids.push(id);
					}
				});

				if (status) {
					status.textContent =
						tutorhelperBookingHub
							.bookingTypeOrder
							.saving;

					status.classList.remove('is-error');
					status.classList.add('is-saving');
				}

				$.ajax({
					url: tutorhelperBookingHub.ajaxUrl,
					method: 'POST',

					data: {
						action:
							tutorhelperBookingHub
								.bookingTypeOrder
								.action,

						nonce:
							tutorhelperBookingHub
								.bookingTypeOrder
								.nonce,

						booking_type_ids: ids
					}
				})
					.done(function (response) {
						if (
							!response
							|| !response.success
						) {
							const message =
								response
								&& response.data
								&& response.data.message
									? response.data.message
									: tutorhelperBookingHub
										.bookingTypeOrder
										.failed;

							if (status) {
								status.textContent = message;
								status.classList.add(
									'is-error'
								);
							}

							return;
						}

						if (status) {
							status.textContent =
								response.data.message
								|| tutorhelperBookingHub
									.bookingTypeOrder
									.saved;

							status.classList.remove(
								'is-error',
								'is-saving'
							);

							status.classList.add(
								'is-saved'
							);
						}
					})
					.fail(function (request) {
						let message =
							tutorhelperBookingHub
								.bookingTypeOrder
								.failed;

						if (
							request.responseJSON
							&& request.responseJSON.data
							&& request.responseJSON
								.data.message
						) {
							message =
								request.responseJSON
									.data.message;
						}

						if (status) {
							status.textContent = message;

							status.classList.remove(
								'is-saving'
							);

							status.classList.add(
								'is-error'
							);
						}
					});
			}
		});

	}

	/**
	 * Initialise AJAX searching for linked Parent Accounts.
	 */
	function initialiseParentAccountSearch() {

		const wrapper = document.querySelector(
			'.tutorhelper-user-search'
		);

		if (!wrapper) {

			return;
		}

		if (
			typeof tutorhelperBookingHub === 'undefined'
			|| !tutorhelperBookingHub.parentAccountSearch
		) {
			if (typeof tutorhelperBookingHub === 'undefined'){
				console.log(
				'TutorHelper: no booking hub'
			);
			}
			if (!tutorhelperBookingHub.parentAccountSearch){
				console.log(
				'TutorHelper: No parent account search'
			);
			}  
			
			return;
		}

		if (typeof window.jQuery === 'undefined') {

			return;
		}

		const $ = window.jQuery;

		const settings =
			tutorhelperBookingHub.parentAccountSearch;

		const minimumCharacters = Math.max(
			2,
			parseInt(
				settings.minimumCharacters,
				10
			) || 2
		);

		const input = wrapper.querySelector(
			'.tutorhelper-user-search__input'
		);

		const valueInput = wrapper.querySelector(
			'.tutorhelper-user-search__value'
		);

		const results = wrapper.querySelector(
			'.tutorhelper-user-search__results'
		);

		const statusElement = wrapper.querySelector(
			'.tutorhelper-user-search__status'
		);

		const selection = wrapper.querySelector(
			'.tutorhelper-user-search__selection'
		);

		const spinner = wrapper.querySelector(
			'.tutorhelper-user-search__spinner'
		);

		const overrideCheckbox = document.querySelector(
			'.tutorhelper-use-linked-parent'
		);

		if (
			!input
			|| !valueInput
			|| !results
			|| !selection
		) {
			console.warn(
				'TutorHelper: Parent Account search markup is incomplete.',
				{
					input: input,
					valueInput: valueInput,
					results: results,
					selection: selection
				}
			);

			return;
		}

		let searchTimer = null;
		let searchRequest = null;

		/**
		 * Return safely escaped text for generated HTML.
		 *
		 * @param {string} value Text to escape.
		 *
		 * @returns {string}
		 */
		function escapeHtml(value) {
			const element = document.createElement('div');

			element.textContent = value || '';

			return element.innerHTML;
		}

		/**
		 * Update the accessible search status.
		 *
		 * @param {string} message Status text.
		 * @param {boolean} isError Whether this is an error.
		 */
		function setStatus(message, isError) {
			if (!statusElement) {
				return;
			}

			statusElement.textContent = message || '';

			statusElement.classList.toggle(
				'is-error',
				Boolean(isError)
			);
		}

		/**
		 * Close and clear the search results.
		 */
		function closeResults() {
			results.hidden = true;
			results.innerHTML = '';
		}

		/**
		 * Enable the override checkbox only when a parent is selected.
		 */
		function updateOverrideState() {
			if (!overrideCheckbox) {
				return;
			}

			const parentUserId = parseInt(
				valueInput.value,
				10
			);

			const hasParent =
				Number.isInteger(parentUserId)
				&& parentUserId > 0;

			overrideCheckbox.disabled = !hasParent;

			if (!hasParent) {
				overrideCheckbox.checked = false;
			}
		}

		/**
		 * Show the empty linked-account state.
		 */
		function renderEmptySelection() {
			selection.innerHTML =
				'<p class="tutorhelper-user-search__empty">'
				+ 'No Parent Account selected.'
				+ '</p>';
		}

		/**
		 * Show the selected Parent Account.
		 *
		 * @param {Object} user Selected user data.
		 */
		function renderSelection(user) {
			selection.innerHTML =
				'<div class="tutorhelper-user-search__selected">'
				+ '<div>'
				+ '<strong>'
				+ escapeHtml(user.name)
				+ '</strong>'
				+ '<span>'
				+ escapeHtml(user.email)
				+ '</span>'
				+ '</div>'
				+ '<button '
				+ 'type="button" '
				+ 'class="button '
				+ 'tutorhelper-hub-pill-button-small '
				+ 'tutorhelper-user-search__remove">'
				+ 'Remove link'
				+ '</button>'
				+ '</div>';
		}

		/**
		 * Select one returned WordPress user.
		 *
		 * @param {Object} user Selected user data.
		 */
		function selectUser(user) {
			valueInput.value = String(user.id);
			input.value = '';

			renderSelection(user);
			closeResults();
			updateOverrideState();

			setStatus(
				'Parent Account selected.',
				false
			);
		}

		/**
		 * Remove the currently selected Parent Account.
		 */
		function removeSelection() {
			valueInput.value = '0';
			input.value = '';

			renderEmptySelection();
			closeResults();
			updateOverrideState();

			setStatus(
				'Parent Account link will be removed when you save.',
				false
			);

			input.focus();
		}

		/**
		 * Render returned Parent Account candidates.
		 *
		 * @param {Object[]} users User search results.
		 */
		function renderResults(users) {
			results.innerHTML = '';

			if (!Array.isArray(users) || users.length === 0) {
				results.innerHTML =
					'<p class="tutorhelper-user-search__message">'
					+ escapeHtml(
						settings.noResults
						|| 'No matching user accounts found.'
					)
					+ '</p>';

				results.hidden = false;

				return;
			}

			users.forEach(function (user) {
				const button = document.createElement(
					'button'
				);

				button.type = 'button';

				button.className =
					'tutorhelper-user-search__result';

				button.setAttribute(
					'role',
					'option'
				);

				let roleText = '';

				if (user.is_parent) {
					roleText =
						' · '
						+ escapeHtml(
							settings.parentRole
							|| 'TH Parent'
						);
				}

				button.innerHTML =
					'<strong>'
					+ escapeHtml(user.name)
					+ '</strong>'
					+ '<span>'
					+ escapeHtml(user.email)
					+ '</span>';

				button.addEventListener(
					'click',
					function () {
						selectUser(user);
					}
				);

				results.appendChild(button);
			});

			results.hidden = false;
		}

		/**
		 * Search WordPress users through admin-ajax.php.
		 *
		 * @param {string} searchTerm Search text.
		 */
		function searchUsers(searchTerm) {
			if (
				searchRequest
				&& searchRequest.readyState !== 4
			) {
				searchRequest.abort();
			}

			if (spinner) {
				spinner.classList.add('is-active');
			}

			setStatus(
				settings.searching
				|| 'Searching user accounts…',
				false
			);

			searchRequest = $.ajax({
				url: tutorhelperBookingHub.ajaxUrl,
				method: 'GET',
				dataType: 'json',

				data: {
					action: settings.action,
					nonce: settings.nonce,
					search: searchTerm,
					student_user_id:
						wrapper.dataset.studentUserId
						|| '0'
				}
			})
				.done(function (response) {
					if (
						!response
						|| !response.success
						|| !response.data
					) {
						const message =
							response
							&& response.data
							&& response.data.message
								? response.data.message
								: (
									settings.failed
									|| 'User accounts could not be searched.'
								);

						setStatus(message, true);

						results.innerHTML =
							'<p class="'
							+ 'tutorhelper-user-search__message '
							+ 'is-error">'
							+ escapeHtml(message)
							+ '</p>';

						results.hidden = false;

						return;
					}

					const users =
						response.data.results || [];

					setStatus(
						users.length > 0
							? users.length
								+ (
									users.length === 1
										? ' account found.'
										: ' accounts found.'
								)
							: (
								settings.noResults
								|| 'No matching user accounts found.'
							),
						false
					);

					renderResults(users);
				})
				.fail(function (
					request,
					requestStatus,
					errorThrown
				) {
					if (requestStatus === 'abort') {
						return;
					}

					let message =
						settings.failed
						|| 'User accounts could not be searched.';

					if (
						request.responseJSON
						&& request.responseJSON.data
						&& request.responseJSON.data.message
					) {
						message =
							request.responseJSON.data.message;
					} else if (errorThrown) {
						message += ' ' + errorThrown;
					}

					setStatus(message, true);

					results.innerHTML =
						'<p class="'
						+ 'tutorhelper-user-search__message '
						+ 'is-error">'
						+ escapeHtml(message)
						+ '</p>';

					results.hidden = false;
				})
				.always(function () {
					if (spinner) {
						spinner.classList.remove(
							'is-active'
						);
					}
				});
		}

		input.addEventListener(
			'input',
			function () {
				window.clearTimeout(searchTimer);

				const searchTerm =
					input.value.trim();

				if (
					searchTerm.length
					< minimumCharacters
				) {
					if (
						searchRequest
						&& searchRequest.readyState !== 4
					) {
						searchRequest.abort();
					}

					closeResults();

					if (spinner) {
						spinner.classList.remove(
							'is-active'
						);
					}

					setStatus(
						'Enter at least '
						+ minimumCharacters
						+ ' characters.',
						false
					);

					return;
				}

				searchTimer = window.setTimeout(
					function () {
						searchUsers(searchTerm);
					},
					300
				);
			}
		);

		input.addEventListener(
			'keydown',
			function (event) {
				if (event.key !== 'Escape') {
					return;
				}

				closeResults();
			}
		);

		selection.addEventListener(
			'click',
			function (event) {
				const removeButton =
					event.target.closest(
						'.tutorhelper-user-search__remove'
					);

				if (!removeButton) {
					return;
				}

				removeSelection();
			}
		);

		document.addEventListener(
			'click',
			function (event) {
				if (!wrapper.contains(event.target)) {
					closeResults();
				}
			}
		);

		updateOverrideState();
	}



	


	/**
	 * Collect the Working day form values.
	 *
	 * @returns {Object}
	 */
	function getWorkingDayData() {
		if (!workingDayFields) {
			return {};
		}

		const getCheckedValues = function (name) {
			return Array.from(
				workingDayFields.querySelectorAll(
					'input[name="' + name + '"]:checked'
				)
			).map(function (input) {
				return input.value;
			});
		};

		return {
			weekdays: getCheckedValues(
				'weekdays[]'
			),
			startTime:
				workingDayFields.querySelector(
					'[name="start_time"]'
				).value,
			endTime:
				workingDayFields.querySelector(
					'[name="end_time"]'
				).value,
			locationTypes: getCheckedValues(
				'location_types[]'
			),
			primaryLocation:
				workingDayFields.querySelector(
					'[name="location_type"]'
				).value,
			locationDetails:
				workingDayFields.querySelector(
					'[name="location_details"]'
				).value,
			bookingTypeIds: getCheckedValues(
				'allowed_booking_type_ids[]'
			)
		};
	}


	/**
	 * Initialise live proposed-booking condition checks.
	 */
	function initialiseBookingConditionChecks() {
		const form = document.querySelector(
			'[data-booking-condition-form]'
		);

		const bookingStatusField = form.querySelector(
			'select[name="status"]'
		);

		const cancelledStatus =
			form.dataset.cancelledStatus || 'cancelled';

		if (!form) {
			return;
		}

		const fields = Array.from(
			form.querySelectorAll(
				'[data-booking-condition-field]'
			)
		);

		const timezoneField = form.querySelector(
			'[data-booking-condition-timezone]'
		);

		const statusElement = form.querySelector(
			'[data-booking-condition-status]'
		);

		const region = form.querySelector(
			'[data-booking-condition-region]'
		);

		const heading = form.querySelector(
			'[data-booking-condition-heading]'
		);

		const summary = form.querySelector(
			'[data-booking-condition-summary]'
		);

		const list = form.querySelector(
			'[data-booking-condition-list]'
		);

		const icon = form.querySelector(
			'[data-booking-condition-icon]'
		);

		const submitButton = form.querySelector(
			'[data-booking-condition-submit]'
		);

		if (
			fields.length === 0
			|| !statusElement
			|| !region
			|| !heading
			|| !summary
			|| !list
		) {
			console.warn('TutorHelper: booking condition markup is incomplete.');

			return;
		}

		const ajaxUrl =
			form.dataset.conditionAjaxUrl || '';

		const action =
			form.dataset.conditionAction || '';

		const nonce =
			form.dataset.conditionNonce || '';

		const bookingId =
			form.dataset.conditionBookingId || '0';

		if (
			ajaxUrl === ''
			|| action === ''
			|| nonce === ''
		) {
			console.warn(
				'TutorHelper: booking condition AJAX configuration is incomplete.'
			);

			return;
		}

		const labels = {
			checking:
				form.dataset.conditionLabelChecking
				|| 'Checking booking availability…',

			clear:
				form.dataset.conditionLabelClear
				|| 'No booking warnings were found.',

			warning:
				form.dataset.conditionLabelWarning
				|| 'This booking can be saved, but please review the following warning.',

			warnings:
				form.dataset.conditionLabelWarnings
				|| 'This booking can be saved, but please review the following warnings.',

			blocked:
				form.dataset.conditionLabelBlocked
				|| 'This booking cannot currently be saved.',

			failed:
				form.dataset.conditionLabelFailed
				|| 'Booking checks could not be completed.',
			
			headingClear:
				form.dataset.conditionHeadingClear
				|| 'Booking checks complete',

			headingWarning:
				form.dataset.conditionHeadingWarning
				|| 'Booking warnings',

			headingBlocked:
				form.dataset.conditionHeadingBlocked
				|| 'Booking cannot be saved',
		};

		const originalSubmitDisabled =
			submitButton
				? submitButton.disabled
				: false;

		let debounceTimer = null;
		let requestController = null;
		let requestNumber = 0;

		/**
		 * Read one form field.
		 *
		 * @param {string} name Field name.
		 *
		 * @returns {string}
		 */
		function getFieldValue(name) {
			const field = form.elements.namedItem(name);

			if (!field || typeof field.value !== 'string') {
				return '';
			}

			return field.value.trim();
		}

		/**
		 * Return the current condition-check values.
		 *
		 * @returns {Object}
		 */
		function getRequestValues() {
			return {
				bookingTypeId:
					getFieldValue(
						'booking_type_id'
					),

				bookingDate:
					getFieldValue(
						'booking_date'
					),

				startTime:
					getFieldValue(
						'start_time'
					),

				endTime:
					getFieldValue(
						'end_time'
					),

				timezone:
					timezoneField
						? timezoneField.value.trim()
						: getFieldValue(
							'timezone'
						),

				locationType:
					getFieldValue(
						'location_type'
					),

				excludeBookingId:
					bookingId
			};
		}

		/**
		 * Determine whether all required checking values are present.
		 *
		 * @param {Object} values Form values.
		 *
		 * @returns {boolean}
		 */
		function isComplete(values) {
			return Boolean(
				values.bookingTypeId
				&& values.bookingDate
				&& values.startTime
				&& values.endTime
				&& values.locationType
			);
		}

		/**
		 * Update the accessible checking status.
		 *
		 * @param {string} message Status message.
		 * @param {boolean} isError Whether the status is an error.
		 */
		function setStatus(message, isError) {
			statusElement.textContent =
				message || '';

			statusElement.classList.toggle(
				'is-error',
				Boolean(isError)
			);

			statusElement.classList.toggle(
				'is-checking',
				message === labels.checking
			);
		}

		/**
		 * Restore the submit button to its original state.
		 */
		function restoreSubmitState() {
			if (!submitButton) {
				return;
			}

			submitButton.disabled =
				originalSubmitDisabled;

			submitButton.removeAttribute(
				'aria-describedby'
			);
		}

		/**
		 * Update submit state from the evaluation.
		 *
		 * @param {boolean} canTutorSave Whether the evaluator permits saving.
		 */
		function updateSubmitState(canTutorSave) {
			if (!submitButton) {
				return;
			}

			submitButton.disabled =
				originalSubmitDisabled
				|| !canTutorSave;

			if (!canTutorSave) {
				submitButton.setAttribute(
					'aria-describedby',
					'tutorhelper-booking-conditions-heading'
				);
			} else {
				submitButton.removeAttribute(
					'aria-describedby'
				);
			}
		}

		/**
		 * Hide and reset the condition result.
		 */
		function clearResult() {
			region.hidden = true;

			region.classList.remove(
				'is-clear',
				'has-warnings',
				'has-blocking-conditions'
			);

			summary.textContent = '';
			list.innerHTML = '';

			restoreSubmitState();
		}

		/**
		 * Set the region's semantic visual state.
		 *
		 * @param {string} state clear, warning or blocking.
		 */
		function setRegionState(state) {
			region.classList.toggle(
				'is-clear',
				state === 'clear'
			);

			region.classList.toggle(
				'has-warnings',
				state === 'warning'
			);

			region.classList.toggle(
				'has-blocking-conditions',
				state === 'blocking'
			);

			if (!icon) {
				return;
			}

			icon.classList.remove(
				'dashicons-info-outline',
				'dashicons-warning',
				'dashicons-yes-alt',
				'dashicons-dismiss'
			);

			if (state === 'blocking') {
				icon.classList.add(
					'dashicons-dismiss'
				);
			} else if (state === 'warning') {
				icon.classList.add(
					'dashicons-warning'
				);
			} else {
				icon.classList.add(
					'dashicons-yes-alt'
				);
			}
		}

		/**
		 * Render returned booking conditions.
		 *
		 * @param {Object} data AJAX response data.
		 */
		function renderResult(data) {
			const conditions =
				Array.isArray(data.conditions)
					? data.conditions
					: [];

			const blockingConditions =
				conditions.filter(
					function (condition) {
						return condition
							&& condition.severity
								=== 'blocking';
					}
				);

			const warningConditions =
				conditions.filter(
					function (condition) {
						return condition
							&& condition.severity
								=== 'warning';
					}
				);

			list.innerHTML = '';

			conditions.forEach(
				function (condition) {
					if (
						!condition
						|| typeof condition.message
							!== 'string'
						|| condition.message === ''
					) {
						return;
					}

					const item =
						document.createElement(
							'li'
						);

					item.className =
						'tutorhelper-booking-conditions__item';

					item.classList.toggle(
						'is-warning',
						condition.severity
							=== 'warning'
					);

					item.classList.toggle(
						'is-blocking',
						condition.severity
							=== 'blocking'
					);

					const itemIcon =
						document.createElement(
							'span'
						);

					itemIcon.className =
						'dashicons '
						+ (
							condition.severity
								=== 'blocking'
								? 'dashicons-dismiss'
								: 'dashicons-warning'
						);

					itemIcon.setAttribute(
						'aria-hidden',
						'true'
					);

					const message =
						document.createElement(
							'span'
						);

					message.textContent =
						condition.message;

					item.appendChild(
						itemIcon
					);

					item.appendChild(
						message
					);

					list.appendChild(
						item
					);
				}
			);

			if (
				blockingConditions.length > 0
				|| data.canTutorSave === false
			) {
				heading.textContent =
					labels.headingBlocked;

				summary.textContent =
					labels.blocked;

				setRegionState(
					'blocking'
				);
			} else if (
				warningConditions.length > 0
			) {
				heading.textContent =
					labels.headingWarning;

				summary.textContent =
					warningConditions.length === 1
						? labels.warning
						: labels.warnings;

				setRegionState(
					'warning'
				);
			} else {
				heading.textContent =
					labels.headingClear;

				summary.textContent =
					labels.clear;

				setRegionState(
					'clear'
				);
			}

			/*
			* A clear result does not need an empty bullet list.
			*/
			list.hidden =
				conditions.length === 0;

			region.hidden = false;

			updateSubmitState(
				data.canTutorSave !== false
			);
		}

		/**
		 * Handle an unsuccessful condition request.
		 *
		 * The save button is restored because AJAX is advisory. The
		 * authoritative server-side decision will be added in 3D-B3.
		 *
		 * @param {string} message Error message.
		 */
		function renderFailure(message) {
			clearResult();

			setStatus(
				message || labels.failed,
				true
			);
		}

		/**
		 * Send the proposed booking for evaluation.
		 */
		function checkConditions() {
			if (
				bookingStatusField
				&& bookingStatusField.value
					=== cancelledStatus
			) {
				if (requestController) {
					requestController.abort();
					requestController = null;
				}

				setStatus('', false);
				clearResult();

				return;
			}


			const values =
				getRequestValues();

			if (!isComplete(values)) {
				if (requestController) {
					requestController.abort();
					requestController = null;
				}

				setStatus('', false);
				clearResult();

				return;
			}

			if (requestController) {
				requestController.abort();
			}

			requestController =
				new AbortController();

			requestNumber += 1;

			const thisRequestNumber =
				requestNumber;

			const body =
				new URLSearchParams();

			body.set(
				'action',
				action
			);

			body.set(
				'nonce',
				nonce
			);

			body.set(
				'booking_type_id',
				values.bookingTypeId
			);

			body.set(
				'booking_date',
				values.bookingDate
			);

			body.set(
				'start_time',
				values.startTime
			);

			body.set(
				'end_time',
				values.endTime
			);

			body.set(
				'timezone',
				values.timezone
			);

			body.set(
				'location_type',
				values.locationType
			);

			body.set(
				'exclude_booking_id',
				values.excludeBookingId
			);

			setStatus(
				labels.checking,
				false
			);

			fetch(
				ajaxUrl,
				{
					method: 'POST',

					credentials:
						'same-origin',

					headers: {
						'Content-Type':
							'application/x-www-form-urlencoded; charset=UTF-8'
					},

					body:
						body.toString(),

					signal:
						requestController.signal
				}
			)
				.then(function (response) {
					return response
						.json()
						.then(function (payload) {
							return {
								ok:
									response.ok,

								payload:
									payload
							};
						});
				})
				.then(function (result) {
					if (
						thisRequestNumber
							!== requestNumber
					) {
						return;
					}

					const response =
						result.payload;

					if (
						!result.ok
						|| !response
						|| !response.success
						|| !response.data
					) {
						const message =
							response
							&& response.data
							&& response.data.message
								? response.data.message
								: labels.failed;

						renderFailure(
							message
						);

						return;
					}

					if (
						response.data.complete
							=== false
					) {
						setStatus('', false);
						clearResult();

						return;
					}

					setStatus('', false);

					renderResult(
						response.data
					);
				})
				.catch(function (error) {
					if (
						error
						&& error.name
							=== 'AbortError'
					) {
						return;
					}

					if (
						thisRequestNumber
							!== requestNumber
					) {
						return;
					}

					renderFailure(
						labels.failed
					);
				})
				.finally(function () {
					if (
						thisRequestNumber
							=== requestNumber
					) {
						requestController =
							null;
					}
				});
		}

		/**
		 * Debounce a condition check after a field change.
		 */
		function scheduleCheck() {
			window.clearTimeout(
				debounceTimer
			);

			debounceTimer =
				window.setTimeout(
					checkConditions,
					350
				);
		}

		fields.forEach(function (field) {
			field.addEventListener(
				'change',
				scheduleCheck
			);

			/*
			* Date and time inputs can update through keyboard input before
			* their change event fires.
			*/
			if (
				field.matches(
					'input[type="date"], input[type="time"]'
				)
			) {
				field.addEventListener(
					'input',
					scheduleCheck
				);
			}
		});

		// Allows the cancelled status to reset if tutor backs out on prompt
		if (bookingStatusField) {
			bookingStatusField.addEventListener(
				'change',
				scheduleCheck
			);
		}

		/*
		* Edit mode and pre-populated create forms should be checked when
		* they first load.
		*/
		scheduleCheck();
	}


	/**
	 * Initialise tutor booking cancellation controls.
	 */
	function initialiseTutorBookingCancellation() {
		const form = document.querySelector(
			'[data-booking-condition-form]'
		);

		if (!form || form.dataset.mode !== 'edit') {
			return;
		}

		const statusField = form.querySelector(
			'select[name="status"]'
		);

		const reasonField = form.querySelector(
			'[data-booking-cancellation-reason]'
		);

		const reasonInput = form.querySelector(
			'[data-booking-cancellation-reason-input]'
		);

		const cancelledStatus =
			form.dataset.cancelledStatus || 'cancelled';

		if (
			!statusField
			|| !reasonField
			|| !reasonInput
		) {
			return;
		}

		function updateCancellationState() {
			const isCancelled =
				statusField.value === cancelledStatus;

			reasonField.hidden = !isCancelled;
			reasonInput.disabled = !isCancelled;
			reasonInput.required = isCancelled;
		}

		statusField.addEventListener(
			'change',
			updateCancellationState
		);

		updateCancellationState();
	}



	/**
	 * Run one component initialiser without preventing other
	 * Booking Hub components from loading.
	 *
	 * @param {Function} callback Initialiser function.
	 * @param {string} name Component name for debugging.
	 */
	function safelyInitialise(callback, name) {
		try {
			callback();
		} catch (error) {
			console.error(
				'TutorHelper: failed to initialise '
				+ name
				+ '.',
				error
			);
		}
	}

	/**
	 * Initialise components used by the current Booking Hub tab.
	 */
	function initialiseBookingHub() {
		/*
		 * Parent Account searching is placed first while it is
		 * being tested. Each function exits silently when its
		 * corresponding markup is absent.
		 */
		safelyInitialise(
			initialiseParentAccountSearch,
			'Parent Account search'
		);

		safelyInitialise(
			initialiseHubPanels,
			'Hub panels'
		);

		safelyInitialise(
			initialiseSingleDateWeekday,
			'single-date weekday selection'
		);

		safelyInitialise(
			initialiseAllowedBookingTypePicker,
			'allowed Booking Type picker'
		);

		safelyInitialise(
			initialiseAvailabilityEditScope,
			'availability edit scope'
		);

		safelyInitialise(
			initialiseAvailabilityRemoval,
			'availability removal'
		);

		safelyInitialise(
			initialiseBookingTypeStatusActions,
			'Booking Type status actions'
		);

		safelyInitialise(
			initialiseBookingTypeListToggle,
			'Booking Type list toggle'
		);

		safelyInitialise(
			initialiseBookingTypeSorting,
			'Booking Type sorting'
		);

		safelyInitialise(
			initialiseTutorBookingCancellation,
			'tutor booking cancellation'
		);

		safelyInitialise(
			initialiseBookingConditionChecks,
			'booking condition checks'
		);
	}

	if (document.readyState === 'loading') {
		document.addEventListener(
			'DOMContentLoaded',
			initialiseBookingHub
		);
	} else {
		initialiseBookingHub();
	}
})();

