<?php

namespace SugarCalendarBookings\Calendar\Admin;

use DateInterval;
use DatePeriod;
use DateTimeImmutable;
use Exception;
use SugarCalendarBookings\Appointments\AppointmentModel;
use SugarCalendarBookings\Services\ServiceModel;
use SugarCalendarBookings\Support\Data\DateTime as DateTimeData;
use SugarCalendarBookings\Support\Helpers\Date;
use SugarCalendarBookings\Support\Helpers\FilterLabels;
use SugarCalendarBookings\Support\Helpers\UI;

/**
 * Abstract calendar view.
 *
 * @since 1.0.0
 */
abstract class AbstractCalendarView {

	/**
	 * View mode.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	const MODE = '';

	/**
	 * Current query.
	 *
	 * @since 1.0.0
	 *
	 * @var CalendarQuery
	 */
	protected $query;

	/**
	 * Entries.
	 *
	 * @since 1.0.0
	 *
	 * @var array
	 */
	protected $entries = [];

	/**
	 * Calendar data structure.
	 *
	 * @since 1.0.0
	 *
	 * @var array;
	 */
	protected $calendar = [];

	/**
	 * Constructor.
	 *
	 * @since 1.0.0
	 * @since 1.3.0 Use relationships from get_appointments_relationships().
	 *
	 * @param CalendarQuery $query Current query.
	 */
	public function __construct( $query ) {

		$this->query   = $query;
		$this->entries = array_column(
			$this->query->get_appointments( $this->get_appointments_relationships() ),
			null,
			'id'
		);

		$this->populate_calendar();
	}

	/**
	 * Get view mode.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	public function get_mode() {

		return static::MODE;
	}

	/**
	 * Get appointments relationships.
	 *
	 * @since 1.3.0
	 *
	 * @return array
	 */
	protected function get_appointments_relationships() {

		return [
			'service' => fn( $q ) => $q->select( [ 'id', 'name', 'color' ] ),
		];
	}

	/**
	 * Get column options.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	public function get_column_options() {

		return [];
	}

	/**
	 * Get appointment status labels.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	private function get_status_labels() {

		return [
			''          => esc_html__( 'All', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			'approved'  => esc_html__( 'Approved', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			'pending'   => esc_html__( 'Pending', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			'cancelled' => esc_html__( 'Cancelled', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			'done'      => esc_html__( 'Done', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
		];
	}

	/**
	 * Display filters.
	 *
	 * @since 1.0.0
	 * @since 1.3.0 Grouped filters.
	 *
	 * @return void
	 */
	protected function display_filters() {

		?>
		<div class="sugar-calendar-bookings-calendar-view__filters">
			<div class="sugar-calendar-bookings-calendar-view__filters__top-row">
				<?php
				$this->display_status_filter( $this->query->get_status_counts(), $this->query->get_status() );
				$this->display_search_filter( $this->query->get_search() );
				?>
			</div>
			<div class="sugar-calendar-bookings-calendar-view__filters__bottom-row">
				<?php
				$this->display_date_filter( $this->query->get_year(), $this->query->get_month(), $this->query->get_day() );
				$this->display_relationship_filters();
				$this->display_pagination();
				?>
			</div>
		</div>
		<?php
	}

	/**
	 * Display status filter.
	 *
	 * @since 1.0.0
	 *
	 * @param array  $counts         Status counts.
	 * @param string $current_status Current status.
	 *
	 * @return void
	 */
	private function display_status_filter( $counts, $current_status ) {

		$labels = $this->get_status_labels();
		?>
		<ul class="subsubsub sugar-calendar-bookings-calendar-view__filters__status-filter">

			<?php foreach ( $labels as $status => $label ) : ?>

				<?php if ( isset( $counts[ $status ] ) ) : ?>

					<li class="<?php echo esc_attr( $status ); ?>">
						<a href="<?php echo esc_url( $this->query->get_url( [ 'status' => $status ] ) ); ?>"
							<?php
							if ( $current_status === $status ) :
								?>
								class="current"<?php endif; ?>>
							<?php echo esc_html( $label ); ?> <span class="count">(<?php echo esc_html( $counts[ $status ] ); ?>)</span>
						</a>
					</li>

				<?php endif; ?>

			<?php endforeach; ?>

		</ul>
		<?php
	}

