<?php
/**
 * Tutor individual-booking update service.
 *
 * @package TutorHelper_Bookings
 */

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

/**
 * Validates ownership and tutor-entered editing data.
 */
final class TutorHelper_Tutor_Booking_Update {

	/**
	 * Tutor-editable statuses.
	 *
	 * @var array
	 */
	const ALLOWED_STATUSES = array(
		TutorHelper_Booking_Records::
			STATUS_CONFIRMED,

		TutorHelper_Booking_Records::
			STATUS_UNCONFIRMED,

		TutorHelper_Booking_Records::
			STATUS_CANCELLED,
	);

	/**
	 * Tutor-editable locations.
	 *
	 * @var array
	 */
	const ALLOWED_LOCATIONS = array(
		'online',
		'phone',
		'in_person',
		'custom',
		'other',
	);

	/**
	 * Parse one exact local date.
	 *
	 * @param mixed $value Raw value.
	 *
	 * @return string|WP_Error
	 */
	private static function normalise_date(
		$value
	) {
		$value = sanitize_text_field(
			(string) $value
		);

		$date =
			DateTimeImmutable::createFromFormat(
				'!Y-m-d',
				$value,
				new DateTimeZone( 'UTC' )
			);

		$errors =
			DateTimeImmutable::getLastErrors();

		if (
			false === $date
			|| (
				is_array( $errors )
				&& (
					0 < $errors['warning_count']
					|| 0 < $errors['error_count']
				)
			)
			|| $date->format( 'Y-m-d' )
				!== $value
		) {
			return new WP_Error(
				'tutorhelper_tutor_booking_date_invalid',
				__(
					'Enter a valid booking date.',
					'tutorhelper-bookings'
				)
			);
		}

		return $value;
	}

	/**
	 * Parse one exact local time.
	 *
	 * @param mixed  $value      Raw time.
	 * @param string $error_code Error code.
	 * @param string $message    Error message.
	 *
	 * @return string|WP_Error
	 */
	private static function normalise_time(
		$value,
		$error_code,
		$message
	) {
		$value = sanitize_text_field(
			(string) $value
		);

		foreach (
			array( '!H:i', '!H:i:s' )
			as $format
		) {
			$time =
				DateTimeImmutable::createFromFormat(
					$format,
					$value,
					new DateTimeZone( 'UTC' )
				);

			$errors =
				DateTimeImmutable::getLastErrors();

			if (
				false !== $time
				&& ! (
					is_array( $errors )
					&& (
						0 < $errors['warning_count']
						|| 0 < $errors['error_count']
					)
				)
			) {
				$expected =
					'!H:i' === $format
						? $time->format(
							'H:i'
						)
						: $time->format(
							'H:i:s'
						);

				if ( $expected === $value ) {
					return $time->format(
						'H:i:s'
					);
				}
			}
		}

		return new WP_Error(
			$error_code,
			$message
		);
	}

	/**
	 * Return a valid timezone.
	 *
	 * @param mixed $value Raw timezone.
	 *
	 * @return string|WP_Error
	 */
	private static function normalise_timezone(
		$value
	) {
		$value = sanitize_text_field(
			(string) $value
		);

		if ( '' === $value ) {
			$value =
				wp_timezone()->getName();
		}

		try {
			new DateTimeZone( $value );
		} catch ( Exception $exception ) {
			return new WP_Error(
				'tutorhelper_tutor_booking_timezone_invalid',
				__(
					'The booking timezone is invalid.',
					'tutorhelper-bookings'
				)
			);
		}

		return $value;
	}

	/**
	 * Convert an evaluator blocking condition into an update-service error.
	 *
	 * @param array $evaluation Booking-condition evaluation.
	 *
	 * @return WP_Error|null
	 */
	private static function get_condition_error(
		$evaluation
	) {
		if (
			! is_array( $evaluation )
			|| ! empty(
				$evaluation['can_tutor_save']
			)
		) {
			return null;
		}

		$blocking_conditions = isset(
			$evaluation['blocking_conditions']
		)
		&& is_array(
			$evaluation['blocking_conditions']
		)
			? $evaluation['blocking_conditions']
			: array();

		foreach (
			$blocking_conditions as $condition
		) {
			if (
				! is_array( $condition )
				|| empty( $condition['code'] )
			) {
				continue;
			}

			$code = sanitize_key(
				$condition['code']
			);

			switch ( $code ) {
				case 'tutor_leave':
					return new WP_Error(
						'tutorhelper_tutor_booking_tutor_leave',
						__(
							'The booking cannot be updated while the tutor is marked as unavailable.',
							'tutorhelper-bookings'
						),
						array(
							'condition_code' =>
								'tutor_leave',
						)
					);

				case 'booking_conflict':
					return new WP_Error(
						'tutorhelper_booking_time_conflict',
						__(
							'The tutor already has another booking during this time.',
							'tutorhelper-bookings'
						),
						array(
							'condition_code' =>
								'booking_conflict',
						)
					);
			}
		}

		return new WP_Error(
			'tutorhelper_tutor_booking_conditions_blocked',
			__(
				'The booking cannot be updated because one or more booking conditions prevent it.',
				'tutorhelper-bookings'
			)
		);
	}


	



