<?php
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;

/**
 * Appointments WC ajax callbacks.
 */
class WC_Appointments_WC_Ajax {

	/**
	 * Constructor.
	 */
	public function __construct() {
		add_action( 'wc_ajax_wc_appointments_find_scheduled_day_slots', [ $this, 'find_scheduled_day_slots' ] );
		add_action( 'wc_ajax_wc_appointments_set_timezone_cookie', [ $this, 'set_timezone_cookie' ] );
		add_action( 'wc_ajax_wc_appointments_add_appointment_to_cart', [ $this, 'add_appointment_to_cart' ] );
	}

	/**
	 * Find scheduled slots.
	 *
	 * AJAX handler to find scheduled slots for a given day range.
	 */
	public function find_scheduled_day_slots(): void {
		// Enable, when you figure out why it results in 403 error sometimes.
		#check_ajax_referer( 'find-scheduled-day-slots', 'security' );

		// Filter posted data.
		$posted = apply_filters( 'wc_appointments_before_find_scheduled_day_slots', $_POST );


		// Get product ID-
		$product_id = absint( $posted['product_id'] );

		if ( empty( $product_id ) || '' === $product_id ) {
			wp_send_json_error( 'Missing product ID' );
			exit;
		}

		try {

			$args                         = [];
			$product                      = get_wc_product_appointment( $product_id );
			$args['availability_rules']   = $product->get_availability_rules();
			$args['default_availability'] = $product->get_default_availability();
			$args['has_staff']            = $product->has_staff();
			$args['has_staff_ids']        = $product->get_staff_ids();
			$args['set_staff_id']         = empty( $posted['set_staff_id'] ) ? 0 : absint( $posted['set_staff_id'] );
			$args['appointment_duration'] = in_array( $product->get_duration_unit(), [ 'minute', 'hour' ] ) ? 1 : $product->get_duration();
			$args['staff_assignment']     = $product->has_staff() ? $product->get_staff_assignment() : 'customer';
			$args['duration_unit']        = $product->get_duration_unit();
			$args['min_date']             = isset( $posted['min_date'] ) ? strtotime( $posted['min_date'] ) : $product->get_min_date_a();
			$args['max_date']             = isset( $posted['max_date'] ) ? strtotime( $posted['max_date'] ) : $product->get_max_date_a();

			$min_date          = isset( $posted['min_date'] ) ? $args['min_date'] : strtotime( "+{$args['min_date']['value']} {$args['min_date']['unit']}", current_time( 'timestamp' ) );
			$max_date          = isset( $posted['max_date'] ) ? $args['max_date'] : strtotime( "+{$args['max_date']['value']} {$args['max_date']['unit']}", current_time( 'timestamp' ) );
			$staff_id_to_check = $args['set_staff_id'] && ! is_array( $args['set_staff_id'] ) ? [ $args['set_staff_id'] ] : [];

			// Normalize staff IDs.
			$staff_ids_a = [];
			if ( $staff_id_to_check && is_int( $staff_id_to_check ) && 0 !== $staff_id_to_check ) {
				$staff_ids_a[] = (int) $staff_id_to_check;
			} elseif ( $staff_id_to_check && is_array( $staff_id_to_check ) && 0 !== $staff_id_to_check ) {
				$staff_ids_a = array_map( 'intval', $staff_id_to_check );
			} elseif ( $product->has_staff() ) {
				if ( $product->is_staff_assignment_type( 'all' ) ) {
					// Staff all together at the same time.
					$staff_ids_a = array_map( 'intval', (array) $product->get_staff_ids() );
				} else {
					// When no specific staff is selected, pass empty array to allow individual staff processing
					$staff_ids_a = [];
				}
			} else {
				$staff_ids_a = [ 0 ];
			}
			$staff_ids = array_unique( $staff_ids_a );

			// Compute timezone offset (in hours) based on customer-selected timezone cookie.
			$tz_site_string = function_exists( 'wp_timezone_string' ) ? wp_timezone_string() : wc_timezone_string();
			$tz_user_string = isset( $_COOKIE['appointments_time_zone'] ) && $_COOKIE['appointments_time_zone'] ? sanitize_text_field( $_COOKIE['appointments_time_zone'] ) : $tz_site_string;
			$tz_offset_hours = 0;
			try {
				$tz_site  = new DateTimeZone( $tz_site_string );
				$tz_user  = new DateTimeZone( $tz_user_string );
				$dt_ref   = new DateTimeImmutable( '@' . (int) $min_date );
				$offset_s = (int) ( $tz_user->getOffset( $dt_ref ) - $tz_site->getOffset( $dt_ref ) );
				$tz_offset_hours = (int) round( $offset_s / HOUR_IN_SECONDS );
			} catch ( Exception $e ) {
				$tz_offset_hours = 0;
			}

			$scheduled = WC_Appointments_Controller::find_scheduled_day_slots( $product, $min_date, $max_date, 'Y-n-j', $tz_offset_hours, $staff_ids );


			$args['partially_scheduled_days'] = $scheduled['partially_scheduled_days'];
			$args['remaining_scheduled_days'] = $scheduled['remaining_scheduled_days'];
			$args['fully_scheduled_days']     = $scheduled['fully_scheduled_days'];
			$args['unavailable_days']         = $scheduled['unavailable_days'];
			$args['restricted_days']          = $product->has_restricted_days() ? $product->get_restricted_days() : false;

			$padding_days = [];
			if ( ! in_array( $product->get_duration_unit(), [ 'minute', 'hour' ] ) ) {
				$padding_days = WC_Appointments_Controller::get_padding_day_slots_for_scheduled_days( $product, $args['fully_scheduled_days'] );
			}

			$args['padding_days'] = $padding_days;

			// Filter all arguments.
			$args = apply_filters( 'wc_appointments_find_scheduled_day_slots', $args, $product );


			wp_send_json( $args );

		} catch ( Exception $e ) {

			wp_die();

		}
	}

