<?php
/*
 * Copyright (c) 2022 LatePoint LLC. All rights reserved.
 */


class OsEventsHelper {

	public static function week_grid( OsWpDateTime $calendar_start, OsWpDateTime $calendar_end, OsWpDateTime $target_date, array $services_with_resources, array $filter = [] ) {
		$now_datetime = OsTimeHelper::now_datetime_object();
		$html         = '';


		$start_time = 540;
		$end_time   = 1080;

		$min_service_duration = false;

		$day_slots = [];
		for ( $day_date = clone $calendar_start; $day_date <= $calendar_end; $day_date->modify( '+1 day' ) ) {
			$day_slots[ $day_date->format( 'Y-m-d' ) ] = [];
			foreach ( $services_with_resources as $service_resources ) {
				if ( empty( $service_resources['resources'][ $day_date->format( 'Y-m-d' ) ] ) ) {
					continue;
				}
				$clean_periods = [];
				foreach ( $service_resources['resources'][ $day_date->format( 'Y-m-d' ) ] as $resource ) {
					if ( ! empty( $filter['agent_id'] ) && $resource->agent_id != $filter['agent_id'] ) {
						continue;
					}
					if ( ! empty( $filter['service_id'] ) && $resource->service_id != $filter['service_id'] ) {
						continue;
					}
					if ( ! empty( $filter['location_id'] ) && $resource->location_id != $filter['location_id'] ) {
						continue;
					}
					foreach ( $resource->work_time_periods as $time_period ) {
						$clean_periods[] = $time_period;
					}
				}
				$merged_periods = \LatePoint\Misc\TimePeriod::merge_periods( $clean_periods );


				foreach ( $merged_periods as $time_period ) {
					if ( ( $time_period->end_time - $time_period->start_time ) > 0 ) {
						$min_service_duration = $min_service_duration ? min( $min_service_duration, ( $time_period->end_time - $time_period->start_time ) ) : $time_period->end_time - $time_period->start_time;
					}
					$service_box_data = [
						'model'       => $service_resources['model'],
						'time_period' => $time_period,
					];
					$day_slots[ $day_date->format( 'Y-m-d' ) ][ $time_period->start_time ][] = $service_box_data;
					$start_time = min( $start_time, $time_period->start_time );
					$end_time   = max( $end_time, $time_period->end_time );
				}
			}
			ksort( $day_slots[ $day_date->format( 'Y-m-d' ) ] );
		}

		$html .= '<div class="latepoint-calendar-week">';

		// make it start a little earlier so that there is some space between day labels and first bookings
		$start_time = $start_time - 60;

		// make sure timeline starts at hour marks
		if ( $start_time % 60 != 0 ) {
			$start_time = $start_time - ( $start_time % 60 );
		}
		$day_duration = $end_time - $start_time;

		$hour_height_css = '';
		if ( $min_service_duration && $min_service_duration < 60 ) {
			$default_hour_height = 44;
			$hour_height         = ceil( 60 / $min_service_duration ) * $default_hour_height;
			$hour_height_css     = 'style="height: ' . $hour_height . 'px"';
		}

		for ( $day_date = clone $calendar_start; $day_date <= $calendar_end; $day_date->modify( '+1 day' ) ) {



			$is_today        = ( $day_date->format( 'Y-m-d' ) == $now_datetime->format( 'Y-m-d' ) );
			$is_day_in_past  = ( $day_date->format( 'Y-m-d' ) < $now_datetime->format( 'Y-m-d' ) );
			$is_target_month = ( $day_date->format( 'm' ) == $target_date->format( 'm' ) );
			$is_next_month   = ( $day_date->format( 'm' ) > $target_date->format( 'm' ) );
			$is_prev_month   = ( $day_date->format( 'm' ) < $target_date->format( 'm' ) );


			$day_class = '';
			if ( $is_today ) {
				$day_class .= ' os-today';
			}
			if ( $is_day_in_past ) {
				$day_class .= ' os-day-passed';
			}
			if ( $is_target_month ) {
				$day_class .= ' os-month-current';
			}
			if ( $is_next_month ) {
				$day_class .= ' os-month-next';
			}
			if ( $is_prev_month ) {
				$day_class .= ' os-month-prev';
			}
			$day_class .= ' day-weekday-' . $day_date->format( 'N' );

			$html .= '<div class="le-day-wrapper ' . $day_class . '">';


			$html .= '<div class="le-day-weekday-wrapper">';
			$html .= '<div class="le-day-weekday">' . OsBookingHelper::get_weekday_name_by_number( $day_date->format( 'N' ) ) . '</div>';
			if ( $day_date->format( 'd' ) == '1' ) {
				$html .= '<div class="os-day-month">' . OsUtilHelper::get_month_name_by_number( $day_date->format( 'n' ) ) . '</div>';
			}
			$html .= '<div class="le-day-number" 
					data-os-params="' . OsUtilHelper::build_os_params(
				[
					'target_date_string' => $day_date->format( 'Y-m-d' ),
					'filter'             => $filter,
				] 
			) . '" 
					data-os-output-target="lightbox"
					data-os-lightbox-classes="width-600"
					data-os-action="' . OsRouterHelper::build_route_name( 'events', 'events_day_view' ) . '">' . $day_date->format( 'j' ) .
				'</div>';
			$html .= '</div>';

			$html .= '<div class="le-day-schedule-wrapper">';
			$html .= '<div class="day-schedule-timeslots">';
			for ( $i = $start_time; $i < $end_time; $i += 60 ) {
				$html .= '<div class="day-schedule-timeslot-wrapper">';
				$html .= '<div class="day-schedule-timeslot" ' . $hour_height_css . '><div class="day-schedule-timeslot-value">' . OsTimeHelper::minutes_to_hours_and_minutes( $i, null, true, true ) . '</div></div>';
				$html .= '</div>';

			}
			foreach ( $day_slots[ $day_date->format( 'Y-m-d' ) ] as $day_slot_services ) {
				foreach ( $day_slot_services as $day_slot_service ) {
					$slot_duration  = $day_slot_service['time_period']->end_time - $day_slot_service['time_period']->start_time;
					$start_position = ( $day_slot_service['time_period']->start_time - $start_time ) / $day_duration * 100;
					$height         = $slot_duration / $day_duration * 100;
					$extra_attrs    = 'style="top: ' . $start_position . '%; height: ' . $height . '%"';
					$html          .= self::event_service_box_html( $day_slot_service['model'], $day_slot_service['time_period'], $filter, $day_date, $extra_attrs );
				}
			}
			$html .= '</div>';
			$html .= '</div>';
			$html .= '</div>';
		}
		$html .= '</div>';
		return $html;
	}

	public static function month_grid( OsWpDateTime $calendar_start, OsWpDateTime $calendar_end, OsWpDateTime $target_date, array $services_with_resources, array $filter = [] ) {
		$now_datetime = OsTimeHelper::now_datetime_object();
		$html         = '';
		$html        .= '<div class="latepoint-calendar-month">';

		$weekdays = OsBookingHelper::get_weekdays_arr();
		foreach ( $weekdays as $weekday_number => $weekday_name ) {
			$html .= '<div class="le-weekday weekday-' . ( $weekday_number + 1 ) . '">' . $weekday_name . '</div>';
		}

		for ( $day_date = clone $calendar_start; $day_date <= $calendar_end; $day_date->modify( '+1 day' ) ) {
			$is_today        = ( $day_date->format( 'Y-m-d' ) == $now_datetime->format( 'Y-m-d' ) );
			$is_day_in_past  = ( $day_date->format( 'Y-m-d' ) < $now_datetime->format( 'Y-m-d' ) );
			$is_target_month = ( $day_date->format( 'm' ) == $target_date->format( 'm' ) );
			$is_next_month   = ( $day_date->format( 'm' ) > $target_date->format( 'm' ) );
			$is_prev_month   = ( $day_date->format( 'm' ) < $target_date->format( 'm' ) );

			$day_class = '';
			if ( $is_today ) {
				$day_class .= ' os-today';
			}
			if ( $is_day_in_past ) {
				$day_class .= ' os-day-passed';
			}
			if ( $is_target_month ) {
				$day_class .= ' os-month-current';
			}
			if ( $is_next_month ) {
				$day_class .= ' os-month-next';
			}
			if ( $is_prev_month ) {
				$day_class .= ' os-month-prev';
			}
			$day_class .= ' day-weekday-' . $day_date->format( 'N' );

			$html .= '<div class="le-day-wrapper ' . $day_class . '">';

			if ( $day_date->format( 'd' ) == '1' ) {
				$html .= '<div class="os-day-month">' . OsUtilHelper::get_month_name_by_number( $day_date->format( 'n' ) ) . '</div>';
			}
			$html             .= '<div class="le-day-number" 
					data-os-params="' . OsUtilHelper::build_os_params(
				[
					'target_date_string' => $day_date->format( 'Y-m-d' ),
					'filter'             => $filter,
				] 
			) . '" 
					data-os-output-target="lightbox"
					data-os-lightbox-classes="width-600"
					data-os-action="' . OsRouterHelper::build_route_name( 'events', 'events_day_view' ) . '">' . $day_date->format( 'j' ) .
				'</div>';
			$day_services_html = [];
			foreach ( $services_with_resources as $service_resources ) {
				if ( empty( $service_resources['resources'][ $day_date->format( 'Y-m-d' ) ] ) ) {
					continue;
				}
				$clean_periods = [];
				foreach ( $service_resources['resources'][ $day_date->format( 'Y-m-d' ) ] as $resource ) {
					if ( ! empty( $filter['agent_id'] ) && $resource->agent_id != $filter['agent_id'] ) {
						continue;
					}
					if ( ! empty( $filter['service_id'] ) && $resource->service_id != $filter['service_id'] ) {
						continue;
					}
					if ( ! empty( $filter['location_id'] ) && $resource->location_id != $filter['location_id'] ) {
						continue;
					}
					foreach ( $resource->work_time_periods as $time_period ) {
						$clean_periods[] = $time_period;
					}
				}
				$merged_periods = \LatePoint\Misc\TimePeriod::merge_periods( $clean_periods );
				foreach ( $merged_periods as $time_period ) {
					$day_services_html[ $time_period->start_time ][] = self::event_service_box_html( $service_resources['model'], $time_period, $filter, $day_date );
				}
			}
			ksort( $day_services_html );
			$daily_count = 0;
			foreach ( $day_services_html as $day_service_html ) {
				if ( $daily_count >= 4 ) {
					// translators: %d is the number of services
					$more  = sprintf( __( '+ %d more', 'latepoint' ), count( $day_services_html ) - 4 );
					$html .= '<div class="le-more-services" 
					data-os-params="' . esc_attr(
						OsUtilHelper::build_os_params(
							[
								'target_date_string' => $day_date->format( 'Y-m-d' ),
								'filter'             => $filter,
							] 
						) 
					) . '" 
					data-os-output-target="lightbox"
					data-os-lightbox-classes="width-600"
					data-os-action="' . esc_attr( OsRouterHelper::build_route_name( 'events', 'events_day_view' ) ) . '">' . esc_html( $more ) . '</div>';
					break;
				}
				$daily_count++;
				$html .= implode( '', $day_service_html );
			}
			$html .= '</div>';
		}
		$html .= '</div>';
		return $html;
	}

	public static function event_service_box_html( OsServiceModel $service, \LatePoint\Misc\TimePeriod $time_period, array $filter, OsWpDateTime $start_date, string $extra_attrs = '' ) {
		$service_box_html    = '';
		$selected_attributes = '';

		if ( ! empty( $filter['agent_id'] ) ) {
			$selected_attributes .= ' data-selected-agent="' . esc_attr( $filter['agent_id'] ) . '" ';
		}
		if ( ! empty( $filter['location_id'] ) ) {
			$selected_attributes .= ' data-selected-location="' . esc_attr( $filter['location_id'] ) . '" ';
		}
		if ( ! empty( $filter['show_agents'] ) ) {
			$selected_attributes .= ' data-show-agents="' . esc_attr( $filter['show_agents'] ) . '" ';
		}
		if ( ! empty( $filter['show_locations'] ) ) {
			$selected_attributes .= ' data-show-locations="' . esc_attr( $filter['show_locations'] ) . '" ';
		}


		if ( OsEventsHelper::is_service_slot_passed( $service, $start_date, $time_period ) ) {
			$service_box_html .= '<div class="le-service-wrapper is-passed" ' . $extra_attrs . '><div class="is-passed-message">' . esc_html__( 'This event has passed', 'latepoint' ) . '</div>';
		} else {
			$start_time_attr   = ( ( $time_period->end_time - $time_period->start_time ) > $service->duration ) ? '' : 'data-selected-start-time="' . esc_attr( $time_period->start_time ) . '"';
			$service_box_html .= '<div class="le-service-wrapper os_trigger_booking" ' . $extra_attrs . ' ' . $selected_attributes . '
			data-selected-service="' . esc_attr( $service->id ) . '" 
			data-selected-start-date="' . esc_attr( $start_date->format( 'Y-m-d' ) ) . '" 
			' . $start_time_attr . ' 
			data-hide-side-panel="yes">';
		}
		$service_box_html .= '<div class="le-service-inner">';
		$service_box_html .= '<div class="le-color-elem" style="background-color: ' . esc_attr( $service->bg_color ) . '"></div>';
		$service_box_html .= '<div class="le-service-name">' . esc_html( $service->name ) . '</div>';
		$service_box_html .= '<div class="le-service-time-period">' . OsTimeHelper::minutes_to_hours_and_minutes( $time_period->start_time ) . ' - ' . OsTimeHelper::minutes_to_hours_and_minutes( $time_period->end_time ) . '</div>';
		$service_box_html .= '</div>';
		$service_box_html .= '</div>';
		return $service_box_html;
	}

	public static function events_grid( OsWpDateTime $target_date, array $filter = [], $range_type = 'week', array $restrictions = [] ): string {
		$html = '';

		switch ( $range_type ) {
			case 'week':
				# Get bounds for a month of a targeted day
				$calendar_start = clone $target_date;
				// set monday as start of the week
				$start_of_the_week_day = 1;
				// figure out what week day is target and then find start and end of that week
				$shift = $calendar_start->format( 'N' ) - $start_of_the_week_day;
				if ( $shift ) {
					$calendar_start->modify( '-' . $shift . ' days' );
				}
				$calendar_end = clone $calendar_start;
				$calendar_end->modify( '+6 days' );

				$next_period = clone $calendar_start;
				$next_period->modify( '+7 days' );

				$prev_period = clone $calendar_start;
				$prev_period->modify( '-7 days' );
				break;
			case 'month':
				# Get bounds for a month of a targeted day
				$calendar_start = clone $target_date;
				$calendar_start->modify( 'first day of this month' );
				$calendar_end = clone $target_date;
				$calendar_end->modify( 'last day of this month' );

				$next_period = clone $target_date;
				$next_period->modify( 'first day of next month' );

				$prev_period = clone $target_date;
				$prev_period->modify( 'first day of previous month' );


				$weekday_for_first_day_of_month = $calendar_start->format( 'N' ) - 1;
				$weekday_for_last_day_of_month  = $calendar_end->format( 'N' ) - 1;


				if ( $weekday_for_first_day_of_month > 0 ) {
					$calendar_start->modify( '-' . $weekday_for_first_day_of_month . ' days' );
				}

				if ( $weekday_for_last_day_of_month < 6 ) {
					$days_to_add = 6 - $weekday_for_last_day_of_month;
					$calendar_end->modify( '+' . $days_to_add . ' days' );
				}
				break;
		}


		$now_datetime = OsTimeHelper::now_datetime_object();


		$services = new OsServiceModel();
		if ( ! empty( $restrictions['show_services'] ) ) {
			$services->where( [ 'id' => explode( ',', $restrictions['show_services'] ) ] );
		}
		$services = $services->should_be_active()->get_results_as_models();

		$services_with_resources = [];

		foreach ( $services as $service ) {
			$services_with_resources[ 'service_' . $service->id ] = [
				'model'        => $service,
				'work_periods' => [],
			];
			$booking_request                                      = new \LatePoint\Misc\BookingRequest(
				[
					'service_id' => $service->id,
				]
			);
			$services_with_resources[ 'service_' . $service->id ]['resources'] = OsResourceHelper::get_resources_grouped_by_day( $booking_request, $calendar_start, $calendar_end );
		}

		$clean_filter = [];
		foreach ( $filter as $filter_key => $filter_value ) {
			if ( ! empty( $filter_value ) ) {
				$clean_filter[ $filter_key ] = $filter_value;
			}
		}

		$filter_class = empty( $clean_filter ) ? '' : 'show-filters';

		$clean_filter        = array_merge( $clean_filter, $restrictions );
		$agent_restriction   = ! empty( $restrictions['show_agents'] ) ? explode( ',', $restrictions['show_agents'] ) : [];
		$service_restriction = ! empty( $restrictions['show_services'] ) ? explode( ',', $restrictions['show_services'] ) : [];

		$html     .= '<div class="latepoint-calendar-wrapper ' . $filter_class . '" data-route-name="' . OsRouterHelper::build_route_name( 'events', 'load_calendar_events' ) . '" data-target-date="' . $target_date->format( 'Y-m-d' ) . '">';
		$html     .= '<div class="latepoint-calendar-controls-wrapper">';
		$html     .= OsFormHelper::hidden_field( 'restrictions', wp_json_encode( $restrictions ) );
			$html .= '<div class="latepoint-calendar-controls">';
		if ( $range_type == 'week' ) {
			if ( $calendar_start->format( 'M' ) == $calendar_end->format( 'M' ) ) {
				$html .= '<div class="le-week">' . $calendar_start->format( 'M j' ) . ' - ' . $calendar_end->format( 'j' ) . '</div>';
			} else {
				$html .= '<div class="le-week">' . $calendar_start->format( 'M j' ) . ' - ' . $calendar_end->format( 'M j' ) . '</div>';
			}
		}
		if ( $range_type == 'month' ) {
			$html .= '<div class="le-month">' . OsUtilHelper::get_month_name_by_number( $target_date->format( 'n' ) ) . '</div>';
		}
			$html     .= '<div class="le-range-selector">' . OsFormHelper::select_field(
				'calendar_range_type',
				false,
				[
					'week'  => __( 'Weekly', 'latepoint' ),
					'month' => __( 'Monthly', 'latepoint' ),
				],
				$range_type 
			) . '</div>';
			$html     .= '<div class="le-filter le-filter-trigger"><i class="latepoint-icon latepoint-icon-ui-47"></i><span>' . __( 'Filters', 'latepoint' ) . '</span></div>';
			$html     .= OsFormHelper::hidden_field( 'target_date_string', $target_date->format( 'Y-m-d' ) );
			$html     .= '<div class="le-navigation-wrapper">';
			$html     .= '<div class="le-navigation">
									<div class="le-navigation-button le-navigation-trigger" data-target-date="' . $prev_period->format( 'Y-m-d' ) . '"><i class="latepoint-icon latepoint-icon-arrow-left"></i></div>
									<div class="le-today le-navigation-trigger" data-target-date="' . $now_datetime->format( 'Y-m-d' ) . '">' . __( 'Today', 'latepoint' ) . '</div>
									<div class="le-navigation-button le-navigation-trigger" data-target-date="' . $next_period->format( 'Y-m-d' ) . '"><i class="latepoint-icon latepoint-icon-arrow-right"></i></div>
								</div>';
			$html     .= '</div>';
			$html     .= '</div>';
			$html     .= '<div class="latepoint-calendar-filters">';
				$html .= '<div class="le-filters-label">' . __( 'Show:', 'latepoint' ) . '</div>';
				$html .= OsFormHelper::select_field(
					'filter[agent_id]',
					false,
					array_merge(
						[
							[
								'value' => '',
								'label' => __( 'All Agents', 'latepoint' ),
							],
						],
						OsAgentHelper::get_agents_list( false, $agent_restriction ) 
					),
					$clean_filter['agent_id'] ?? '' 
				);
				$html .= OsFormHelper::select_field(
					'filter[service_id]',
					false,
					array_merge(
						[
							[
								'value' => '',
								'label' => __( 'All Services', 'latepoint' ),
							],
						],
						OsServiceHelper::get_services_list( false, $service_restriction ) 
					),
					$clean_filter['service_id'] ?? '' 
				);
			$html     .= '</div>';
		$html         .= '</div>';

		switch ( $range_type ) {
			case 'week':
				$html .= self::week_grid( $calendar_start, $calendar_end, $target_date, $services_with_resources, $clean_filter );
				break;
			case 'month':
				$html .= self::month_grid( $calendar_start, $calendar_end, $target_date, $services_with_resources, $clean_filter );
				break;
		}

		$html .= '</div>';
		return $html;
	}

	private static function is_service_slot_passed( OsServiceModel $service, OsWpDateTime $start_date, \LatePoint\Misc\TimePeriod $time_period ): bool {
		$now_datetime   = OsTimeHelper::now_datetime_object();
		$is_day_in_past = ( $start_date->format( 'Y-m-d' ) < $now_datetime->format( 'Y-m-d' ) );
		$is_today       = ( $start_date->format( 'Y-m-d' ) == $now_datetime->format( 'Y-m-d' ) );

		try {
			$is_passed = ( $is_day_in_past || $is_today && ( OsWpDateTime::os_createFromFormat( LATEPOINT_DATETIME_DB_FORMAT, $start_date->format( 'Y-m-d' ) . ' ' . OsTimeHelper::minutes_to_army_hours_and_minutes( $time_period->end_time - $service->duration ) . ':00' ) < $now_datetime ) );
		} catch ( Exception $e ) {
			$is_passed = false;
		}
		return $is_passed;
	}
}
