<?php
/**
 * TutorHelper Booking Hub.
 *
 * @package TutorHelper_Bookings
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Provides the central TutorHelper Booking Hub administration page.
 */
final class TutorHelper_Booking_Hub {

	/**
	 * Booking Hub admin page slug.
	 *
	 * @var string
	 */
	const PAGE_SLUG = 'tutorhelper';


	/**
	 * Default Booking Hub tab.
	 *
	 * @var string
	 */
	const DEFAULT_TAB = 'calendar';


	/**
	 * Capability required to access the Booking Hub.
	 *
	 * A dedicated capability can replace this later.
	 *
	 * @var string
	 */
	const CAPABILITY = 'manage_options';

	/**
	 * Register WordPress hooks.
	 *
	 * @return void
	 */

	/**
	 * User-meta key storing the preferred calendar view.
	 *
	 * @var string
	 */
	const CALENDAR_VIEW_META_KEY =
		'tutorhelper_booking_calendar_view';


	public static function init() {
		add_action(
			'admin_menu',
			array( __CLASS__, 'register_menu' )
		);

		add_action(
			'admin_enqueue_scripts',
			array( __CLASS__, 'enqueue_assets' )
		);
	}

	/**
     * Register the Booking Hub under the shared TutorHelper menu.
     *
     * @return void
     */
    public static function register_menu() {
        add_submenu_page(
            TutorHelper_Admin::MENU_SLUG,
            __(
                'Booking Hub',
                'tutorhelper-bookings'
            ),
            __(
                'Booking Hub',
                'tutorhelper-bookings'
            ),
            TutorHelper_Admin::CAPABILITY,
            TutorHelper_Admin::MENU_SLUG,
            array( __CLASS__, 'render_page' )
        );
    }