	/**
	 * Set timezone cookie.
	 *
	 * AJAX handler to save the user's timezone preference in a cookie.
	 */
	public function set_timezone_cookie(): void {
		$timezone = $_POST['timezone'];

		if ( empty( $timezone ) ) {
			wp_send_json_error( __( 'Missing timezone', 'woocommerce-appointments' ) );
			exit;
		}

		try {

			setcookie( 'appointments_time_zone', $timezone, ['expires' => time() + ( DAY_IN_SECONDS * 30 ), 'path' => "/"] );


			wp_send_json( $timezone );

		} catch ( Exception $e ) {

			wp_die();

		}
	}

	/**
	 * Adds the appointment to the cart using WC().
	 *
	 * @since 4.5.0
	 */
	public function add_appointment_to_cart(): void {
		check_ajax_referer( 'add-appointment-to-cart', 'security' );

		$date = $_GET['date'] ?? '';

		if ( empty( $_GET['product_id'] ) || empty( $date ) ) {
			wp_die();
		}

		$product = wc_get_product( absint( $_GET['product_id'] ) );

		if ( ! is_wc_appointment_product( $product ) ) {
			wp_die();
		}

		$link = apply_filters( 'woocommerce_loop_product_link', $product->get_permalink(), $product );

		try {
			/*
			 * At this point we need to check if appointment can be
			 * made without any further user selection such as
			 * staff or product add-ons...etc. If so we cannot
			 * add appointment to cart via AJAX. Redirect them.
			 */
			if ( $product->has_staff() && $product->is_staff_assignment_type( 'customer' ) ) {
				wp_send_json(
				    [
						'scheduled' => false,
						'link'      => esc_url( $link ),
					],
				);
			}

			if ( 'hour' === $product->get_duration_unit() || 'minute' === $product->get_duration_unit() ) {
				$_POST['wc_appointments_field_start_date_time'] = $date;
			} else {
				$date_time                                       = new DateTime( $date );
				$_POST['wc_appointments_field_start_date_month'] = $date_time->format( 'm' );
				$_POST['wc_appointments_field_start_date_day']   = $date_time->format( 'd' );
				$_POST['wc_appointments_field_start_date_year']  = $date_time->format( 'Y' );
			}

			$added = WC()->cart->add_to_cart(
			    $product->get_id(),
			);

			wp_send_json(
			    [
					'scheduled' => false !== $added,
					'link'      => esc_url( $link ),
				],
			);
		} catch ( Exception $e ) {
			wp_send_json(
			    [
					'scheduled' => false,
					'link'      => esc_url( $link ),
				],
			);
		}
	}
}

new WC_Appointments_WC_Ajax();