	/**
	 * Get search form parameters.
	 *
	 * Returns the list of query parameters that the search form handles.
	 * These parameters will be excluded from hidden fields in the search form.
	 *
	 * @since 1.3.0
	 *
	 * @return array
	 */
	protected function get_search_form_params() {

		return [ 'search' ];
	}

	/**
	 * Get date form parameters.
	 *
	 * Returns the list of query parameters that the date form handles.
	 * These parameters will be excluded from hidden fields in the date form.
	 *
	 * @since 1.3.0
	 *
	 * @return array
	 */
	protected function get_date_form_params() {

		return [ 'year', 'month', 'day' ];
	}

	/**
	 * Get relationship form parameters.
	 *
	 * Returns the list of query parameters that the relationship form handles.
	 * These parameters will be excluded from hidden fields in the relationship form.
	 *
	 * @since 1.3.0
	 *
	 * @return array
	 */
	protected function get_relationship_form_params() {

		return [ 'service_id' ];
	}

	/**
	 * Display current query parameter fields.
	 *
	 * @since 1.0.0
	 * @since 1.3.0 Add exclude parameter to filter out form-controlled params.
	 *
	 * @param array $exclude List of parameter names to exclude from hidden fields.
	 *
	 * @return void
	 */
	private function display_query_parameter_fields( $exclude = [] ) {

		$url   = wp_parse_url( $this->query->get_url() );
		$query = [];

		wp_parse_str( $url['query'], $query );

		foreach ( $query as $parameter => $value ) :

			// Skip parameters that are handled by the form itself.
			if ( in_array( $parameter, $exclude, true ) ) {
				continue;
			}
			?>

			<?php if ( is_array( $value ) ) : ?>

				<?php foreach ( $value as $item ) : ?>
					<input type="hidden" name="<?php echo esc_attr( $parameter ); ?>[]" value="<?php echo esc_attr( $item ); ?>">
				<?php endforeach; ?>

			<?php else : ?>

				<input type="hidden" name="<?php echo esc_attr( $parameter ); ?>" value="<?php echo esc_attr( $value ); ?>">
			<?php endif; ?>
		<?php
		endforeach;
	}

	/**
	 * Display a filters form wrapper.
	 *
	 * @since 1.0.0
	 * @since 1.3.0 Changed to use exclude parameter instead of boolean flag.
	 *
	 * @param callable $callback Callback to render form inputs.
	 * @param array    $exclude  List of parameter names to exclude from hidden fields.
	 *
	 * @return void
	 */
	private function display_filter_form_wrapper( $callback, $exclude = [] ) {

		?>
		<form action="<?php echo esc_url( $this->query->get_url() ); ?>">

			<?php
			$this->display_query_parameter_fields( $exclude );

			call_user_func( $callback );
			?>

		</form>
		<?php
	}

