<?php
/**
 * GFB Calendar Class
 *
 * @package GFB.
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

if ( ! class_exists( 'APBGF_Calendar' ) ) {
	
	class APBGF_Calendar {        
		/**
		 * Instance
		 *
		 * @var $instance
		 */
		private static $instance = null;

		/**
		 * Init the Dependencies.
		 */
		private function __construct() {            

			add_action('apbgf_after_menu_register', array( $this, 'register_menu' ));

			add_action('admin_enqueue_scripts', array( $this, 'register_calendar_admin_assets' ));

			add_action('wp_ajax_apbgf_month_changer', array( $this, 'apbgf_month_changer' ));
			add_action('wp_ajax_nopriv_apbgf_month_changer', array( $this, 'apbgf_month_changer' ));

			add_action('wp_ajax_apbgf_show_slots_by_date', array( $this, 'apbgf_show_slots_by_date' ));
			add_action('wp_ajax_nopriv_apbgf_show_slots_by_date', array( $this, 'apbgf_show_slots_by_date' ));

			add_action('wp_ajax_apbgf_check_booked_slots_per_day', array( $this, 'apbgf_check_booked_slots_per_day' ));
			add_action('wp_ajax_nopriv_apbgf_check_booked_slots_per_day', array( $this, 'apbgf_check_booked_slots_per_day' ));

			add_action('admin_footer', array( $this, 'apbgf_booking_form_modal' ));
		}

		public function apbgf_booking_form_modal() {
			global $current_screen;

			if ( isset($current_screen->id) && 'gravity-booking_page_apbgf-calendar' === $current_screen->id ) {
				//adding customer modal view file
				require_once APBGF_ADMIN_VIEW_PATH . 'modal/booking-form-modal.php';
			}           
		}

		public function apbgf_check_booked_slots_per_day() {
			check_ajax_referer( 'appointment-and-booking-for-gravity-forms', 'nonce' );

			$result = false;
			$staff_id    = isset( $_POST['staff_id'] ) ? absint( $_POST['staff_id'] ) : 0;
			$date        = isset( $_POST['date'] ) ? sanitize_text_field( wp_unslash( $_POST['date'] ) ) : '';
			$service_id  = isset( $_POST['service_id'] ) ? sanitize_text_field( wp_unslash( $_POST['service_id'] ) ) : 'all';
			$location_id = isset( $_POST['location_id'] ) ? sanitize_text_field( wp_unslash( $_POST['location_id'] ) ) : 'all';

			if ( ! empty( $staff_id ) && ! empty( $date ) ) {
				$staffData = APBGF_Staff::get_staff_by_id( $staff_id );
				$daySName = strtolower( gmdate('D', strtotime($date)) );
				if ( isset($staffData['timeslots'][ $daySName ]) ) {                  
					$result = APBGF_Appointment::apbgf_check_day_available_with_slots( $staff_id, $date, $staffData, $daySName );
				}
			}

			echo esc_attr( $result );
			wp_die();
		}

		public function apbgf_show_slots_by_date() {
			check_ajax_referer( 'appointment-and-booking-for-gravity-forms', 'nonce' );
			$staff_id = isset( $_POST['staff_id'] ) ? absint( $_POST['staff_id'] ) : 0;
			$service_id = isset( $_POST['service_id'] ) ? sanitize_text_field( wp_unslash( $_POST['service_id'] ) ) : 'all';
			$date = isset( $_POST['date'] ) ? sanitize_text_field( wp_unslash( $_POST['date'] ) ) : '';
			$location_id = isset( $_POST['location_id'] ) ? sanitize_text_field( wp_unslash( $_POST['location_id'] ) ) : 'all';
			
			if ( ! empty( $staff_id ) && 'all' != $staff_id ) {
				if ( ! empty( $date ) && 'all' != $date ) {

					$daySName = strtolower( gmdate('D', strtotime($date)) );

					$formated_date = gmdate('d, Y', strtotime($date));

					$month_name = gmdate('F', strtotime($date));

					$staffData = APBGF_Staff::get_staff_by_id( $staff_id );

					$front_formated_date = gmdate('l, F d Y', strtotime($date));

					$multiple_booking = false;

					if ( isset( $staffData['services'] ) && is_array( $staffData['services'] ) && count( $staffData['services'] ) > 0 ) {
						
						if ( isset( $staffData['services'][ $service_id ]['available'] ) && 'yes' == $staffData['services'][ $service_id ]['available'] ) {
							$service_price = $staffData['services'][ $service_id ]['price'];
							$service_price = ( '' == $service_price ) ? APBGF_Service::get_service_by_id($service_id)['price'] : $service_price;
						} else {
							// Translators: %1$s is for Current Staff
							printf('<p class="apbgf-slot-error">%1$s</p>', esc_html__('Currently staff is not providing this service', 'appointment-and-booking-for-gravity-forms'));
							wp_die();   
						}
					} else {
						// translators: %1$s is for Current Staff
						printf('<p class="apbgf-slot-error">%1$s</p>', esc_html__('Currently staff is not providing this service', 'appointment-and-booking-for-gravity-forms'));
						wp_die();
					}

					if ( isset( $staffData['holidays'] ) && is_array( $staffData['holidays'] ) && count( $staffData['holidays'] ) > 0 ) {               
						$staff_holidays = $staffData['holidays'];
					} else {
						$staff_holidays = array();
					}

					if ( isset( $staffData['holidays'] ) && is_array( $staffData['holidays'] ) && count( $staffData['holidays'] ) > 0 && in_array( $date, $staff_holidays ) ) {                      
						// Translators: %1$s is for Staff message   
						printf('<p class="apbgf-slot-error">%1$s</p>', esc_html__('This date is marked as holiday', 'appointment-and-booking-for-gravity-forms'));
						wp_die();
					}

					// Determine the timezone string
					if ( ! empty( $staffData['timezone'] ) ) {
						$timezone_string = apbgf_timezone_string( $staff_id ); // Staff timezone
					} else {
						$timezone_string = apbgf_timezone_string(); // WordPress timezone
					}

					// Create DateTime in correct timezone
					$timezone     = new DateTimeZone( $timezone_string );
					$datetime_obj = new DateTime( 'now', $timezone );

					// Format the current time
					$current_time = apply_filters( 'apbgf_check_current_time', $datetime_obj->format( 'Y-m-d H:i' ) );

					$special_day = false;
					if ( isset( $staffData['spl_day_timeslots'] ) && is_array( $staffData['spl_day_timeslots'] ) && isset( $staffData['spl_day_timeslots'][ $date ] ) ) {
						#continue.
						$staff_timeslots = $staffData['spl_day_timeslots'][ $date ];
						$index_of_timings = $staff_timeslots[ $daySName ]['index'];
						$staff_timings = $staffData['spl_day_timings'][ $index_of_timings ];                  
						// if found special day event then ignore normal timings.
						$special_day = true;
					}

					if ( !$special_day ) {
						
						if ( isset( $staffData['timeslots'] ) && is_array( $staffData['timeslots'] ) && count( $staffData['timeslots'] ) > 0 ) {
							#continue.
							$staff_timeslots = $staffData['timeslots'];
							$staff_timings = $staffData['timings'];
						} else {                        
							// Translators: %1$s is for Staff message 
							printf('<p class="apbgf-slot-error">%1$s</p>', esc_html__('No slots is available for this staff', 'appointment-and-booking-for-gravity-forms'));
							wp_die();
						}

						//For fullday booking!!
						if ( isset( $staffData['booking_type'] ) && 'fullday' == $staffData['booking_type'] ) {
							//Full Day Code will be placed here!
							$response['booking_type'] = 'fullday';                      
							$response['date'] = $date;
							$response['time'] = '00:00 - 23:59';
							$response['service_price'] = $service_price;
							$response['multiple_booking'] = $multiple_booking;
							$response['front_formated_date'] = $front_formated_date;
							$html = '';
							$month_name = apbgf_translable_name('months', strtolower($month_name));
							$placeholder = apply_filters('apbgf_staff_time_slot_capacity_placeholder', __('Enter Slot Capacity', 'appointment-and-booking-for-gravity-forms'));
							$staff_id = isset($_POST['staff_id']) ? sanitize_text_field(wp_unslash($_POST['staff_id'])) : '';
							$total_slots_consumed = (int) APBGF_Appointment::apbgf_all_appointment_by_date_time( $staff_id, $date, '00:00 - 23:59', 'fullday', $service_id, $location_id );
							$slot_capacity = (int) $staff_timings[ $daySName ]['capacity'];
							$remaining_capacity = (int) $slot_capacity - $total_slots_consumed;
							// translators: %1$s is the number of slot left
							$single_slot = sprintf(esc_html__('%1$s slot left', 'appointment-and-booking-for-gravity-forms'), $remaining_capacity);
							// translators: %1$s is the number of slots left
							$multiple_slot = sprintf(esc_html__('%1$s slots left', 'appointment-and-booking-for-gravity-forms'), $remaining_capacity);
							$tooltip = ( 1 >= $remaining_capacity ) ? $single_slot : $multiple_slot;
							$filter_slot = apply_filters('apbgf_staff_time_slot', __('Fullday', 'appointment-and-booking-for-gravity-forms'), $staffData);
							if ( $remaining_capacity > 0 ) {
								$default_capacity = 1;  
							} else {
								$default_capacity = 0;
							}

							$default_capacity = apply_filters('apbgf_show_default_capacity', $default_capacity, $remaining_capacity);
							
							// if multiple booking is enable also hide capactiy
							//conitnue from here on 26 June 2023
							if ( ! apply_filters('apbgf_show_slot_list', true) || $multiple_booking ) {
								$response['html'] = '';
								echo wp_json_encode( $response );
								wp_die(); //terminate immediately.
							}

							// Translators: %1$s is for slot totle and %2$s is for formated date
							$slot_title = apply_filters('apbgf_slot_title', sprintf('%1$s %2$s', esc_attr($month_name), esc_attr($formated_date)), sanitize_text_field($date));

							ob_start();
							?>						
							<h4 class="slot-title test-1"><?php echo wp_kses_post($slot_title); ?></h4>
							<ul class="apbgf-slot-list">
								<li class="apbgf-tooltip">
									<span><?php echo esc_attr($filter_slot); ?></span>
									<input class="apbgf-selected-capacity" disabled="true" type="number" placeholder="<?php echo esc_html($placeholder); ?>" min="1" max="<?php echo esc_html($remaining_capacity); ?>" value="<?php echo esc_attr($default_capacity); ?>" />
									<input class="apbgf-selected-time" type="hidden" value="00:00 - 23:59" />
									<input class="apbgf-selected-date" type="hidden" value="<?php echo esc_attr(sanitize_text_field($date)); ?>" />
									
									<input class="apbgf-selected-price" type="hidden" value="<?php echo esc_attr( sanitize_text_field( $service_price ) ); ?>" />
									<span class="apbgf-tooltip-text"><?php echo esc_html($tooltip); ?></span>
								</li>
							</ul>
							<?php
							$html = ob_get_clean(); 
							$response['html'] = $html;
							echo wp_json_encode( $response );
							wp_die();
						}
					}


					
					if (isset($staff_timeslots[ $daySName ]['slot']) && is_array($staff_timeslots[ $daySName ]['slot']) && count($staff_timeslots[ $daySName ]['slot']) > 0) {
						$html = '';
						$staff_id = isset($_POST['staff_id']) ? sanitize_text_field(wp_unslash($_POST['staff_id'])) : '';
						$lead_time_meta = get_post_meta($staff_id, 'gbf_lead_time', true);
						$apbgf_settings = APBGF_Setting::get_settings();
						$lead_time = !empty($lead_time_meta) ? $lead_time_meta : $apbgf_settings['gbf_lead_time'];
						$current_time = new DateTime();

						$current_time_formatted = $current_time->format('Y-m-d H:i');
						$lead_time_limit = clone $current_time;
						$lead_time_limit->modify('+' . intval($lead_time) . ' minute');
						
						foreach ( $staff_timeslots[ $daySName ]['slot'] as $key => $slot ) {
							if ($special_day) {
								$staff_breaks = isset($staff_timings['break']) ? $staff_timings['break'] : '';
							} else {
								$staff_breaks = isset($staff_timings[ $daySName ]['break']) ? $staff_timings[ $daySName ]['break'] : '';
							}

							$time_break = false;
							if (is_array($staff_breaks) && ( in_array($staff_timeslots[ $daySName ]['start'][ $key ], $staff_breaks) || in_array($staff_timeslots[ $daySName ]['slot'][ $key ], $staff_breaks) )) {
								$time_break = true;
							}

							$total_slots_consumed = (int) APBGF_Appointment::apbgf_all_appointment_by_date_time($staff_id, $date, $slot, 'custom', $service_id, $location_id);
							$slot_capacity = (int) $staff_timeslots[ $daySName ]['capacity'][ $key ];
							$remaining_capacity = (int) $slot_capacity - $total_slots_consumed;
							if ($remaining_capacity > 0) {
								$default_capacity = 1;
							} else {
								$default_capacity = 0;
							}

							$default_capacity = apply_filters('apbgf_show_default_capacity', $default_capacity, $remaining_capacity);

							$check_time = gmdate($date . ' ' . $staff_timeslots[ $daySName ]['start'][ $key ]);
							$check_time = new DateTime($check_time);


							if ( $check_time > $lead_time_limit ) {
								if (!$time_break && strtotime($check_time->format('Y-m-d H:i')) > strtotime($current_time_formatted)) {
									$class = ( 0 >= $remaining_capacity ) ? 'apbgf-unavailable' : '';
									$filter_slot = apply_filters('apbgf_staff_time_slot', $slot, $staffData);
									$placeholder = apply_filters('apbgf_staff_time_slot_capacity_placeholder', esc_html__('Enter Slot Capacity', 'appointment-and-booking-for-gravity-forms'));
									// translators: %1$s is the number of slot left
									$single_slot = sprintf(esc_html__('%1$s slot left', 'appointment-and-booking-for-gravity-forms'), $remaining_capacity);
									// translators: %1$s is the number of slots left
									$multiple_slot = sprintf(esc_html__('%1$s slots left', 'appointment-and-booking-for-gravity-forms'), $remaining_capacity);
									$tooltip = ( 1 >= $remaining_capacity ) ? $single_slot : $multiple_slot;
									
									if (( isset($apbgf_settings['hide_slot_capacity']) && 'enabled' == $apbgf_settings['hide_slot_capacity'] ) ) {
										$slot_field_type = 'hidden';
									} else {
										$slot_field_type = 'number';
									}

									$service_price = apply_filters('apbgf_modify_service_price', $service_price, $date, $daySName, $staff_timeslots[ $daySName ]['start'][ $key ], $staff_timeslots[ $daySName ]['end'][ $key ]);

									$html .= '
									<li class="' . $class . ' apbgf-tooltip">
									<span>' . $filter_slot . '</span>
									<input class="apbgf-selected-capacity" disabled="true" type="' . $slot_field_type . '" placeholder="' . $placeholder . '" min="1" max="' . $remaining_capacity . '" value="' . $default_capacity . '" />
									<input class="apbgf-selected-time" type="hidden" value="' . $slot . '" />
									<input class="apbgf-selected-date" type="hidden" value="' . $date . '" />
									<input class="apbgf-selected-price" type="hidden" value="' . $service_price . '" />
									<span class="apbgf-tooltip-text">' . $tooltip . '</span>
									</li>
									';
								}
							}
						}
						if ('' == $html) {
							// Translators: %1$s is for slot message
							printf('<p class="apbgf-slot-error">%1$s</p>', esc_html__('No slot left for the day', 'appointment-and-booking-for-gravity-forms'));
							wp_die();
						}

						$month_name = apbgf_translable_name('months', strtolower($month_name));
						// Translators: %1$s is for slot title and %2$s is for formated date
						$slot_title = apply_filters('apbgf_slot_title', sprintf('%1$s %2$s', esc_attr($month_name), esc_attr($formated_date)), sanitize_text_field($date));
						?>
						<h4 class="slot-title test-2"><?php echo wp_kses_post($slot_title); ?></h4>
						<ul class="apbgf-slot-list">
							<input type="hidden" id="multiple-by-staff" value="<?php echo esc_attr( $staffData['multiple_booking'] ) ; ?>">
							<?php
								// Define allowed HTML tags and attributes
								
								// Escape and output the HTML safely
								echo $html;
							?>
						</ul>
						<?php
					} else {                        
						// Translators: %1$s is for slot message                        
						printf('<p class="apbgf-slot-error">%1$s</p>', esc_html__('No slots is available for the staff', 'appointment-and-booking-for-gravity-forms'));
						wp_die();
					}

				} else {                    
					// Translators: %1$s is for slot message
					printf('<p class="apbgf-slot-error">%1$s</p>', esc_html__('No slots is available for the staff', 'appointment-and-booking-for-gravity-forms'));
					wp_die();   
				}
			} else {
				// Translators: %1$s is for Staff message
				printf('<p class="apbgf-slot-error">%1$s</p>', esc_html__('No slots is available for the staff', 'appointment-and-booking-for-gravity-forms'));            
				wp_die();
			}

			wp_die();
		}

		

		public function apbgf_month_changer() {
			check_ajax_referer( 'appointment-and-booking-for-gravity-forms', 'nonce' );

			$location_id = isset( $_POST['location_id'] ) ? sanitize_text_field( wp_unslash( $_POST['location_id'] ) ) : 'all';
			$service_category_id = isset( $_POST['service_category_id'] ) ? sanitize_text_field( wp_unslash( $_POST['service_category_id'] ) ) : 'all';
			$service_id = isset( $_POST['service_id'] ) ? sanitize_text_field( wp_unslash( $_POST['service_id'] ) ) : 'all';
			$staff_id = isset( $_POST['staff_id'] ) ? sanitize_text_field( wp_unslash( $_POST['staff_id'] ) ) : 'all';
			$is_admin = isset( $_POST['is_admin'] ) ? sanitize_text_field( wp_unslash( $_POST['is_admin'] ) ) : '0';
			$form_id = isset( $_POST['form_id'] ) ? sanitize_text_field( wp_unslash( $_POST['form_id'] ) ) : '';
			$prior_months = isset( $_POST['prior_months'] ) ? sanitize_text_field( wp_unslash( $_POST['prior_months'] ) ) : '';
			$date = isset( $_POST['date'] ) ? sanitize_text_field( wp_unslash( $_POST['date'] ) ) : '';
			if ( ! isset( $location_id ) ) {
				$location_id = 'all';
			}

			if ( ! isset( $service_category_id ) ) {
				$service_category_id = 'all';
			}

			if ( ! isset( $service_id ) ) {
				$service_id = 'all';
			}

			if ( ! isset( $staff_id ) ) {
				$staff_id = 'all';
			}

			if ( isset( $is_admin ) && '1' == $is_admin ) {               

				$appointmentData = APBGF_Appointment::apbgf_show_appointments_on_calendar( $location_id, $service_category_id, $service_id, $staff_id );

				if ( isset( $location_id ) && isset( $service_id ) && isset( $staff_id ) ) {
					$args = array(
						'location_id' => ! empty( $location_id ) ? $location_id : 'all',
						'service_category_id' => ! empty( $service_category_id ) ? $service_category_id : 'all',
						'service_id' => ! empty( $service_id ) ? $service_id : 'all',
						'staff_id' => ! empty( $staff_id ) ? $staff_id : 'all',
					);
				}

				$args = apply_filters( 'apbgf_month_changer_args', $args );
				self::render( $appointmentData, $args );

			} elseif ( isset( $form_id ) && isset( $location_id ) && isset( $service_id ) && isset( $staff_id ) ) {


					$args = array(
						'location_id' => isset( $location_id ) ? $location_id : 'all',
						'service_category_id' => $service_category_id,
						'service_id' => $service_id,
						'staff_id' => $staff_id,
					);              
					$args = apply_filters( 'apbgf_month_changer_args', $args );
					self::render_frontend( $form_id, $args );
			}

			wp_die();
		}

		public function register_calendar_admin_assets() {

			if ( APBGF_LIVE_MODE ) {
				$minimized = '.min';
				$clear_cache = '';
			} else {
				$minimized = '';
				$clear_cache = '?t=' . gmdate('ymdhis');
			}

			$apbgf_settings = APBGF_Setting::get_settings();

			wp_register_style( 'apbgf-calendar-style', APBGF_ADMIN_CSS_URL . 'apbgf-calendar-style' . $minimized . '.css', array(), APBGF_VERSION . $clear_cache, 'all' );      

			wp_register_script('apbgf-calendar-script', APBGF_ADMIN_JS_URL . 'apbgf-calendar-script' . $minimized . '.js', array( 'jquery' ), APBGF_VERSION . $clear_cache, true);
			wp_localize_script('apbgf-calendar-script', 'apbgf_calendar', array(
				'ajaxurl'  => admin_url('admin-ajax.php'),
				'nonce' => wp_create_nonce('appointment-and-booking-for-gravity-forms'),
				'prior_months' => '',
				'show_timeslot' => $apbgf_settings['apbgf_show_timeslot'],
			));

			if ( is_admin() ) {

					//load assets
				wp_enqueue_style('apbgf-calendar-style');
				wp_enqueue_script('apbgf-calendar-script');
			}
		}

		public static function render_frontend( $form_id = '', $args = array() ) {          

			if ( empty( $form_id ) ) {
				echo esc_html__('Calendar can\'t be load due to form id is missing.', 'appointment-and-booking-for-gravity-forms');
				return false;
			}

			$staff_special_timeslots = array();
			$form = GFAPI::get_form( $form_id );
			$field = GFAPI::get_fields_by_type( $form, array( 'apbgf_appointment_calendar' ), true );
			$location_hide = false ;
			$service_hide = false ;
			$staff_hide = false;
			$service_category_hide = false; 
			$location_id = 0 ;
			$service_id = 0 ;
			$staff_id = 0; 
			$service_category_id  = '';     
			
			if ( isset($field[0]) ) {

				if ( isset($field[0]->apbgf_service_category_hide) && $field[0]->apbgf_service_category_hide ) {
					$service_category_hide = $field[0]->apbgf_service_category_hide;
				} else {
					$service_category_hide = false;
				}

				if ( isset($field[0]->apbgf_service_category) ) {
					$service_category_id = $field[0]->apbgf_service_category;
				}


				if ( isset($field[0]->apbgf_location_hide) && $field[0]->apbgf_location_hide ) {
					$location_hide = $field[0]->apbgf_location_hide;
				} else {
					$location_hide = false;
				}

				if ( isset($field[0]->apbgf_location) ) {
					$location_id = $field[0]->apbgf_location;
				}

				if ( isset($field[0]->apbgf_service_hide) && $field[0]->apbgf_service_hide ) {
					$service_hide = $field[0]->apbgf_service_hide;
				} else {
					$service_hide = false;
				}

				if ( isset($field[0]->apbgf_service) ) {
					$service_id = $field[0]->apbgf_service;
				}

				if ( isset($field[0]->apbgf_staff_hide) && $field[0]->apbgf_staff_hide ) {
					$staff_hide = $field[0]->apbgf_staff_hide;
				} else {
					$staff_hide = false;
				}

				if ( isset($field[0]->apbgf_staff) ) {
					$staff_id = $field[0]->apbgf_staff;
				}
			}

			if ( isset( $args['prior_months'] ) && ! empty( $args['prior_months'] ) ) {
				$prior_months = $args['prior_months'];
			} else {
				$prior_months = 3;
			}

			if ( isset( $args['service_category_id'] ) && ! empty( $args['service_category_id'] ) ) {
				$service_category_id = $args['service_category_id'];
			}

			if ( isset( $args['location_id'] ) && ! empty( $args['location_id'] ) ) {
				$location_id = $args['location_id'];
			}

			if ( isset( $args['service_id'] ) && ! empty( $args['service_id'] ) ) {
				$service_id = $args['service_id'];
			}

			if ( isset( $args['staff_id'] ) && ! empty( $args['staff_id'] ) ) {
				$staff_id = $args['staff_id'];
			}   

			$service_category_error = apply_filters( 'apbgf_service_category_error', __('Please select Category.', 'appointment-and-booking-for-gravity-forms') );
			$location_error        = apply_filters( 'apbgf_location_error', __('Please select location.', 'appointment-and-booking-for-gravity-forms') );
			$service_error         = apply_filters( 'apbgf_service_error', __('Please select service.', 'appointment-and-booking-for-gravity-forms') );
			$staff_error           = apply_filters('apbgf_staff_error', __('Please select staff', 'appointment-and-booking-for-gravity-forms') );




			if ( ! $location_hide && ( 'all' == $location_id || empty($location_id) ) ) {
				echo esc_attr($location_error);
				return false;
			}
			
			if ( ! $service_category_hide && ( 'all' == $service_category_id || empty($service_category_id) ) ) {
				echo esc_attr($service_category_error);
				return false;
			}

			if ( empty( $service_id ) || 'all' == $service_id ) {
				echo esc_attr( $service_error );
				return false;
			}

			if ( empty( $staff_id ) || 'all' == $staff_id ) {           
				echo esc_attr($staff_error);
				return false;
			}

			$staffData = APBGF_Staff::get_staff_by_id( $staff_id ); 
			

			if ( ! $location_hide && ( 'all' != $location_id && !empty($location_id) ) ) {               

				$flag = false;

				if ( isset($staffData['location']) && is_array($staffData['location']) && count($staffData['location']) > 0 ) {                 

					if ( in_array($location_id, $staffData['location']) ) {
						$flag = true;
					}

				} else {
					$flag = true;
				}       

				if ( !$flag ) {
					echo esc_html__('Staff currently not available to this location.', 'appointment-and-booking-for-gravity-forms');
					return $flag;
				}
			}

			if ( $location_hide && ( 'all' != $location_id && !empty($location_id) ) ) {             
				$flag = false;

				if ( isset($staffData['location']) && is_array($staffData['location']) && count($staffData['location']) > 0 ) {

					if ( in_array($location_id, $staffData['location']) ) {
						$flag = true;
					}

				} else {
					$flag = true;
				}

				if ( !$flag ) {
					echo esc_html__('Staff currently not available to this location.', 'appointment-and-booking-for-gravity-forms');
					return $flag;
				}
			}

			if ( 'all' != $service_id && !empty($service_id ) ) {

				$flag = false;

				if ( isset($staffData['services']) && is_array($staffData['services']) && count($staffData['services']) > 0 ) {

					if ( array_key_exists($service_id, $staffData['services']) ) {
						$flag = true;
					}
				}

				if ( !$flag ) {
					echo esc_html__('The staff is not providing this service.', 'appointment-and-booking-for-gravity-forms');
					return $flag;
				}
			}           

			$timezone_string = ( '' !== $staffData['timezone'] ) ? apbgf_timezone_string($staff_id) : apbgf_timezone_string();

			$datetime_zone = new DateTimeZone( $timezone_string );

			// Example: creating a DateTime object using the correct timezone.
			$datetime = new DateTime( 'now', $datetime_zone );

			$special_day = false;
			$staff_special_timeslots = array();
			$staff_special_timings = array();
			$multiple_booking = false;

			if ( isset( $staffData['spl_day_timeslots'] ) && is_array( $staffData['spl_day_timeslots'] ) && count( $staffData['spl_day_timeslots'] ) > 0 ) {
				#continue.
				$staff_special_timeslots = $staffData['spl_day_timeslots'];             
				$staff_special_timings = $staffData['spl_day_timings']; 
				// atleast special day timings are found.
				$special_day = true;
			}
			

			$staff_timings = array();
			if ( isset( $staffData['timings'] ) && is_array( $staffData['timings'] ) && count( $staffData['timings'] ) > 0 ) {
				#continue.
				$staff_timings = $staffData['timings'];
			} elseif ( !$special_day ) {
					echo esc_html__('No timeslot is available for this staff.', 'appointment-and-booking-for-gravity-forms');
					return false;
			}

			if ( isset( $staffData['holidays'] ) && is_array( $staffData['holidays'] ) && count( $staffData['holidays'] ) > 0 ) {               
				$staff_holidays = $staffData['holidays'];
			} else {
				$staff_holidays = false;
			}
			
			// Get prev & next month
			// phpcs:ignore
			$date = isset( $_POST['date'] ) ? sanitize_text_field( wp_unslash( $_POST['date'] ) ) : '';
	
			if ( ! empty( $date ) ) {
				$ym = $date;
			} else {
				$ym = apply_filters( 'apbgf_frontend_calendar_init', gmdate('Y-m'));
			}

			// Check format
			$timestamp = strtotime($ym . '-01');        
			if ($timestamp === false) {             
				$ym = date('Y-m');//phpcs:ignore 
				$timestamp = strtotime($ym . '-01');
			}

			// Today
			$today = date('Y-m-d', time()); //phpcs:ignore 

			/*CT-284*/
			$apbgf_settings = APBGF_Setting::get_settings();
			$numberOfDates = isset($apbgf_settings['apbgf_prior_days_book_appointment']) ? $apbgf_settings['apbgf_prior_days_book_appointment'] : 0;        
			$nextDates = array();
			if ( ! empty($numberOfDates) ) {
				$currentDate = new DateTime(); // Create a DateTime object with the current date
				for ($i = 1; $i <= $numberOfDates; $i++) {
					if ( 1 == $i && 0 < $numberOfDates ) {
						$nextDates[] = $today;  
					}

					if ( 1 < $numberOfDates ) {
						$nextDate = $currentDate->add(new DateInterval('P1D')); // Add 1 day to the current date
						$nextDates[] = $nextDate->format('Y-m-d'); // Store the formatted date in the array
					}
				}
			}           
			/*CT-284*/

			// For H3 title
			$current_month = date('F', $timestamp); //phpcs:ignore 
			$current_year = date('Y', $timestamp); //phpcs:ignore 
			$html_title = apbgf_translable_name('months', strtolower($current_month)) . ' ' . $current_year;

			// Create prev & next month link     mktime(hour,minute,second,month,day,year)
			//phpcs:ignore 
			$prev = date('Y-m', mktime(0, 0, 0, date('m', $timestamp) - 1, 1, date('Y', $timestamp)));
			//phpcs:ignore 
			$next = date('Y-m', mktime(0, 0, 0, date('m', $timestamp) + 1, 1, date('Y', $timestamp)));
			//echo 'prior months is ' . $prior_months . '<br>';
			//phpcs:ignore 
			$prior_next = date('Y-m', strtotime( date('Y-m-01') . " +$prior_months months" ));
			//echo 'prior months is ' . $prior_next;
			//phpcs:ignore 
			$curr = date('Y-m'); // get the current year and month
			$disabled_prev = true;
			$disabled_next = true;

			$check_prev = explode('-', $prev); // take year and month as an array to compare
			$check_curr = explode('-', $curr); // take year and month as an array to compare

			// Number of days in the month
			//phpcs:ignore 
			$day_count = date('t', $timestamp);

			//echo 'total days ' . $day_count;

			// 0:Sun 1:Mon 2:Tue ...
			if ( isset( get_option('apbgf_settings', array())['general']['gbf_start_of_week'] ) && 'sunday' == get_option('apbgf_settings', array())['general']['gbf_start_of_week'] ) {                
				//phpcs:ignore 
				$str = date('w', mktime(0, 0, 0, date('m', $timestamp), 1, date('Y', $timestamp)));
			} else {   
				//phpcs:ignore              
				$str = date('w', mktime(0, 0, 0, date('m', $timestamp), 1, date('Y', $timestamp))) - 1;
				if ( $str == -1 ) {
					$str = 6;
				}
			}

			// Create Calendar!!
			$weeks = array();
			$week = '';

			// Add empty cell
			$week .= str_repeat('<td></td>', $str); 

			for ( $day = 1; $day <= $day_count; $day++, $str++) {

				$date = $ym . '-' . $day;
				$date = gmdate( 'Y-m-d', strtotime($date) ); // Ensure $date is always set for each day

				// get day short name
				$daySName = strtolower( date('D', strtotime($date)) ); //phpcs:ignore                

				$class = 'apbgf-not-available';
				$hdnInput = '';             

				if ( is_array( $staff_special_timeslots ) && isset( $staff_special_timeslots[ $date ] ) ) {

					$special_day = true;
					if ( false != $staff_holidays && in_array( $date, $staff_holidays ) && $date >= $today ) {
						$class = 'apbgf-holiday';
					} elseif ( $date >= $today) {
							$checkDayAvaible = APBGF_Appointment::apbgf_check_day_available_with_slots( $staff_id, $date, $staffData, $daySName, 'special', $location_id, $service_id );
						if ( $checkDayAvaible ) {
							$class = 'apbgf-available';
							$hdnInput = '<input type="hidden" class="apbgf-hdn" value="' . $date . '" />';
						}
					}
				} else {                    
					$special_day = false;
				}


				if ( !$special_day && isset( $staff_timings[ $daySName ] ) ) {                    
					if ( false != $staff_holidays && in_array( $date, $staff_holidays ) && $date >= $today ) {
						$class = 'apbgf-holiday';
					} elseif ( $date >= $today) {
							/* Fullday */
						if ( isset( $staffData['booking_type'] ) && 'fullday' == $staffData['booking_type'] ) { 
							//check if is fullday booking type
							$total_slots_consumed = (int) APBGF_Appointment::apbgf_all_appointment_by_date_time( $staff_id, $date, '00:00 - 23:59', 'fullday', $service_id, $location_id );

							$slot_capacity = (int) $staff_timings[ $daySName ]['capacity'];

							$remaining_capacity = $slot_capacity - $total_slots_consumed;
							/* translators: %1$s is the number of slot left */
							$single_slot = sprintf(esc_html__('%1$s slot left', 'appointment-and-booking-for-gravity-forms'), $remaining_capacity);
							/* translators: %1$s is the number of slots left */
							$multiple_slot = sprintf(esc_html__('%1$s slots left', 'appointment-and-booking-for-gravity-forms'), $remaining_capacity);
							$tooltip = ( 1 >= $remaining_capacity ) ? $single_slot : $multiple_slot;        	
						                    
							if ( $date > $today && $remaining_capacity > 0 ) {
								$class = 'apbgf-available apbgf-tooltip';
								$data_multiple = '';
								$hdnInput = '<input type="hidden" ' . $data_multiple . ' class="apbgf-hdn" value="' . $date . '" />';
								$hdnInput .= '<span class="apbgf-tooltip-text">' . $tooltip . '</span>';
							}
						} else {                                
							$checkDayAvaible = APBGF_Appointment::apbgf_check_day_available_with_slots( $staff_id, $date, $staffData, $daySName, 'normal', $location_id, $service_id );

							if ( $checkDayAvaible ) {
								$class = 'apbgf-available';
								$hdnInput = '<input type="hidden" class="apbgf-hdn" value="' . $date . '" />';
							}
						}
					}
				}

				/*CT-284*/
				if ($today == $date) {
					if ( is_array( $nextDates ) && in_array( $date, $nextDates ) ) {
						$week .= '<td class="today apbgf-not-available"><span>' . $day . '</span>';
					} else {
						$week .= '<td class="today ' . $class . '"><span>' . $day . '</span>' . $hdnInput;
					}
				} elseif ( is_array( $nextDates ) && in_array( $date, $nextDates ) ) {

						$week .= '<td class="apbgf-not-available">' . $day;
				} else {
					$week .= '<td class="' . $class . '">' . $day . $hdnInput;

				}
				/*CT-284*/
				$week .= '</td>';

				// End of the week OR End of the month
				if ($str % 7 == 6 || $day == $day_count) {

					if ($day == $day_count) {
						// Add empty cell
						$week .= str_repeat('<td></td>', 6 - ( $str % 7 ));
					}

					$weeks[] = '<tr>' . $week . '</tr>';

					// Prepare for new week
					$week = '';
				}

			}

			require_once APBGF_VIEW_PATH . 'calendar.php';
		}


		public static function render( $appointmentData = array(), $args = array() ) {

			if ( is_array( $args ) && empty($args) ) {
				$args = array(
					'location_id' => 'all',
					'service_id'  => 'all',
					'staff_id'    => 'all'
				);
			}

			
			$timezone = apbgf_timezone_string(); // returns something like 'Pacific/Auckland'
			$datetime_zone = new DateTimeZone( $timezone );

			// Now create a DateTime object with this timezone
			$datetime = new DateTime( 'now', $datetime_zone );


			/* custom add booking from admin area starts*/
			$admin_booking_flag = false;
			if ( ( 'all' != $args['location_id'] && ! empty( $args['location_id'] ) ) && ( 'all' != $args['service_id'] && ! empty( $args['service_id'] ) ) && ( 'all' != $args['staff_id'] && ! empty( $args['staff_id'] ) ) ) {

				$location_id = $args['location_id'];
				$service_id = $args['service_id'];
				$staff_id = $args['staff_id'];

				$staffData = APBGF_Staff::get_staff_by_id( $staff_id );

				// echo 'timezone is ' . apbgf_timezone_string( $staff_id );              
				$special_day = false;
				if ( isset( $staffData['spl_day_timeslots'] ) && is_array( $staffData['spl_day_timeslots'] ) && count( $staffData['spl_day_timeslots'] ) > 0 ) {
					#continue.
					$staff_special_timeslots = $staffData['spl_day_timeslots'];             
					$staff_special_timings = $staffData['spl_day_timings'];
					// atleast special day timings are found.
					$special_day = true;
				}


				$staff_timings = array();
				if ( isset( $staffData['timings'] ) && is_array( $staffData['timings'] ) && count( $staffData['timings'] ) > 0 ) {
					#continue.
					$staff_timings = $staffData['timings'];
				}

				if ( isset( $staffData['holidays'] ) && is_array( $staffData['holidays'] ) && count( $staffData['holidays'] ) > 0 ) {               
					$staff_holidays = $staffData['holidays'];
				} else {
					$staff_holidays = false;
				}


				$admin_booking_flag = true;

			}   
			/* custom add booking from admin area end*/

			
			// Get prev & next month
			// phpcs:ignore WordPress.Security.NonceVerification.Missing
			$date = isset( $_POST['date'] ) ? sanitize_text_field( wp_unslash( $_POST['date'] ) ) : '';

			if ( ! empty( $date ) ) {
				$ym = $date;
			} else {
				$ym = gmdate('Y-m');
			}

			echo '<input type="hidden" name="date" id="date" value="' . esc_attr($ym) . '" />';

			// Check format
			$timestamp = strtotime($ym . '-01');        
			if ($timestamp === false) {             
				$ym = gmdate('Y-m');
				$timestamp = strtotime($ym . '-01');
			}           

			// Today
			$today = gmdate('Y-m-d', time());         

			// For H3 title
			$current_month = gmdate('F', $timestamp);
			$current_year = gmdate('Y', $timestamp);
			$html_title = apbgf_translable_name('months', strtolower($current_month)) . ' ' . $current_year;

			// Create prev & next month link     mktime(hour,minute,second,month,day,year)
			$prev = gmdate('Y-m', mktime(0, 0, 0, gmdate('m', $timestamp)-1, 1, gmdate('Y', $timestamp)));
			$next = gmdate('Y-m', mktime(0, 0, 0, gmdate('m', $timestamp)+1, 1, gmdate('Y', $timestamp)));
			$curr = gmdate('Y-m'); // get the current year and month
			$disabled_prev = false;
			$disabled_next = false;

			// Number of days in the month
			$day_count = gmdate('t', $timestamp);

			//echo 'total days ' . $day_count;

			// 0:Sun 1:Mon 2:Tue ...
			if ( isset( get_option('apbgf_settings', array())['general']['gbf_start_of_week'] ) && 'sunday' == get_option('apbgf_settings', array())['general']['gbf_start_of_week'] ) {                
				$str = gmdate('w', mktime(0, 0, 0, gmdate('m', $timestamp), 1, gmdate('Y', $timestamp)));
			} else {                
				$str = gmdate('w', mktime(0, 0, 0, gmdate('m', $timestamp), 1, gmdate('Y', $timestamp))) - 1;
				if ( $str == -1 ) {
					$str = 6;
				}
			}


			//$str = date('w', $timestamp);

			//echo '<br>str ' . $str;


			// Create Calendar!!
			$weeks = array();
			$week = '';

			// Add empty cell
			$week .= str_repeat('<td></td>', $str);

			for ( $day = 1; $day <= $day_count; $day++, $str++) {

				$date = $ym . '-' . $day;

				$date = gmdate( 'Y-m-d', strtotime($date) );          

				if ($today == $date) {
					$week .= '<td class="today"><span>' . $day . '</span>';
				} else {
					$week .= '<td>' . $day;
				}

				// Will continue from here tomorrow 15 June 2023
				/* custom add booking from admin area start*/
				$hdnInput = '';
				if ( $admin_booking_flag ) {                    
					// get day short name
					$daySName = strtolower( gmdate('D', strtotime($date)) );

					// Always use the current $date for the button
					$addBookingBtn = '<a href="#" class="apbgf-add-booking" data-date="' . esc_attr($date) . '">+</a>';
					
					if ( $special_day ) {
						if ( is_array( $staff_special_timeslots ) && isset( $staff_special_timeslots[ $date ] ) ) {

							$special_day = true;
							if ( false != $staff_holidays && in_array( $date, $staff_holidays ) && $date >= $today ) {
								// holiday added on this day.
								$holiday_added = 'Your Logic here';
							} elseif ( $date >= $today) {
									$checkDayAvaible = APBGF_Appointment::apbgf_check_day_available_with_slots( $staff_id, $date, $staffData, $daySName, 'special', $location_id, $service_id );
								if ( $checkDayAvaible ) {
									$hdnInput = $addBookingBtn;
								}
							}
						} else {                    
							$special_day = false;
						}
					}

					if ( !$special_day && isset( $staff_timings[ $daySName ] ) ) {                    

						if ( false != $staff_holidays && in_array( $date, $staff_holidays ) && $date >= $today ) {
							// holiday added on this day.
							$holiday_added = '';
						} elseif ( $date >= $today) {
								/* Fullday */
							if ( isset( $staffData['booking_type'] ) && 'fullday' == $staffData['booking_type'] ) { 
								//check if is fullday booking type
								
								$total_slots_consumed = (int) APBGF_Appointment::apbgf_all_appointment_by_date_time( $staff_id, $date, '00:00 - 23:59', 'fullday', $service_id, $location_id );
								
								$slot_capacity = (int) $staff_timings[ $daySName ]['capacity'];
								$remaining_capacity = $slot_capacity - $total_slots_consumed;
								

								// translators: %1$s is the number of slot left
								$single_slot = sprintf(esc_html__('%1$s slot left', 'appointment-and-booking-for-gravity-forms'), $remaining_capacity);
								// translators: %1$s is the number of slots left
								$multiple_slots = sprintf(esc_html__('%1$s slots left', 'appointment-and-booking-for-gravity-forms'), $remaining_capacity);
								$tooltip = ( 1 >= $remaining_capacity ) ? $single_slot : $multiple_slots;
								if ( $date >= $today && $remaining_capacity > 0 ) {
									$hdnInput = $addBookingBtn;
								}
								
							} else {                                
								$checkDayAvaible = APBGF_Appointment::apbgf_check_day_available_with_slots( $staff_id, $date, $staffData, $daySName, 'normal', $location_id, $service_id );

								if ( $checkDayAvaible ) {
									$hdnInput = $addBookingBtn;
								}
							}
							// Removed debug code, booking button and calendar will render normally
						
						}
					}
					
				}
				/* custom add booking from admin area end*/             

				if ( is_array($appointmentData) && count( $appointmentData ) > 0 ) {
					foreach ( $appointmentData as $appointment_id ) {
						$app_date = get_post_meta($appointment_id, 'date', true);                       
						if ( is_array($app_date) && count($app_date) > 0 ) {
							foreach ( $app_date as $app_date ) {
								$check_app_date = gmdate('Y-m-d', strtotime($app_date));
								//$app_date = ! empty( $app_date ) ? date('Y-m-d', strtotime($app_date)) : '';
								if ( ! empty( $check_app_date ) && $check_app_date == $date ) {
									ob_start();
									require APBGF_ADMIN_VIEW_PATH . 'appointment-block.php';
									$week .= ob_get_clean();                            
								}
							}
						} else {
							$check_app_date = gmdate('Y-m-d', strtotime($app_date));
							//$app_date = ! empty( $app_date ) ? date('Y-m-d', strtotime($app_date)) : '';                      
							if ( ! empty( $check_app_date ) && $check_app_date == $date ) {
								ob_start();
								require APBGF_ADMIN_VIEW_PATH . 'appointment-block.php';
								$week .= ob_get_clean();                            
							}
						}               
					}
				}

				$week .= $hdnInput . '</td>';

				// End of the week OR End of the month
				if ($str % 7 == 6 || $day == $day_count) {

					if ($day == $day_count) {
						// Add empty cell
						$week .= str_repeat('<td></td>', 6 - ( $str % 7 ));
					}

					$weeks[] = '<tr>' . $week . '</tr>';

					// Prepare for new week
					$week = '';
				}

			}           

			require_once APBGF_ADMIN_VIEW_PATH . 'admin-calendar.php';
		}

		public function register_menu() {

			add_submenu_page(
				'appointment-and-booking-for-gravity-forms',
				__( 'Calendar - Gravity Booking', 'appointment-and-booking-for-gravity-forms' ),
				__( 'Calendar', 'appointment-and-booking-for-gravity-forms' ),
				'manage_options',
				'apbgf-calendar',
				array( get_class($this), 'apbgf_calendar_page' ),
				0
			);
		}

		public static function apbgf_calendar_page() {
			
			$apbgf_settings = APBGF_Setting::get_settings();

			$appointmentData = APBGF_Appointment::apbgf_show_appointments_on_calendar(array(
				'location_id' => 'all',
				'service_id' => 'all',
				'staff_id' => 'all',
			));

			require_once APBGF_ADMIN_VIEW_PATH . 'admin-calendar-top-filters.php';

			echo '<div id="apbgf-fancy-calendar">';
			self::render($appointmentData);
			echo '</div>';
		}


		/**
		 * Singleton Class Instatnce.
		 */
		public static function get_instance() {
			if ( null === self::$instance ) {
				self::$instance = new APBGF_Calendar();
			}

			return self::$instance;
		}
	}

	APBGF_Calendar::get_instance();
}