	/**
	 * Update one tutor-owned booking.
	 *
	 * Student identity is always obtained from the existing booking.
	 *
	 * @param int   $booking_id    Booking ID.
	 * @param array $data          Submitted form data.
	 * @param int   $tutor_user_id Tutor performing the edit.
	 *
	 * @return array|WP_Error
	 */
	public static function update(
		$booking_id,
		$data,
		$tutor_user_id
	) {
		$booking_id =
			absint( $booking_id );

		$tutor_user_id =
			absint( $tutor_user_id );

		$data = is_array( $data )
			? $data
			: array();

		if ( ! $booking_id ) {
			return new WP_Error(
				'tutorhelper_booking_update_id_missing',
				__(
					'A booking is required.',
					'tutorhelper-bookings'
				)
			);
		}

		if ( ! $tutor_user_id ) {
			return new WP_Error(
				'tutorhelper_tutor_booking_tutor_missing',
				__(
					'A tutor is required.',
					'tutorhelper-bookings'
				)
			);
		}

		$existing =
			TutorHelper_Booking_Records::
				get_by_id(
					$booking_id
				);

		if ( ! $existing ) {
			return new WP_Error(
				'tutorhelper_booking_update_not_found',
				__(
					'The booking could not be found.',
					'tutorhelper-bookings'
				)
			);
		}

		if (
			$tutor_user_id
				!== absint(
					$existing
						->tutor_user_id
				)
		) {
			return new WP_Error(
				'tutorhelper_booking_update_forbidden',
				__(
					'You are not allowed to edit this booking.',
					'tutorhelper-bookings'
				)
			);
		}

		$status =
			isset( $data['status'] )
				? sanitize_key(
					$data['status']
				)
				: '';

		if (
			! in_array(
				$status,
				self::ALLOWED_STATUSES,
				true
			)
		) {
			return new WP_Error(
				'tutorhelper_tutor_booking_status_invalid',
				__(
					'Select a valid booking status.',
					'tutorhelper-bookings'
				)
			);
		}

		/*
		* Cancellation is handled separately from a normal booking edit.
		*
		* Use the shared cancellation service so that the cancellation
		* reason, timestamp and email events are handled consistently.
		*/
		if (
			TutorHelper_Booking_Records::
				STATUS_CANCELLED
			=== $status
		) {
			$cancellation_reason =
				isset(
					$data['cancellation_reason']
				)
					? sanitize_textarea_field(
						$data[
							'cancellation_reason'
						]
					)
					: '';

			$cancellation_reason =
				trim(
					$cancellation_reason
				);

			if ( '' === $cancellation_reason ) {
				return new WP_Error(
					'tutorhelper_tutor_booking_cancel_reason_required',
					__(
						'Enter a reason for cancelling this lesson.',
						'tutorhelper-bookings'
					)
				);
			}

			if ( function_exists( 'mb_substr' ) ) {
				$cancellation_reason =
					mb_substr(
						$cancellation_reason,
						0,
						500
					);
			} else {
				$cancellation_reason =
					substr(
						$cancellation_reason,
						0,
						500
					);
			}

			return TutorHelper_Booking_Records::cancel(
				$booking_id,
				$cancellation_reason,
				'tutor'
			);
		}

		$booking_type_id = isset(
			$data['booking_type_id']
		)
			? absint(
				$data['booking_type_id']
			)
			: 0;

		if ( ! $booking_type_id ) {
			return new WP_Error(
				'tutorhelper_tutor_booking_type_missing',
				__(
					'Select a Booking Type.',
					'tutorhelper-bookings'
				)
			);
		}

		$date = self::normalise_date(
			isset( $data['booking_date'] )
				? $data['booking_date']
				: ''
		);

		if ( is_wp_error( $date ) ) {
			return $date;
		}

		$start_time =
			self::normalise_time(
				isset( $data['start_time'] )
					? $data['start_time']
					: '',
				'tutorhelper_tutor_booking_start_time_invalid',
				__(
					'Enter a valid booking start time.',
					'tutorhelper-bookings'
				)
			);

		if ( is_wp_error( $start_time ) ) {
			return $start_time;
		}

		$end_time =
			self::normalise_time(
				isset( $data['end_time'] )
					? $data['end_time']
					: '',
				'tutorhelper_tutor_booking_end_time_invalid',
				__(
					'Enter a valid booking end time.',
					'tutorhelper-bookings'
				)
			);

		if ( is_wp_error( $end_time ) ) {
			return $end_time;
		}

		$timezone =
			self::normalise_timezone(
				isset( $data['timezone'] )
					? $data['timezone']
					: ''
			);

		if ( is_wp_error( $timezone ) ) {
			return $timezone;
		}

		try {
			$timezone_object =
				new DateTimeZone(
					$timezone
				);

			$local_start =
				new DateTimeImmutable(
					$date . ' '
						. $start_time,
					$timezone_object
				);

			$local_end =
				new DateTimeImmutable(
					$date . ' '
						. $end_time,
					$timezone_object
				);
		} catch ( Exception $exception ) {
			return new WP_Error(
				'tutorhelper_tutor_booking_datetime_invalid',
				__(
					'The booking date or time is invalid.',
					'tutorhelper-bookings'
				)
			);
		}

		if ( $local_end <= $local_start ) {
			return new WP_Error(
				'tutorhelper_tutor_booking_end_before_start',
				__(
					'The booking end time must be after its start time.',
					'tutorhelper-bookings'
				)
			);
		}


		$location_type = isset(
			$data['location_type']
		)
			? sanitize_key(
				$data['location_type']
			)
			: '';

		if (
			! in_array(
				$location_type,
				self::ALLOWED_LOCATIONS,
				true
			)
		) {
			return new WP_Error(
				'tutorhelper_tutor_booking_location_invalid',
				__(
					'Select a valid booking location.',
					'tutorhelper-bookings'
				)
			);
		}

		$condition_evaluation =
			TutorHelper_Booking_Conditions::
				evaluate(
					array(
						'tutor_user_id' =>
							$tutor_user_id,

						'booking_type_id' =>
							$booking_type_id,

						'booking_start' =>
							$local_start->format(
								'Y-m-d H:i:s'
							),

						'booking_end' =>
							$local_end->format(
								'Y-m-d H:i:s'
							),

						'timezone' =>
							$timezone,

						'location_type' =>
							$location_type,

						'exclude_booking_id' =>
							$booking_id,
					)
				);

		if ( is_wp_error( $condition_evaluation ) ) {
			return $condition_evaluation;
		}

		$condition_error =
			self::get_condition_error(
				$condition_evaluation
			);

		if ( is_wp_error( $condition_error ) ) {
			TutorHelper_Logger::warning(
				'Tutor booking update blocked by booking conditions.',
				array(
					'booking_id' =>
						$booking_id,

					'tutor_user_id' =>
						$tutor_user_id,

					'booking_type_id' =>
						$booking_type_id,

					'error_code' =>
						$condition_error
							->get_error_code(),

					'blocking_conditions' =>
						isset(
							$condition_evaluation[
								'blocking_conditions'
							]
						)
						&& is_array(
							$condition_evaluation[
								'blocking_conditions'
							]
						)
							? array_values(
								array_filter(
									array_map(
										static function (
											$condition
										) {
											return is_array(
												$condition
											)
											&& isset(
												$condition['code']
											)
												? sanitize_key(
													$condition['code']
												)
												: '';
										},
										$condition_evaluation[
											'blocking_conditions'
										]
									)
								)
							)
							: array(),
				)
			);

			return $condition_error;
		}


		$updated =
			TutorHelper_Booking_Records::update(
				$booking_id,
				array(
					'booking_type_id' =>
						$booking_type_id,

					'booking_start' =>
						$local_start->format(
							'Y-m-d H:i:s'
						),

					'booking_end' =>
						$local_end->format(
							'Y-m-d H:i:s'
						),

					'timezone' =>
						$timezone,

					'status' =>
						$status,

					'location_type' =>
						$location_type,

					'location_details' =>
						isset(
							$data[
								'location_details'
							]
						)
							? sanitize_textarea_field(
								$data[
									'location_details'
								]
							)
							: '',

					'notes' =>
						isset( $data['notes'] )
							? sanitize_textarea_field(
								$data['notes']
							)
							: '',

					'editor_source' =>
						TutorHelper_Booking_Records::
							SOURCE_TUTOR,
				)
			);

		if ( is_wp_error( $updated ) ) {
			return $updated;
		}

		return array(
			'booking_id' =>
				$booking_id,

			'booking' =>
				$updated,

			'student_user_id' =>
				absint(
					$updated
						->student_user_id
				),

			'status' =>
				sanitize_key(
					$updated->status
				),

			'price_minor_units' =>
				absint(
					$updated
						->price_minor_units
				),

			'currency' =>
				sanitize_key(
					$updated->currency
				),
				
			'conditions' =>
				isset(
					$condition_evaluation[
						'conditions'
					]
				)
				&& is_array(
					$condition_evaluation[
						'conditions'
					]
				)
					? $condition_evaluation[
						'conditions'
					]
					: array(),

			'warnings' =>
				isset(
					$condition_evaluation[
						'warnings'
					]
				)
				&& is_array(
					$condition_evaluation[
						'warnings'
					]
				)
					? $condition_evaluation[
						'warnings'
					]
					: array(),
					);
	}
}