	/**
	 * Display search filter.
	 *
	 * @since 1.0.0
	 *
	 * @param string $search Current search.
	 *
	 * @return void
	 */
	private function display_search_filter( $search ) {

		?>
		<div class="sugar-calendar-bookings-calendar-view__filters__search-filter">

			<?php
			$this->display_filter_form_wrapper(
				function () use ( $search ) {

				UI::search_input(
					[
						'id'          => 'calendar-search',
						'value'       => $search,
						'placeholder' => esc_html__( 'Search Appointments', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					]
				);
				},
				$this->get_search_form_params()
			);
			?>

		</div>
		<?php
	}

	/**
	 * Display date filter.
	 *
	 * @since 1.0.0
	 * @since 1.3.0 Added max attribute to day input.
	 *
	 * @param string $year  Current year.
	 * @param string $month Current month.
	 * @param string $day   Current day.
	 *
	 * @return void
	 */
	private function display_date_filter( $year, $month, $day ) {

		?>
		<div class="sugar-calendar-bookings-calendar-view__filters__date-filter">

			<?php
			$this->display_filter_form_wrapper(
				function () use ( $year, $month, $day ) {

					UI::select_input(
						[
							'name'    => 'month',
							'options' => DateTimeData::get_months(),
							'value'   => $month,
							'data'    => [ 'date-part' => 'month' ],
						],
						true
					);

					if ( $this->get_mode() === 'day' ) {

						// Get number of days in month.
						$days_in_month = (int) gmdate( 't', gmmktime( 0, 0, 0, (int) $month, 1, (int) $year ) );

						UI::number_input(
							[
								'name'  => 'day',
								'value' => $day,
								'max'   => $days_in_month,
								'data'  => [ 'date-part' => 'day' ],
							],
							true
						);
					}

					UI::number_input(
						[
							'name'  => 'year',
							'value' => $year,
							'data'  => [ 'date-part' => 'year' ],
						],
						true
					);
					?>

					<button type="submit" class="button"><?php esc_html_e( 'View', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?></button>

					<?php
				},
				$this->get_date_form_params()
			);
			?>
		</div>
		<?php
	}

	/**
	 * Display relationship filter.
	 *
	 * @since 1.0.0
	 * @since 1.3.0 Changed to relationship filter.
	 *
	 * @return void
	 */
	private function display_relationship_filters() {
		?>
		<div class="sugar-calendar-bookings-calendar-view__filters__relationship-filters">

			<?php
			$this->display_filter_form_wrapper(
				function () {

					$this->display_relationship_filter_inputs();

					?>
					<button type="submit" class="button"><?php esc_html_e( 'Filter', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?></button>
					<?php
				},
				$this->get_relationship_form_params()
			);
			?>
		</div>
		<?php
	}

	/**
	 * Display relationship inputs.
	 *
	 * @since 1.3.0
	 *
	 * @return void
	 */
	protected function display_relationship_filter_inputs() {

		$this->display_service_filter_input();
	}

	/**
	 * Display service filter.
	 *
	 * @since 1.3.0
	 *
	 * @return void
	 */
	private function display_service_filter_input() {

		$service_id = $this->query->get_service_id();

		$services     = wp_list_pluck( ServiceModel::query()->get(), 'name', 'id' );
		$all_services = [ '' => esc_html__( 'All Services', 'sugar-calendar-bookings-scheduling-appointments-lite' ) ];
		$options      = $all_services + $services;

		UI::select_input(
			[
				'name'    => 'service_id',
				'options' => $options,
				'value'   => is_numeric( $service_id ) ? (int) $service_id : $service_id,
			],
			true
		);
	}

	/**
	 * Display pagination.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	private function display_pagination() {

		$today_label    = esc_html__( 'Today', 'sugar-calendar-bookings-scheduling-appointments-lite' );
		$today          = $this->query->get_today();
		$today_link     = $this->query->get_url(
			[
				'year'  => $today->format( 'Y' ),
				'month' => $today->format( 'n' ),
				'day'   => $today->format( 'j' ),
			]
		);
		$first_link     = $this->pagination_get_first_link_url();
		$previous_link  = $this->pagination_get_previous_link_url();
		$next_link      = $this->pagination_get_next_link_url();
		$last_link      = $this->pagination_get_last_link_url();
		$first_label    = $this->pagination_get_first_link_text();
		$previous_label = $this->pagination_get_previous_link_text();
		$next_label     = $this->pagination_get_next_link_text();
		$last_label     = $this->pagination_get_last_link_text();
		?>
		<div class="sugar-calendar-bookings-calendar-view__filters__pagination-filter">
			<a class="previous-page button" href="<?php echo esc_url( $first_link ); ?>" title="<?php echo esc_html( $first_label ); ?>">
				<span class="screen-reader-text"><?php echo esc_html( $first_label ); ?></span>
				<span aria-hidden="true">&laquo;</span>
			</a>

			<a class="previous-page button" href="<?php echo esc_url( $previous_link ); ?>" title="<?php echo esc_html( $previous_label ); ?>">
				<span class="screen-reader-text"><?php echo esc_html( $previous_label ); ?></span>
				<span aria-hidden="true">&lsaquo;</span>
			</a>

			<a href="<?php echo esc_url( $today_link ); ?>" class="previous-page today button" title="<?php echo esc_html( $today_label ); ?>">
				<span class="screen-reader-text"><?php echo esc_html( $today_label ); ?></span>
				<span aria-hidden="true"><?php echo esc_html( $today_label ); ?></span>
			</a>

			<a class="next-page button" href="<?php echo esc_url( $next_link ); ?>" title="<?php echo esc_html( $next_label ); ?>">
				<span class="screen-reader-text"><?php echo esc_html( $next_label ); ?></span>
				<span aria-hidden="true">&rsaquo;</span>
			</a>

			<a class="next-page button" href="<?php echo esc_url( $last_link ); ?>" title="<?php echo esc_html( $last_label ); ?>">
				<span class="screen-reader-text"><?php echo esc_html( $last_label ); ?></span>
				<span aria-hidden="true">&raquo;</span>
			</a>
		</div>
		<?php
	}

	/**
	 * Get pagination first link URL.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	protected function pagination_get_first_link_url() {

		return $this->query->get_url_with_date( $this->query->get_previous_year() );
	}

	/**
	 * Get pagination previous link URL.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	protected function pagination_get_previous_link_url() {

		return $this->query->get_url_with_date( $this->query->get_previous_month() );
	}

	/**
	 * Get pagination next link URL.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	protected function pagination_get_next_link_url() {

		return $this->query->get_url_with_date( $this->query->get_next_month() );
	}

	/**
	 * Get pagination last link URL.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	protected function pagination_get_last_link_url() {

		return $this->query->get_url_with_date( $this->query->get_next_year() );
	}

	/**
	 * Get pagination first link text.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	protected function pagination_get_first_link_text() {

		return esc_html__( 'Previous Year', 'sugar-calendar-bookings-scheduling-appointments-lite' );
	}

	/**
	 * Get pagination previous link text.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	protected function pagination_get_previous_link_text() {

		return esc_html__( 'Previous Month', 'sugar-calendar-bookings-scheduling-appointments-lite' );
	}

	/**
	 * Get pagination next link text.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	protected function pagination_get_next_link_text() {

		return esc_html__( 'Next Month', 'sugar-calendar-bookings-scheduling-appointments-lite' );
	}

	/**
	 * Get pagination last link text.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	protected function pagination_get_last_link_text() {

		return esc_html__( 'Next Year', 'sugar-calendar-bookings-scheduling-appointments-lite' );
	}

	/**
	 * Get header slots.
	 *
	 * @since 1.0.0
	 *
	 * @return array<BaseCalendarSlot>
	 */
	protected function get_header_slots() {

		return [];
	}

	/**
	 * Get body slots.
	 *
	 * @since 1.0.0
	 *
	 * @return array<BaseCalendarSlot>
	 */
	protected function get_body_slots() {

		return $this->get_slots(
			$this->query->get_start_date(),
			$this->query->get_end_date()
		);
	}

	/**
	 * Get footer slots.
	 *
	 * @since 1.0.0
	 *
	 * @return array<BaseCalendarSlot>
	 */
	protected function get_footer_slots() {

		return $this->get_header_slots();
	}

	/**
	 * Get header cell class.
	 *
	 * @since 1.0.0
	 *
	 * @param BaseCalendarSlot $slot Calendar slot.
	 *
	 * @return array
	 */
	protected function get_header_cell_class( $slot ) {

		return $this->get_body_cell_class( $slot );
	}

	/**
	 * Get body cell class.
	 *
	 * @since 1.0.0
	 *
	 * @param BaseCalendarSlot $slot Calendar slot.
	 *
	 * @return array
	 */
	protected function get_body_cell_class( $slot ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found

		return [ 'sugar-calendar-bookings-calendar__cell' ];
	}

	/**
	 * Get footer cell class.
	 *
	 * @since 1.0.0
	 *
	 * @param BaseCalendarSlot $slot Calendar slot.
	 *
	 * @return array
	 */
	protected function get_footer_cell_class( $slot ) {

		return $this->get_header_cell_class( $slot );
	}

	/**
	 * Display header cell.
	 *
	 * @since 1.0.0
	 *
	 * @param BaseCalendarSlot $slot Calendar slot.
	 *
	 * @return void
	 */
	protected function display_header_cell( $slot ) {}

	/**
	 * Display body cell.
	 *
	 * @since 1.0.0
	 *
	 * @param BaseCalendarSlot        $slot         Calendar slot.
	 * @param array<AppointmentModel> $appointments Appointments.
	 *
	 * @return void
	 */
	protected function display_body_cell( $slot, $appointments ) {}

	/**
	 * Display footer cell.
	 *
	 * @since 1.0.0
	 *
	 * @param BaseCalendarSlot $slot Calendar slot.
	 *
	 * @return void
	 */
	protected function display_footer_cell( $slot ) {

		$this->display_header_cell( $slot );
	}

	/**
	 * Output dynamic styles.
	 *
	 * @since 1.0.0
	 * @since 1.4.0 Instance method so Week view can use query/entries for bounds.
	 *
	 * @return void
	 */
	public function output_styles() {}

	/**
	 * Output view specific styles.
	 *
	 * @since 1.3.0
	 *
	 * @return void
	 */
	protected function output_view_specific_styles() {}

	/**
	 * Display search results notice.
	 *
	 * @since 1.0.0
	 * @since 1.4.0 Added filter labels.
	 *
	 * @return void
	 */
	protected function display_search_notice() {

		UI::search_result_notice(
			[
				'result_count'     => count( $this->query->get_appointments() ),
				'search_term'      => $this->query->get_search(),
				'close_url'        => add_query_arg( 'mode', $this->get_mode(), $this->query->get_base_url() ),
				'item_singular'    => esc_html__( 'appointment', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				'item_plural'      => esc_html__( 'appointments', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				'css_class_prefix' => 'sugar-calendar-bookings-calendar',
				'filters'          => $this->get_active_filter_labels(),
			]
		);
	}

	/**
	 * Get the active filters.
	 *
	 * Override in child classes to add additional filters.
	 *
	 * @since 1.4.0
	 *
	 * @return array Associative array of filter key => value pairs.
	 */
	protected function get_active_filters() {

		$values = [];

		$status = $this->query->get_status();

		if ( ! empty( $status ) ) {
			$values['status'] = $status;
		}

		$service_id = $this->query->get_service_id();

		if ( ! empty( $service_id ) ) {
			$values['service_id'] = $service_id;
		}

		return $values;
	}

	/**
	 * Get the filter label map for the search results notice.
	 *
	 * Override in child classes to add domain-specific filters.
	 *
	 * @since 1.4.0
	 *
	 * @return array Filter label map.
	 */
	protected function get_filter_label_map() {

		return [
			'status'     => [
				'label' => esc_html__( 'Status', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			],
			'service_id' => [
				'label' => esc_html__( 'Service', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				'model' => ServiceModel::class,
			],
		];
	}

	/**
	 * Build resolved filter labels for the search results notice.
	 *
	 * @since 1.4.0
	 *
	 * @return array Indexed array of resolved filter label entries.
	 */
	protected function get_active_filter_labels() {

		return FilterLabels::resolve( $this->get_active_filters(), $this->get_filter_label_map() );
	}

	/**
	 * Check if the search/filter notice should be displayed.
	 *
	 * @since 1.4.0
	 *
	 * @return bool
	 */
	protected function should_display_notice() {

		return ! empty( $this->query->get_search() ) || ! empty( $this->get_active_filter_labels() );
	}

	/**
	 * Display content after body.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	protected function display_body_after() {}

	/**
	 * Check if a slot is today.
	 *
	 * @since 1.3.0
	 *
	 * @param BaseCalendarSlot $slot Calendar slot.
	 *
	 * @return bool
	 */
	protected function is_today( $slot ) {

		return $slot->is_same_day( $this->query->get_today() );
	}

	/**
	 * Display current time marker.
	 *
	 * @since 1.3.0
	 *
	 * @param string $style Style.
	 *
	 * @return void
	 */
	protected function display_current_time_marker( $style = '' ) {
		?>
		<div
			class="sugar-calendar-bookings-calendar__current-time-line"
			style="<?php echo esc_attr( $style ); ?>"
		></div>
		<?php
	}

	/**
	 * Display the calendar.
	 *
	 * @since 1.0.0
	 * @since 1.4.0 Use instance method output_styles().
	 *
	 * @return void
	 */
	public function display() {

		$this->output_styles();

		$this->output_view_specific_styles();

		if ( $this->should_display_notice() ) {
			$this->display_search_notice();
		}
		?>

		<div class="sugar-calendar-bookings-calendar-view" data-mode="<?php echo esc_attr( static::MODE ); ?>">

			<?php $this->display_filters(); ?>

			<div class="sugar-calendar-bookings-calendar sugar-calendar-bookings-calendar--<?php echo esc_attr( static::MODE ); ?>">

				<div class="sugar-calendar-bookings-calendar__header">

					<?php
					foreach ( $this->get_header_slots() as $slot ) :
						$class = UI::sanitize_class( $this->get_header_cell_class( $slot ) );
						?>

						<div class="<?php echo esc_attr( $class ); ?>"><?php $this->display_header_cell( $slot ); ?></div>

					<?php endforeach; ?>

				</div>
				<div class="sugar-calendar-bookings-calendar__content">

					<?php foreach ( $this->get_body_slots() as $slot ) : ?>

						<?php
						$entries = $this->get_entries( $slot );
						$class   = UI::sanitize_class( $this->get_body_cell_class( $slot ) );
						?>

						<div class="<?php echo esc_attr( $class ); ?>">
							<?php $this->display_body_cell( $slot, $entries ); ?>
						</div>

					<?php endforeach; ?>

					<?php $this->display_body_after(); ?>

				</div>
				<div class="sugar-calendar-bookings-calendar__footer">

					<?php
					foreach ( $this->get_footer_slots() as $slot ) :
						$class = UI::sanitize_class( $this->get_footer_cell_class( $slot ) );
						?>

						<div class="<?php echo esc_attr( $class ); ?>"><?php $this->display_footer_cell( $slot ); ?></div>

					<?php endforeach; ?>

				</div>
			</div>
		</div>
		<?php
	}

	/**
	 * Populate calendar data structure.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	protected function populate_calendar() {

		foreach ( $this->entries as $entry ) {
			$entry_start = $entry->start_at->setTimezone( Date::get_default_timezone() );
			$entry_end   = $entry->end_at->setTimezone( Date::get_default_timezone() );

			$entry_slots = $this->get_slots( $entry_start, $entry_end );

			foreach ( $entry_slots as $slot ) {
				$slot_string = $slot->to_string();

				$this->calendar[ $slot_string ][] = $entry->id;
			}
		}
	}

	/**
	 * Get slots between a start and end date.
	 *
	 * @since 1.0.0
	 *
	 * @param DateTimeImmutable $start_date Start date.
	 * @param DateTimeImmutable $end_date   End date.
	 *
	 * @return BaseCalendarSlot[]
	 */
	public function get_slots( $start_date, $end_date ) {

		try {
			$period = new DatePeriod(
				$start_date->setTime( 0, 0 ),
				DateInterval::createFromDateString( '1 day' ),
				$end_date->setTime( 23, 59, 59 )
			);

			return array_map(
				fn( $date ) => new BaseCalendarSlot(
					$date->setTime( 0, 0 ),
					$date->setTime( 23, 59, 59 )
				),
				iterator_to_array( $period )
			);
		} catch ( Exception $e ) {
			return [];
		}
	}

	/**
	 * Get entries for a given slot.
	 *
	 * @since 1.0.0
	 *
	 * @param BaseCalendarSlot $slot Calendar slot.
	 *
	 * @return array
	 */
	public function get_entries( $slot ) {

		$slot_string = $slot->to_string();
		$entry_ids   = $this->calendar[ $slot_string ] ?? [];

		return array_map(
			fn( $id ) => $this->entries[ $id ],
			$entry_ids
		);
	}
}