	/**
     * Load Booking Hub assets only on Booking Hub pages.
     *
     * @param string $hook_suffix Current WordPress admin page hook.
     *
     * @return void
     */
    public static function enqueue_assets( $hook_suffix ) {
        if (
            'toplevel_page_' . TutorHelper_Admin::MENU_SLUG
            !== $hook_suffix
        ) {
            return;
        }

        $css_path = TUTORHELPER_BOOKINGS_PATH
            . 'assets/css/booking-hub.css';

		$script_path = TUTORHELPER_BOOKINGS_PATH
			. 'assets/js/booking-hub.js';

        wp_enqueue_style(
            'tutorhelper-booking-hub',
            TUTORHELPER_BOOKINGS_URL
                . 'assets/css/booking-hub.css',
            array(),
            file_exists( $css_path )
                ? (string) filemtime( $css_path )
                : TUTORHELPER_BOOKINGS_VERSION
			
        );



		wp_enqueue_script(
			'tutorhelper-booking-hub',
			TUTORHELPER_BOOKINGS_URL
				. 'assets/js/booking-hub.js',
			array(
				'jquery',
				'jquery-ui-sortable',
			),
			file_exists( $script_path )
				? (string) filemtime( $script_path )
				: TUTORHELPER_BOOKINGS_VERSION,
			true
		);

		$selected_tab = isset( $_GET['tab'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request state used for navigation or feedback; value is sanitized and no state change occurs.
			? sanitize_key(
				wp_unslash(
					$_GET['tab'] // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request state used for navigation or feedback; value is sanitized and no state change occurs.
				)
			)
			: self::DEFAULT_TAB;


		if ( 'calendar' === $selected_tab ) {
			$calendar_css_path =
				TUTORHELPER_BOOKINGS_PATH
				. 'assets/css/admin-calendar.css';

			wp_enqueue_style(
				'tutorhelper-admin-calendar',
				TUTORHELPER_BOOKINGS_URL
					. 'assets/css/admin-calendar.css',
				array(
					'tutorhelper-booking-hub',
				),
				file_exists(
					$calendar_css_path
				)
					? (string) filemtime(
						$calendar_css_path
					)
					: TUTORHELPER_BOOKINGS_VERSION
			);

			$calendar_script_path =
				TUTORHELPER_BOOKINGS_PATH
				. 'assets/js/admin-calendar.js';

			wp_enqueue_script(
				'tutorhelper-admin-calendar',
				TUTORHELPER_BOOKINGS_URL
					. 'assets/js/admin-calendar.js',
				array(),
				file_exists(
					$calendar_script_path
				)
					? (string) filemtime(
						$calendar_script_path
					)
					: TUTORHELPER_BOOKINGS_VERSION,
				true
			);


			wp_localize_script(
				'tutorhelper-admin-calendar',
				'tutorhelperAdminCalendar',
				array(
					'sidebar' => array(
						'showLabel' => __(
							'Show side panel',
							'tutorhelper-bookings'
						),

						'hideLabel' => __(
							'Hide side panel',
							'tutorhelper-bookings'
						),
					),
				)
			);

		}


		if ( 'availability' === $selected_tab ) {
			$planner_script_path =
				TUTORHELPER_BOOKINGS_PATH
				. 'assets/js/availability-planner.js';

			wp_enqueue_script(
				'tutorhelper-availability-planner',
				TUTORHELPER_BOOKINGS_URL
					. 'assets/js/availability-planner.js',
				array('tutorhelper-booking-hub',),
				file_exists( $planner_script_path )
					? (string) filemtime(
						$planner_script_path
					)
					: TUTORHELPER_BOOKINGS_VERSION,
				true
			);

			wp_localize_script(
				'tutorhelper-availability-planner',
				'tutorhelperAvailabilityPlanner',
				array(
					'ajaxUrl' =>
						admin_url(
							'admin-ajax.php'
						),

					'loadItemAction' =>
						TutorHelper_Availability_Calendar_Admin::
							LOAD_ITEM_ACTION,

					'loadItemNonce' =>
						wp_create_nonce(
							TutorHelper_Availability_Calendar_Admin::
								LOAD_ITEM_ACTION
						),

					'calendarEditUrl' =>
						add_query_arg(
							array(
								'page' =>
									TutorHelper_Booking_Hub::
										PAGE_SLUG,

								'tab' =>
									'calendar',
							),
							admin_url(
								'admin.php'
							)
						),
				)
			);
		}


		if ( 'add-booking' === $selected_tab ) {
			$tutor_booking_css_path =
				TUTORHELPER_BOOKINGS_PATH
				. 'assets/css/admin-tutor-booking.css';

			wp_enqueue_style(
				'tutorhelper-admin-tutor-booking',
				TUTORHELPER_BOOKINGS_URL
					. 'assets/css/admin-tutor-booking.css',
				array(
					'tutorhelper-booking-hub',
				),
				file_exists(
					$tutor_booking_css_path
				)
					? (string) filemtime(
						$tutor_booking_css_path
					)
					: TUTORHELPER_BOOKINGS_VERSION
			);


			$tutor_booking_script_path =
				TUTORHELPER_BOOKINGS_PATH
				. 'assets/js/admin-tutor-booking.js';

			wp_enqueue_script(
				'tutorhelper-admin-tutor-booking',
				TUTORHELPER_BOOKINGS_URL
					. 'assets/js/admin-tutor-booking.js',
				array(
					'jquery',
				),
				file_exists(
					$tutor_booking_script_path
				)
					? (string) filemtime(
						$tutor_booking_script_path
					)
					: TUTORHELPER_BOOKINGS_VERSION,
				true
			);

			$currency =
				TutorHelper_Currency::get_currency();

			$currency_details =
				TutorHelper_Currency::get_currency_details(
					$currency
				);

			wp_localize_script(
				'tutorhelper-admin-tutor-booking',
				'tutorhelperTutorBooking',
				array(
					'ajaxUrl' =>
						admin_url( 'admin-ajax.php' ),

					'studentSearch' => array(
						'action' =>
							TutorHelper_Tutor_Booking_Admin::
								STUDENT_SEARCH_ACTION,

						'nonce' =>
							wp_create_nonce(
								TutorHelper_Tutor_Booking_Admin::
									STUDENT_SEARCH_ACTION
							),

						'minimumCharacters' =>
							2,

						'searchDelay' =>
							300,

						'searching' => __(
							'Searching students…',
							'tutorhelper-bookings'
						),

						'noResults' => __(
							'No matching TutorHelper students found.',
							'tutorhelper-bookings'
						),

						'failed' => __(
							'Students could not be searched.',
							'tutorhelper-bookings'
						),

						'selected' => __(
							'Student selected.',
							'tutorhelper-bookings'
						),

						'selectStudent' => __(
							'Select a student before creating the booking.',
							'tutorhelper-bookings'
						),

						'change' => __(
							'Change student',
							'tutorhelper-bookings'
						),
					),

					'pricePreview' => array(
						'action' =>
							TutorHelper_Tutor_Booking_Admin::
								PRICE_PREVIEW_ACTION,

						'nonce' =>
							wp_create_nonce(
								TutorHelper_Tutor_Booking_Admin::
									PRICE_PREVIEW_NONCE
							),
					),

					'currency' => array(
						'code' =>
							$currency,

						'decimals' =>
							TutorHelper_Currency::
								get_decimal_places(
									$currency
								),

						'symbol' => isset(
							$currency_details['symbol']
						)
							? $currency_details['symbol']
							: $currency,

						'symbolPosition' => isset(
							$currency_details[
								'symbol_position'
							]
						)
							? $currency_details[
								'symbol_position'
							]
							: 'before',
					),

					'labels' => array(
						'minutes' =>
						/* translators: %d: Lesson duration in minutes. */
						__(
							'%d minutes',
							'tutorhelper-bookings'
						),

						'invalidTime' => __(
							'Enter an end time after the start time.',
							'tutorhelper-bookings'
						),
					),
				)
			);

		}

		
        /*
        wp_enqueue_style(
            'tutorhelper-booking-hub',
            TUTORHELPER_BOOKINGS_URL
                . 'assets/css/booking-hub.css',
            array(),
            TUTORHELPER_BOOKINGS_VERSION
        );
        

        wp_enqueue_script(
            'tutorhelper-booking-hub',
            TUTORHELPER_BOOKINGS_URL
                . 'assets/js/booking-hub.js',
            array(
				'jquery',
				'jquery-ui-sortable',
			),
            TUTORHELPER_BOOKINGS_VERSION,
            true
        );
		*/

		wp_localize_script(
			'tutorhelper-booking-hub',
			'tutorhelperBookingHub',
			array(
				'ajaxUrl' => admin_url( 'admin-ajax.php' ),
			)
		);

		wp_add_inline_script(
			'tutorhelper-booking-hub',
			'window.tutorhelperBookingHub = window.tutorhelperBookingHub || {};'
			. 'window.tutorhelperBookingHub.bookingTypeOrder = '
			. wp_json_encode(
				array(
					'action' =>
						TutorHelper_Booking_Types_Admin::REORDER_ACTION,

					'nonce' => wp_create_nonce(
						TutorHelper_Booking_Types_Admin::REORDER_ACTION
					),

					'saving' => __(
						'Saving order…',
						'tutorhelper-bookings'
					),

					'saved' => __(
						'Booking Type order saved.',
						'tutorhelper-bookings'
					),

					'failed' => __(
						'The Booking Type order could not be saved.',
						'tutorhelper-bookings'
					),
				)
			)
			. ';',
			'before'
		);


		wp_add_inline_script(
			'tutorhelper-booking-hub',
			'window.tutorhelperBookingHub = window.tutorhelperBookingHub || {};'
			. 'window.tutorhelperBookingHub.parentAccountSearch = '
			. wp_json_encode(
				array(
					'action' =>
						TutorHelper_Students_Admin::PARENT_SEARCH_ACTION,

					'nonce' => wp_create_nonce(
						TutorHelper_Students_Admin::PARENT_SEARCH_ACTION
					),

					'minimumCharacters' => 2,

					'searching' => __(
						'Searching user accounts…',
						'tutorhelper-bookings'
					),

					'noResults' => __(
						'No matching user accounts found.',
						'tutorhelper-bookings'
					),

					'failed' => __(
						'User accounts could not be searched.',
						'tutorhelper-bookings'
					),

					'parentRole' => __(
						'TH Parent',
						'tutorhelper-bookings'
					),
				)
			)
			. ';',
			'before'
		);


    }



	/**
	 * Return the Booking Hub tabs.
	 *
	 * Status values:
	 * - available: usable in the free plugin.
	 * - coming_soon: planned free functionality.
	 * - pro: supplied by the separate Pro add-on.
	 *
	 * @return array
	 */
	private static function get_tabs() {
		$tabs = array(
			'calendar' => array(
				'label'       => __(
					'Calendar',
					'tutorhelper-bookings'
				),
				'icon'        => 'dashicons-calendar-alt',
				'status'      => 'available',
				'description' => __(
					'Manage availability and view bookings.',
					'tutorhelper-bookings'
				),
			),

			'availability' => array(
				'label'       => __(
					'Availability',
					'tutorhelper-bookings'
				),
				'icon'        => 'dashicons-calendar',
				'status'      => 'available',
				'description' => __(
					'Plan general availability, term dates, leave and public holidays.',
					'tutorhelper-bookings'
				),
			),

			'student-bookings' => array(
				'label'       => __(
					'Student Bookings',
					'tutorhelper-bookings'
				),
				'icon'        => 'dashicons-clipboard',
				'status'      => 'pro',
				'description' => __(
					'Review and manage student bookings.',
					'tutorhelper-bookings'
				),
			),

			'students' => array(
				'label'       => __(
					'Students',
					'tutorhelper-bookings'
				),
				'icon'        => 'dashicons-groups',
				'status'      => 'available',
				'description' => __(
					'Manage students and booking permissions.',
					'tutorhelper-bookings'
				),
			),

			'finance' => array(
				'label'       => __(
					'Finance',
					'tutorhelper-bookings'
				),
				'icon'        => 'dashicons-bank',
				'status'      => 'pro',
				'description' => __(
					'Manage payments, account credit and financial actions.',
					'tutorhelper-bookings'
				),
			),
		);

		/**
		 * Filters the Booking Hub tabs.
		 *
		 * The Pro add-on can use this filter to change a Pro tab to available.
		 *
		 * @param array $tabs Booking Hub tab definitions.
		 */
		return apply_filters(
			'tutorhelper_booking_hub_tabs',
			$tabs
		);
	}


	/**
	 * Return the selected Booking Hub tab.
	 *
	 * @return string
	 */
	private static function get_selected_tab() {
		$tabs = self::get_tabs();

		$selected_tab = isset( $_GET['tab'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request state used for navigation or feedback; value is sanitized and no state change occurs.
			? sanitize_key( wp_unslash( $_GET['tab'] ) ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request state used for navigation or feedback; value is sanitized and no state change occurs.
			: self::DEFAULT_TAB;

		if ( ! isset( $tabs[ $selected_tab ] ) ) {
			return self::DEFAULT_TAB;
		}

		return $selected_tab;
	}


	/**
	 * Return a Booking Hub tab URL.
	 *
	 * @param string $tab  Tab identifier.
	 * @param array  $args Additional URL arguments.
	 *
	 * @return string
	 */
	private static function get_tab_url( $tab, $args = array() ) {
		$query_args = array_merge(
			array(
				'page' => self::PAGE_SLUG,
				'tab'  => sanitize_key( $tab ),
			),
			is_array( $args ) ? $args : array()
		);

		return add_query_arg(
			$query_args,
			admin_url( 'admin.php' )
		);
	}


	/**
	 * Return the UTC boundaries for today in the site timezone.
	 *
	 * Booking records are stored in UTC, while "today" should follow the
	 * WordPress site timezone.
	 *
	 * @return array|WP_Error
	 */
	private static function get_today_utc_range() {
		try {
			$site_timezone = wp_timezone();
			$utc_timezone  = new DateTimeZone( 'UTC' );

			$today_start =
				new DateTimeImmutable(
					'now',
					$site_timezone
				);

			$today_start =
				$today_start->setTime(
					0,
					0,
					0
				);

			$tomorrow_start =
				$today_start->modify(
					'+1 day'
				);
		} catch ( Exception $exception ) {
			return new WP_Error(
				'tutorhelper_booking_hub_today_invalid',
				__(
					'The current booking date could not be calculated.',
					'tutorhelper-bookings'
				)
			);
		}

		return array(
			'start' =>
				$today_start
					->setTimezone(
						$utc_timezone
					)
					->format(
						'Y-m-d H:i:s'
					),

			'end' =>
				$tomorrow_start
					->setTimezone(
						$utc_timezone
					)
					->format(
						'Y-m-d H:i:s'
					),
		);
	}


	/**
	 * Count non-cancelled bookings starting today.
	 *
	 * @return int
	 */
	private static function get_lessons_today_count() {
		global $wpdb;

		$range = self::get_today_utc_range();

		if ( is_wp_error( $range ) ) {
			TutorHelper_Logger::warning(
				'Booking Hub could not calculate the Lessons today range.',
				array(
					'error_code' =>
						$range->get_error_code(),
				)
			);

			return 0;
		}

		$table_name =
			TutorHelper_Booking_Records::
				get_table_name();

		if ( empty( $table_name ) ) {
			return 0;
		}

		return absint(
			$wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Booking Hub summary counts intentionally reflect current booking state rather than cached totals.
				$wpdb->prepare(
					'SELECT COUNT(*)
					FROM %i
					WHERE booking_start >= %s
					AND booking_start < %s
					AND status != %s',
					$table_name,
					$range['start'],
					$range['end'],
					TutorHelper_Booking_Records::STATUS_CANCELLED
				)
			)
		);
	}


	/**
	 * Count all unconfirmed booking requests.
	 *
	 * @return int
	 */
	private static function get_unconfirmed_count() {
		global $wpdb;

		$table_name =
			TutorHelper_Booking_Records::
				get_table_name();

		if ( empty( $table_name ) ) {
			return 0;
		}

		return absint(
			$wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Booking Hub summary counts intentionally reflect current booking state rather than cached totals.
				$wpdb->prepare(
					'SELECT COUNT(*)
					FROM %i
					WHERE status = %s',
					$table_name,
					TutorHelper_Booking_Records::
						STATUS_UNCONFIRMED
				)
			)
		);
	}




	/**
	 * Return the Booking Hub summary cards.
	 *
	 * @return array
	 */
	private static function get_summary_cards() {
		$cards = array(
			'lessons_today' => array(
				'label'  => __(
					'Lessons today',
					'tutorhelper-bookings'
				),
				'icon'   => 'dashicons-calendar-alt',
				'value'  =>
					self::get_lessons_today_count(),
				'status' =>
					'available',
			),

			'upcoming_bookings' => array(
				'label'  => __(
					'Booking Requests',
					'tutorhelper-bookings'
				),
				'icon'   => 'dashicons-clock',
				'value'  =>
					self::get_unconfirmed_count(),
				'status' =>
					'available',
				'description' => __(
					'Unconfirmed lesson requests',
					'tutorhelper-bookings'
				),
			),

			'awaiting_payment' => array(
				'label'  => __(
					'Awaiting payment',
					'tutorhelper-bookings'
				),
				'icon'   => 'dashicons-money-alt',
				'value'  => '—',
				'status' => 'pro',
			),

			'financial_actions' => array(
				'label'  => __(
					'Financial actions',
					'tutorhelper-bookings'
				),
				'icon'   => 'dashicons-chart-line',
				'value'  => '—',
				'status' => 'pro',
			),

			'new_registrations' => array(
				'label'  => __(
					'New registrations',
					'tutorhelper-bookings'
				),
				'icon'   => 'dashicons-groups',
				'value'  => '—',
				'status' => 'pro',
			),
		);

		/**
		 * Filters Booking Hub summary cards.
		 *
		 * Migrated modules and add-ons can replace placeholder values.
		 *
		 * @param array $cards Summary-card definitions.
		 */
		return apply_filters(
			'tutorhelper_booking_hub_summary_cards',
			$cards
		);
	}


	/**
	 * Render the Booking Hub summary cards.
	 *
	 * @return void
	 */
	private static function render_summary_cards() {
		$cards = self::get_summary_cards();

		?>
		<div class="tutorhelper-hub-summary">
			<?php foreach ( $cards as $card ) : ?>
				<?php
				$status = isset( $card['status'] )
					? sanitize_key( $card['status'] )
					: 'available';

				$classes = array(
					'tutorhelper-hub-summary__card',
					'is-' . $status,
				);

				$card_url =
				! empty( $card['url'] )
					? esc_url( $card['url'] )
					: '';
				?>

				<?php if ( $card_url ) : ?>
					<a
						class="<?php echo esc_attr( implode( ' ', $classes ) ); ?>"
						href="<?php echo esc_url( $card_url ); ?>"
					>
				<?php else : ?>
					<div
						class="<?php echo esc_attr( implode( ' ', $classes ) ); ?>"
					>
				<?php endif; ?>
					<span
						class="dashicons <?php echo esc_attr( $card['icon'] ); ?>"
						aria-hidden="true"
					></span>

					<div class="tutorhelper-hub-summary__content">
						<div class="tutorhelper-hub-summary__value">
							<?php echo esc_html( $card['value'] ); ?>

							<?php if ( 'pro' === $status ) : ?>
								<span class="tutorhelper-pro-badge">
									<?php
									echo esc_html__(
										'Pro',
										'tutorhelper-bookings'
									);
									?>
								</span>
							<?php endif; ?>
						</div>

						<div class="tutorhelper-hub-summary__label">
							<?php echo esc_html( $card['label'] ); ?>
						</div>

						<?php if ( ! empty( $card['description'] ) ) : ?>
							<div class="tutorhelper-hub-summary__status">
								<?php
								echo esc_html(
									$card['description']
								);
								?>
							</div>
						<?php endif; ?>

						<?php if (
								empty( $card['description'] )
								&& 'coming_soon' === $status
							) : ?>
							<div class="tutorhelper-hub-summary__status">
								<?php
								echo esc_html__(
									'Coming soon',
									'tutorhelper-bookings'
								);
								?>
							</div>
						<?php elseif ( 'pro' === $status ) : ?>
							<div class="tutorhelper-hub-summary__status">
								<?php
								echo esc_html__(
									'Available with Pro',
									'tutorhelper-bookings'
								);
								?>
							</div>
						<?php endif; ?>
					</div>
				<?php if ( $card_url ) : ?>
					</a>
				<?php else : ?>
					</div>
				<?php endif; ?>
				<?php endforeach; ?>
		</div>
		<?php
	}



	/**
	 * Render the Booking Hub navigation tabs.
	 *
	 * @param string $selected_tab Selected tab identifier.
	 *
	 * @return void
	 */
	private static function render_tabs( $selected_tab ) {
		$tabs = self::get_tabs();

		?>
		<nav
			class="tutorhelper-hub-tabs"
			aria-label="<?php echo esc_attr__( 'Booking Hub sections', 'tutorhelper-bookings' ); ?>"
		>
			<?php foreach ( $tabs as $tab_key => $tab ) : ?>
				<?php
				$status = isset( $tab['status'] )
					? sanitize_key( $tab['status'] )
					: 'available';

				$classes = array(
					'tutorhelper-hub-tabs__tab',
					'is-' . $status,
				);

				if ( $tab_key === $selected_tab ) {
					$classes[] = 'is-active';
				}
				?>

				<a
					class="<?php echo esc_attr( implode( ' ', $classes ) ); ?>"
					href="<?php echo esc_url( self::get_tab_url( $tab_key ) ); ?>"
					<?php echo $tab_key === $selected_tab ? 'aria-current="page"' : ''; ?>
				>
					<span
						class="dashicons <?php echo esc_attr( $tab['icon'] ); ?>"
						aria-hidden="true"
					></span>

					<span>
						<?php echo esc_html( $tab['label'] ); ?>
					</span>

					<?php if ( 'pro' === $status ) : ?>
						<span class="tutorhelper-pro-badge">
							<?php
							echo esc_html__(
								'Pro',
								'tutorhelper-bookings'
							);
							?>
						</span>
					<?php elseif ( 'coming_soon' === $status ) : ?>
						<span class="screen-reader-text">
							<?php
							echo esc_html__(
								'Coming soon',
								'tutorhelper-bookings'
							);
							?>
						</span>
					<?php endif; ?>
				</a>
			<?php endforeach; ?>
		</nav>
		<?php
	}



	/**
	 * Render a placeholder for an upcoming free sections.
	 *
	 * @param array $tab Tab definition.
	 *
	 * @return void
	 */
	private static function render_coming_soon_panel( $tab ) {
		?>
		<section class="tutorhelper-hub-placeholder">
			<span
				class="dashicons <?php echo esc_attr( $tab['icon'] ); ?>"
				aria-hidden="true"
			></span>

			<h2><?php echo esc_html( $tab['label'] ); ?></h2>

			<p><?php echo esc_html( $tab['description'] ); ?></p>

			<span class="tutorhelper-hub-placeholder__label">
				<?php
				echo esc_html__(
					'Coming soon to the free plugin',
					'tutorhelper-bookings'
				);
				?>
			</span>
		</section>
		<?php
	}

	/**
	 * Render a contextual Pro feature panel.
	 *
	 * @param array $tab Tab definition.
	 *
	 * @return void
	 */
	private static function render_pro_panel( $tab ) {
		?>
		<section class="tutorhelper-hub-placeholder is-pro">
			<span
				class="dashicons <?php echo esc_attr( $tab['icon'] ); ?>"
				aria-hidden="true"
			></span>

			<span class="tutorhelper-pro-badge">
				<?php
				echo esc_html__(
					'Pro',
					'tutorhelper-bookings'
				);
				?>
			</span>

			<h2><?php echo esc_html( $tab['label'] ); ?></h2>

			<p><?php echo esc_html( $tab['description'] ); ?></p>

			<p>
				<?php
				echo esc_html__(
					'This section is available through the separate TutorHelper Bookings Pro add-on.',
					'tutorhelper-bookings'
				);
				?>
			</p>

			<?php
			$upgrade_url = apply_filters(
				'tutorhelper_bookings_pro_url',
				''
			);

			if ( ! empty( $upgrade_url ) ) :
				?>
				<a
					class="button button-primary"
					href="<?php echo esc_url( $upgrade_url ); ?>"
					target="_blank"
					rel="noopener noreferrer"
				>
					<?php
					echo esc_html__(
						'View Pro features',
						'tutorhelper-bookings'
					);
					?>
				</a>
			<?php endif; ?>
		</section>
		<?php
	}





	/**
	 * Render the Booking Hub page.
	 *
	 * @return void
	 */
	public static function render_page() {
		if ( ! current_user_can( self::CAPABILITY ) ) {
			return;
		}

		$selected_tab = self::get_selected_tab();
		$tabs         = self::get_tabs();
		$active_tab   = $tabs[ $selected_tab ];
		

		

		?>
		<div class="wrap tutorhelper-booking-hub">
			<?php self::render_hub_heading(); ?>

			<?php self::render_summary_cards(); ?>

			<?php self::render_tabs( $selected_tab ); ?>

			<div class="tutorhelper-hub-workspace">
				<?php
				if ( 'available' === $active_tab['status'] ) {
					if ( 'calendar' === $selected_tab ) {
						self::render_calendar_workspace();
					} elseif ( 'availability' === $selected_tab ) {
						do_action(
							'tutorhelper_booking_hub_render_availability'
						);
					}
					else {
						/**
						 * Allows migrated free modules or add-ons to render a Hub tab.
						 *
						 * @param string $selected_tab Selected tab identifier.
						 * @param array  $active_tab   Tab definition.
						 */
						do_action(
							'tutorhelper_booking_hub_render_tab',
							$selected_tab,
							$active_tab
						);
					}
				} elseif ( 'pro' === $active_tab['status'] ) {
					self::render_pro_panel( $active_tab );
				} else {
					self::render_coming_soon_panel( $active_tab );
				}
				?>
			</div>
		</div>






		<div class="wrap tutorhelper-booking-hub">
			

			
		</div>
		<?php
	}




	/**
	 * Render the Booking Hub Heading.
	 *
	 * @return void
	 */
	public static function render_hub_heading() {
		if ( ! current_user_can( self::CAPABILITY ) ) {
			return;
		}
		?>

			<header class="tutorhelper-booking-hub__heading">
				<img
					src="<?php echo esc_url(
						TUTORHELPER_BOOKINGS_URL
						. 'assets/images/tutorhelper_logo.png'
					); ?>"
					class="tutorhelper-booking-hub__logo"
					alt="<?php echo esc_attr__(
						'TutorHelper',
						'tutorhelper-bookings'
					); ?>"
				/>

				<div class="tutorhelper-booking-hub__heading-content">
					<h1>
						<?php
						echo esc_html__(
							'TutorHelper Booking Hub',
							'tutorhelper-bookings'
						);
						?>
					</h1>

					<p>
						<?php
						echo esc_html__(
							'Manage availability, Booking Types, students and bookings.',
							'tutorhelper-bookings'
						);
						?>
					</p>
				</div>
			</header>

		<?php
	}


	/**
	 * Render the Booking Hub Heading.
	 *
	 * @return void
	 */
	public static function render_calendar_workspace() {
		if ( ! current_user_can( self::CAPABILITY ) ) {
			return;
		}

		$view          = self::get_selected_view();
		$selected_date = self::get_selected_date();

		$previous_url = self::get_previous_url(
			$view,
			$selected_date
		);

		$next_url = self::get_next_url(
			$view,
			$selected_date
		);

		$today_url = self::get_period_url(
			$view,
			new DateTimeImmutable(
				'today',
				wp_timezone()
			)
		);

		$week_url = self::get_period_url(
			'week',
			$selected_date
		);

		$month_url = self::get_period_url(
			'month',
			$selected_date
		);

		?>

		<div class="tutorhelper-booking-hub__toolbar">
				<div class="tutorhelper-booking-hub__period-navigation">
					<a
						class="button"
						href="<?php echo esc_url( $previous_url ); ?>"
					>
						<span aria-hidden="true">&larr;</span>

						<?php
						echo esc_html(
							'month' === $view
								? __(
									'Previous month',
									'tutorhelper-bookings'
								)
								: __(
									'Previous week',
									'tutorhelper-bookings'
								)
						);
						?>
					</a>

					<a
						class="button"
						href="<?php echo esc_url( $today_url ); ?>"
					>
						<?php
						echo esc_html__(
							'Today',
							'tutorhelper-bookings'
						);
						?>
					</a>

					<a
						class="button"
						href="<?php echo esc_url( $next_url ); ?>"
					>
						<?php
						echo esc_html(
							'month' === $view
								? __(
									'Next month',
									'tutorhelper-bookings'
								)
								: __(
									'Next week',
									'tutorhelper-bookings'
								)
						);
						?>

						<span aria-hidden="true">&rarr;</span>
					</a>
				</div>

				<h2 class="tutorhelper-booking-hub__period-title">
					<?php
					echo esc_html(
						self::get_period_label(
							$view,
							$selected_date
						)
					);
					?>
				</h2>

				<div class="tutorhelper-booking-hub__display-controls">
					<div class="tutorhelper-booking-hub__display-controls-row">
						<?php
						self::render_jump_to_date_form(
							$view,
							$selected_date
						);
						?>

						<div class="tutorhelper-booking-hub__view-switcher">
							<a
								class="<?php
									echo esc_attr(
										self::get_view_button_class(
											'week',
											$view
										)
									);
								?>"
								href="<?php
									echo esc_url(
										$week_url
									);
								?>"
							>
								<?php
								echo esc_html__(
									'Week',
									'tutorhelper-bookings'
								);
								?>
							</a>

							<a
								class="<?php
									echo esc_attr(
										self::get_view_button_class(
											'month',
											$view
										)
									);
								?>"
								href="<?php
									echo esc_url(
										$month_url
									);
								?>"
							>
								<?php
								echo esc_html__(
									'Month',
									'tutorhelper-bookings'
								);
								?>
							</a>
						</div>
					</div>

					<button
						type="button"
						class="button tutorhelper-calendar-sidebar-toggle"
						aria-controls="tutorhelper-calendar-sidebar"
						aria-expanded="true"
						data-calendar-sidebar-toggle
					>
						<span
							class="dashicons dashicons-arrow-right-alt2"
							aria-hidden="true"
							data-calendar-sidebar-toggle-icon
						></span>

						<span data-calendar-sidebar-toggle-label>
							<?php
							echo esc_html__(
								'Hide side panel',
								'tutorhelper-bookings'
							);
							?>
						</span>
					</button>
				</div>

                
			</div>

			<div
				class="tutorhelper-booking-hub__layout"
				data-calendar-layout
			>
				<main class="tutorhelper-booking-hub__calendar-area">
					<?php
					if ( 'month' === $view ) {
						self::render_month_view(
							$selected_date
						);
					} else {
						self::render_week_view(
							$selected_date
						);
					}
					?>
				</main>

				<aside
					id="tutorhelper-calendar-sidebar"
					class="tutorhelper-booking-hub__sidebar"
					data-calendar-sidebar
				>
					<?php self::render_availability_panel(); ?>
					<?php self::render_booking_types_panel(); ?>
				</aside>
			</div>

		<?php
	}




	/**
	 * Get the selected calendar view.
	 *
	 * An explicit valid URL value updates the current user's preference.
	 * When no view is supplied, the saved preference is used.
	 *
	 * @return string Either week or month.
	 */
	public static function get_selected_view() {
		$allowed_views = array(
			'week',
			'month',
		);

		$requested_view = isset(
			$_GET['view'] // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request state used for navigation or feedback; value is sanitized and no state change occurs.
		)
		&& ! is_array(
			$_GET['view'] // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request state used for navigation or feedback; value is sanitized and no state change occurs.
		)
			? sanitize_key(
				wp_unslash(
					$_GET['view'] // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request state used for navigation or feedback; value is sanitized and no state change occurs.
				)
			)
			: '';

		/*
		* An explicit valid choice is authoritative and becomes the tutor's
		* saved preference.
		*/
		if (
			in_array(
				$requested_view,
				$allowed_views,
				true
			)
		) {
			$user_id =
				get_current_user_id();

			if ( $user_id ) {
				update_user_meta(
					$user_id,
					self::CALENDAR_VIEW_META_KEY,
					$requested_view
				);
			}

			return $requested_view;
		}

		/*
		* Invalid explicit values are ignored rather than being saved.
		*/
		$user_id =
			get_current_user_id();

		if ( $user_id ) {
			$saved_view = sanitize_key(
				(string) get_user_meta(
					$user_id,
					self::CALENDAR_VIEW_META_KEY,
					true
				)
			);

			if (
				in_array(
					$saved_view,
					$allowed_views,
					true
				)
			) {
				return $saved_view;
			}
		}

		return 'week';
	}

	/**
	 * Get and validate the selected calendar date.
	 *
	 * @return DateTimeImmutable
	 */
	public static function get_selected_date() {
		$timezone = wp_timezone();

		$fallback_date = new DateTimeImmutable(
			'today',
			$timezone
		);

		if ( ! isset( $_GET['date'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request state used for navigation or feedback; value is sanitized and no state change occurs.
			return $fallback_date;
		}

		$date_value = sanitize_text_field(
			wp_unslash( $_GET['date'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request state used for navigation or feedback; value is sanitized and no state change occurs.
		);

		$parsed_date = DateTimeImmutable::createFromFormat(
			'!Y-m-d',
			$date_value,
			$timezone
		);

		if (
			false === $parsed_date
			|| $parsed_date->format( 'Y-m-d' ) !== $date_value
		) {
			return $fallback_date;
		}

		return $parsed_date;
	}

	/**
	 * Get the seven dates displayed in the selected week.
	 *
	 * @param DateTimeImmutable $selected_date Selected calendar date.
	 *
	 * @return array
	 */
	public static function get_week_dates(
		DateTimeImmutable $selected_date
	) {
		$week_start = $selected_date->modify(
			'monday this week'
		);

		$dates = array();

		for ( $day_number = 0; $day_number < 7; ++$day_number ) {
			$dates[] = $week_start->modify(
				'+' . $day_number . ' days'
			);
		}

		return $dates;
	}

	/**
	 * Build a complete Monday-to-Sunday monthly calendar grid.
	 *
	 * Dates from the previous and following months are included where needed.
	 *
	 * @param DateTimeImmutable $selected_date Selected calendar date.
	 *
	 * @return array Array of weeks, each containing seven dates.
	 */
	public static function get_month_grid(
		DateTimeImmutable $selected_date
	) {
		$month_start = $selected_date->modify(
			'first day of this month'
		);

		$month_end = $selected_date->modify(
			'last day of this month'
		);

		$grid_start = $month_start->modify(
			'monday this week'
		);

		$grid_end = $month_end->modify(
			'sunday this week'
		);

		$weeks        = array();
		$current_date = $grid_start;

		while ( $current_date <= $grid_end ) {
			$week = array();

			for ( $day_number = 0; $day_number < 7; ++$day_number ) {
				$week[] = $current_date;

				$current_date = $current_date->modify(
					'+1 day'
				);
			}

			$weeks[] = $week;
		}

		return $weeks;
	}

	/**
	 * Render the weekly calendar.
	 *
	 * @param DateTimeImmutable $selected_date Selected date.
	 *
	 * @return void
	 */
	private static function render_week_view(
		DateTimeImmutable $selected_date
	) {
		$week_dates = self::get_week_dates(
			$selected_date
		);

		?>
		<div class="tutorhelper-week-calendar">
			<?php foreach ( $week_dates as $date ) : ?>
				<?php
				self::render_week_day(
					$date,
					$selected_date
				);
				?>
			<?php endforeach; ?>
		</div>
		<?php
	}

	/**
	 * Render one weekly calendar day.
	 *
	 * @param DateTimeImmutable $date          Date being rendered.
	 * @param DateTimeImmutable $selected_date Selected date.
	 *
	 * @return void
	 */
	private static function render_week_day(
		DateTimeImmutable $date,
		DateTimeImmutable $selected_date
	) {
		$date_string = $date->format( 'Y-m-d' );
		$classes     = self::get_day_classes(
			$date,
			$selected_date
		);

		?>
		<section class="<?php echo esc_attr( implode( ' ', $classes ) ); ?>">
			<header class="tutorhelper-calendar-day__header">
				<span class="tutorhelper-calendar-day__weekday">
					<?php
					echo esc_html(
						wp_date(
							'D',
							$date->getTimestamp(),
							wp_timezone()
						)
					);
					?>
				</span>

				<span class="tutorhelper-calendar-day__date">
					<?php
					echo esc_html(
						wp_date(
							'j M',
							$date->getTimestamp(),
							wp_timezone()
						)
					);
					?>
				</span>
			</header>

			<div class="tutorhelper-calendar-day__content">
				<div class="tutorhelper-calendar-day__section">
					<h3>
						<?php
						echo esc_html__(
							'Availability',
							'tutorhelper-bookings'
						);
						?>
					</h3>

					<div
						class="tutorhelper-calendar-day__availability"
						data-date="<?php echo esc_attr( $date_string ); ?>"
					>
						<?php
						ob_start();

						do_action(
							'tutorhelper_booking_hub_week_availability',
							$date_string,
							$date
						);

						$availability_output = trim(
							ob_get_clean()
						);

						if ( '' !== $availability_output ) {
							echo wp_kses_post( $availability_output );
						} else {
							?>
							<p class="tutorhelper-calendar-day__empty">
								<?php
								echo esc_html__(
									'No availability',
									'tutorhelper-bookings'
								);
								?>
							</p>
							<?php
						}
						?>
					</div>

				<div class="tutorhelper-calendar-day__section">
					<h3>
						<?php
						echo esc_html__(
							'Bookings',
							'tutorhelper-bookings'
						);
						?>
					</h3>

					<div
						class="tutorhelper-calendar-day__bookings"
						data-date="<?php echo esc_attr( $date_string ); ?>"
					>
						<?php
							ob_start();

							/**
							 * Fires inside a weekly Bookings area.
							 *
							 * Modules should echo booking cards for the supplied local date.
							 *
							 * @param string            $date_string Date in Y-m-d format.
							 * @param DateTimeImmutable $date        Local calendar date.
							 */
							do_action(
								'tutorhelper_booking_hub_week_bookings',
								$date_string,
								$date
							);

							$bookings_output = trim(
								ob_get_clean()
							);

							if ( '' !== $bookings_output ) {
								echo wp_kses_post(
									$bookings_output
								);
							} else {
								?>
								<p class="tutorhelper-calendar-day__empty">
									<?php
									echo esc_html__(
										'No bookings',
										'tutorhelper-bookings'
									);
									?>
								</p>
								<?php
							}
						?>
					</div>
				</div>
			</div>
		</section>
		<?php
	}

	/**
	 * Render the monthly calendar.
	 *
	 * @param DateTimeImmutable $selected_date Selected date.
	 *
	 * @return void
	 */
	private static function render_month_view(
		DateTimeImmutable $selected_date
	) {
		$weeks = self::get_month_grid(
			$selected_date
		);

		?>
		<div class="tutorhelper-month-calendar-wrap">
			<div class="tutorhelper-month-calendar">
				<div class="tutorhelper-month-calendar__weekdays">
					<?php
					foreach ( self::get_weekday_labels() as $weekday ) {
						?>
						<div>
							<?php echo esc_html( $weekday ); ?>
						</div>
						<?php
					}
					?>
				</div>

				<div class="tutorhelper-month-calendar__grid">
					<?php foreach ( $weeks as $week ) : ?>
						<?php foreach ( $week as $date ) : ?>
							<?php
							self::render_month_day(
								$date,
								$selected_date
							);
							?>
						<?php endforeach; ?>
					<?php endforeach; ?>
				</div>
			</div>
		</div>
		<?php
	}

	/**
	 * Render one monthly calendar cell.
	 *
	 * @param DateTimeImmutable $date          Date being rendered.
	 * @param DateTimeImmutable $selected_date Selected date.
	 *
	 * @return void
	 */
	private static function render_month_day(
		DateTimeImmutable $date,
		DateTimeImmutable $selected_date
	) {
		$date_string = $date->format( 'Y-m-d' );

		$classes = self::get_day_classes(
			$date,
			$selected_date
		);

		if (
			$date->format( 'm' )
			!== $selected_date->format( 'm' )
		) {
			$classes[] = 'is-outside-month';
		}

		$week_url = self::get_period_url(
			'week',
			$date
		);

		?>
		<article class="<?php echo esc_attr( implode( ' ', $classes ) ); ?>">
			<header class="tutorhelper-month-day__header">
				<a href="<?php echo esc_url( $week_url ); ?>">
					<span class="screen-reader-text">
						<?php
						echo esc_html(
							sprintf(
								/* translators: %s: Full calendar date. */
								__(
									'Open week containing %s',
									'tutorhelper-bookings'
								),
								wp_date(
									'j F Y',
									$date->getTimestamp(),
									wp_timezone()
								)
							)
						);
						?>
					</span>

					<span aria-hidden="true">
						<?php echo esc_html( $date->format( 'j' ) ); ?>
					</span>
				</a>
			</header>

			<div
				class="tutorhelper-month-day__content"
				data-date="<?php echo esc_attr( $date_string ); ?>"
			>
				<?php
				ob_start();

				do_action(
					'tutorhelper_booking_hub_month_day',
					$date_string,
					$date
				);

				$month_output = trim(
					ob_get_clean()
				);

				if ( '' !== $month_output ) {
					echo wp_kses_post( $month_output );
				} else {
					?>
					<p class="tutorhelper-month-day__empty">
						<?php
						echo esc_html__(
							'No events',
							'tutorhelper-bookings'
						);
						?>
					</p>
					<?php
				}
				?>
			</div>
		</article>
		<?php
	}

	/**
	 * Render the jump-to-date form.
	 *
	 * @param string            $view          Current calendar view.
	 * @param DateTimeImmutable $selected_date Selected date.
	 *
	 * @return void
	 */
	private static function render_jump_to_date_form(
		$view,
		DateTimeImmutable $selected_date
	) {
		?>
		<form
			method="get"
			action="<?php echo esc_url( admin_url( 'admin.php' ) ); ?>"
			class="tutorhelper-booking-hub__date-jump"
		>
			<input
				type="hidden"
				name="page"
				value="<?php echo esc_attr( self::PAGE_SLUG ); ?>"
			/>

			<input
				type="hidden"
				name="view"
				value="<?php echo esc_attr( $view ); ?>"
			/>

			<label for="tutorhelper-booking-hub-date">
				<?php
				echo esc_html__(
					'Jump to date',
					'tutorhelper-bookings'
				);
				?>
			</label>

			<input
				type="date"
				id="tutorhelper-booking-hub-date"
				name="date"
				value="<?php echo esc_attr( $selected_date->format( 'Y-m-d' ) ); ?>"
			/>

			<button type="submit" class="button">
				<?php
				echo esc_html__(
					'Go',
					'tutorhelper-bookings'
				);
				?>
			</button>
		</form>
		<?php
	}

	/**
	 * Render the Availability side panel.
	 *
	 * The panel starts closed unless an Availability rule has been selected
	 * from the calendar for editing.
	 *
	 * @return void
	 */
	private static function render_availability_panel() {
		$availability_id = isset( $_GET['availability_id'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request state used for navigation or feedback; value is sanitized and no state change occurs.
			? absint( $_GET['availability_id'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request state used for navigation or feedback; value is sanitized and no state change occurs.
			: 0;

		$panel_expanded = 0 < $availability_id;

		$panel_icon_class = $panel_expanded
			? 'dashicons-arrow-up-alt2'
			: 'dashicons-arrow-down-alt2';

		?>
		<section class="tutorhelper-hub-panel">
			<button
				type="button"
				class="tutorhelper-hub-panel__toggle"
				aria-expanded="<?php echo $panel_expanded ? 'true' : 'false'; ?>"
				aria-controls="tutorhelper-availability-panel"
			>
				<span>
					<?php
					echo esc_html(
						$panel_expanded
							? __(
								'Edit Availability',
								'tutorhelper-bookings'
							)
							: __(
								'Add Availability',
								'tutorhelper-bookings'
							)
					);
					?>
				</span>

				<span
					class="dashicons <?php echo esc_attr( $panel_icon_class ); ?>"
					aria-hidden="true"
				></span>
			</button>

			<div
				id="tutorhelper-availability-panel"
				class="tutorhelper-hub-panel__content"
				<?php echo $panel_expanded ? '' : 'hidden'; ?>
			>
				<?php
				/**
				 * Fires inside the Booking Hub Availability panel.
				 *
				 * The Availability module renders its create or edit form here.
				 */
				do_action(
					'tutorhelper_booking_hub_availability_panel'
				);
				?>
			</div>
		</section>
		<?php
	}

	/**
	 * Render the Booking Types side panel.
	 *
	 * The panel starts closed unless the tutor is adding or editing a
	 * Booking Type.
	 *
	 * @return void
	 */
	private static function render_booking_types_panel() {
		$booking_type_id = isset( $_GET['booking_type_id'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request state used for navigation or feedback; value is sanitized and no state change occurs.
			? absint( $_GET['booking_type_id'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request state used for navigation or feedback; value is sanitized and no state change occurs.
			: 0;

		$booking_type_action = isset(
			$_GET['booking_type_action'] // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request state used for navigation or feedback; value is sanitized and no state change occurs.
		)
			? sanitize_key(
				wp_unslash(
					$_GET['booking_type_action'] // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request state used for navigation or feedback; value is sanitized and no state change occurs.
				)
			)
			: '';

		$panel_expanded = 0 < $booking_type_id
			|| 'add' === $booking_type_action;

		$panel_icon_class = $panel_expanded
			? 'dashicons-arrow-up-alt2'
			: 'dashicons-arrow-down-alt2';

		$booking_types = class_exists(
			'TutorHelper_Booking_Types'
		)
			? TutorHelper_Booking_Types::get_all()
			: array();

		?>
		<section
			class="tutorhelper-hub-panel tutorhelper-booking-types-panel"
			id="tutorhelper-booking-types-section"
		>
			<button
				type="button"
				class="tutorhelper-hub-panel__toggle"
				aria-expanded="<?php echo $panel_expanded ? 'true' : 'false'; ?>"
				aria-controls="tutorhelper-booking-types-panel"
			>
				<span>
					<?php
					echo esc_html__(
						'Booking Types',
						'tutorhelper-bookings'
					);
					?>
				</span>

				<span
					class="dashicons <?php echo esc_attr( $panel_icon_class ); ?>"
					aria-hidden="true"
				></span>
			</button>

			<div
				id="tutorhelper-booking-types-panel"
				class="tutorhelper-hub-panel__content"
				<?php echo $panel_expanded ? '' : 'hidden'; ?>
			>
				<?php
				/**
				 * Fires inside the Booking Hub Booking Types panel.
				 *
				 * @param array $booking_types Existing Booking Types.
				 */
				do_action(
					'tutorhelper_booking_hub_booking_types_panel',
					array(
						'booking_types' => $booking_types,
					)
				);
				?>
			</div>
		</section>
		<?php
	}

	/**
	 * Return classes for a calendar day.
	 *
	 * @param DateTimeImmutable $date          Date being displayed.
	 * @param DateTimeImmutable $selected_date Selected calendar date.
	 *
	 * @return array
	 */
	private static function get_day_classes(
		DateTimeImmutable $date,
		DateTimeImmutable $selected_date
	) {
		$classes = array(
			'tutorhelper-calendar-day',
		);

		$today = new DateTimeImmutable(
			'today',
			wp_timezone()
		);

		if (
			$date->format( 'Y-m-d' )
			=== $today->format( 'Y-m-d' )
		) {
			$classes[] = 'is-today';
		}

		if (
			$date->format( 'Y-m-d' )
			=== $selected_date->format( 'Y-m-d' )
		) {
			$classes[] = 'is-selected';
		}

		return $classes;
	}

	/**
	 * Return a period navigation URL.
	 *
	 * @param string            $view Calendar view.
	 * @param DateTimeImmutable $date Target date.
	 *
	 * @return string
	 */
	public static function get_period_url(
		$view,
		DateTimeImmutable $date
	) {
		$view = in_array(
			$view,
			array( 'week', 'month' ),
			true
		)
			? $view
			: 'week';

		return add_query_arg(
			array(
				'page' => self::PAGE_SLUG,
				'tab'  => 'calendar',
				'view' => $view,
				'date' => $date->format( 'Y-m-d' ),
			),
			admin_url( 'admin.php' )
		);
	}

	/**
	 * Return the previous period URL.
	 *
	 * @param string            $view          Calendar view.
	 * @param DateTimeImmutable $selected_date Selected date.
	 *
	 * @return string
	 */
	private static function get_previous_url(
		$view,
		DateTimeImmutable $selected_date
	) {
		$previous_date = 'month' === $view
			? $selected_date->modify( '-1 month' )
			: $selected_date->modify( '-1 week' );

		return self::get_period_url(
			$view,
			$previous_date
		);
	}

	/**
	 * Return the next period URL.
	 *
	 * @param string            $view          Calendar view.
	 * @param DateTimeImmutable $selected_date Selected date.
	 *
	 * @return string
	 */
	private static function get_next_url(
		$view,
		DateTimeImmutable $selected_date
	) {
		$next_date = 'month' === $view
			? $selected_date->modify( '+1 month' )
			: $selected_date->modify( '+1 week' );

		return self::get_period_url(
			$view,
			$next_date
		);
	}

	/**
	 * Return the calendar period heading.
	 *
	 * @param string            $view          Calendar view.
	 * @param DateTimeImmutable $selected_date Selected date.
	 *
	 * @return string
	 */
	private static function get_period_label(
		$view,
		DateTimeImmutable $selected_date
	) {
		if ( 'month' === $view ) {
			return wp_date(
				'F Y',
				$selected_date->getTimestamp(),
				wp_timezone()
			);
		}

		$week_dates = self::get_week_dates(
			$selected_date
		);

		$week_start = reset( $week_dates );
		$week_end   = end( $week_dates );

		if (
			$week_start->format( 'Y' )
			!== $week_end->format( 'Y' )
		) {
			return sprintf(
				'%1$s – %2$s',
				wp_date(
					'j F Y',
					$week_start->getTimestamp(),
					wp_timezone()
				),
				wp_date(
					'j F Y',
					$week_end->getTimestamp(),
					wp_timezone()
				)
			);
		}

		if (
			$week_start->format( 'm' )
			!== $week_end->format( 'm' )
		) {
			return sprintf(
				'%1$s – %2$s',
				wp_date(
					'j F',
					$week_start->getTimestamp(),
					wp_timezone()
				),
				wp_date(
					'j F Y',
					$week_end->getTimestamp(),
					wp_timezone()
				)
			);
		}

		return sprintf(
			'%1$s–%2$s',
			wp_date(
				'j',
				$week_start->getTimestamp(),
				wp_timezone()
			),
			wp_date(
				'j F Y',
				$week_end->getTimestamp(),
				wp_timezone()
			)
		);
	}

	/**
	 * Return the class for one view-switching button.
	 *
	 * @param string $button_view View represented by the button.
	 * @param string $active_view Currently active view.
	 *
	 * @return string
	 */
	private static function get_view_button_class(
		$button_view,
		$active_view
	) {
		return $button_view === $active_view
			? 'button button-primary'
			: 'button';
	}

	/**
	 * Return translated weekday labels.
	 *
	 * @return array
	 */
	private static function get_weekday_labels() {
		return array(
			__( 'Monday', 'tutorhelper-bookings' ),
			__( 'Tuesday', 'tutorhelper-bookings' ),
			__( 'Wednesday', 'tutorhelper-bookings' ),
			__( 'Thursday', 'tutorhelper-bookings' ),
			__( 'Friday', 'tutorhelper-bookings' ),
			__( 'Saturday', 'tutorhelper-bookings' ),
			__( 'Sunday', 'tutorhelper-bookings' ),
		);
	}
}
