<?php
/**
 * Student dashboard booking data.
 *
 * @package TutorHelper_Bookings
 */

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

/**
 * Retrieves and prepares student dashboard bookings.
 */
final class TutorHelper_Student_Dashboard_Bookings {

	/**
	 * Return future unconfirmed requests for a student.
	 *
	 * @param int $student_user_id Student WordPress user ID.
	 *
	 * @return array
	 */
	public static function get_pending(
		$student_user_id
	) {
		return self::get_future_by_status(
			$student_user_id,
			TutorHelper_Booking_Records::
				STATUS_UNCONFIRMED
		);
	}

	/**
	 * Return future confirmed lessons for a student.
	 *
	 * @param int $student_user_id Student WordPress user ID.
	 *
	 * @return array
	 */
	public static function get_upcoming(
		$student_user_id
	) {
		$student_user_id =
			absint( $student_user_id );

		if ( ! $student_user_id ) {
			return array();
		}

		$now_utc =
			new DateTimeImmutable(
				'now',
				new DateTimeZone( 'UTC' )
			);

		$bookings =
			TutorHelper_Booking_Records::get_all(
				array(
					'student_user_id' =>
						$student_user_id,

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

					'limit' => 100,
				)
			);

		if ( ! is_array( $bookings ) ) {
			return array();
		}

		$allowed_statuses = array(
			TutorHelper_Booking_Records::
				STATUS_CONFIRMED,

			TutorHelper_Booking_Records::
				STATUS_CANCELLED,
		);

		$prepared = array();

		foreach ( $bookings as $booking ) {
			$status =
				isset( $booking->status )
					? sanitize_key(
						$booking->status
					)
					: '';

			if (
				! in_array(
					$status,
					$allowed_statuses,
					true
				)
			) {
				continue;
			}

			$item =
				self::prepare_booking(
					$booking
				);

			if ( ! empty( $item ) ) {
				$prepared[] = $item;
			}
		}

		return $prepared;
	}


    /**
     * Return previous lessons for a student.
     *
     * Includes confirmed, unconfirmed and cancelled records whose
     * scheduled start time has passed.
     *
     * @param int $student_user_id Student WordPress user ID.
     *
     * @return array
     */
    public static function get_past(
        $student_user_id
    ) {
        $student_user_id =
            absint( $student_user_id );

        if ( ! $student_user_id ) {
            return array();
        }

        $now_utc =
            new DateTimeImmutable(
                'now',
                new DateTimeZone( 'UTC' )
            );

        $bookings =
            TutorHelper_Booking_Records::get_all(
                array(
                    'student_user_id' =>
                        $student_user_id,

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

                    'limit' => 100,
                )
            );

        if ( ! is_array( $bookings ) ) {
            return array();
        }

        $allowed_statuses = array(
            TutorHelper_Booking_Records::
                STATUS_CONFIRMED,

            TutorHelper_Booking_Records::
                STATUS_UNCONFIRMED,

            TutorHelper_Booking_Records::
                STATUS_CANCELLED,
        );

        $prepared = array();

        foreach ( $bookings as $booking ) {
            $status =
                is_object( $booking )
                && isset( $booking->status )
                    ? sanitize_key(
                        $booking->status
                    )
                    : '';

            if (
                ! in_array(
                    $status,
                    $allowed_statuses,
                    true
                )
            ) {
                continue;
            }

            $item =
                self::prepare_booking(
                    $booking
                );

            if ( ! empty( $item ) ) {
                $prepared[] = $item;
            }
        }

        /*
        * Booking_Records::get_all() sorts ascending.
        * Past lessons should display most recent first.
        */
        return array_reverse( $prepared );
    }




	/**
	 * Retrieve future bookings with one status.
	 *
	 * Booking records store dates in UTC.
	 *
	 * @param int    $student_user_id Student WordPress user ID.
	 * @param string $status          Booking status.
	 *
	 * @return array
	 */
	private static function get_future_by_status(
		$student_user_id,
		$status
	) {
		$student_user_id =
			absint( $student_user_id );

		if ( ! $student_user_id ) {
			return array();
		}

		$now_utc =
			new DateTimeImmutable(
				'now',
				new DateTimeZone( 'UTC' )
			);

		$bookings =
			TutorHelper_Booking_Records::get_all(
				array(
					'student_user_id' =>
						$student_user_id,

					'status' =>
						sanitize_key( $status ),

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

					'limit' => 100,
				)
			);

		if ( ! is_array( $bookings ) ) {
			return array();
		}

		$prepared = array();

		foreach ( $bookings as $booking ) {
			$item =
				self::prepare_booking(
					$booking
				);

			if ( ! empty( $item ) ) {
				$prepared[] = $item;
			}
		}

		return $prepared;
	}

	/**
	 * Prepare one booking for dashboard display.
	 *
	 * @param object $booking Booking record.
	 *
	 * @return array
	 */
	private static function prepare_booking(
		$booking
	) {
		if (
			! is_object( $booking )
			|| empty( $booking->id )
			|| empty( $booking->booking_start )
			|| empty( $booking->booking_end )
		) {
			return array();
		}

		$booking_type =
			TutorHelper_Booking_Types::get_by_id(
				absint(
					$booking->booking_type_id
				)
			);

		$title =
			$booking_type
			&& ! empty( $booking_type->title )
				? sanitize_text_field(
					$booking_type->title
				)
				: __(
					'Lesson',
					'tutorhelper-bookings'
				);
		
		$cancellation =
			self::get_cancellation_data(
				$booking,
				$booking_type
			);

        $colour =
            $booking_type
            && ! empty( $booking_type->colour )
                ? sanitize_hex_color(
                    $booking_type->colour
                )
                : '';

        if ( ! $colour ) {
            $colour = '#328bb8';
        }

		$timezone_name =
			self::get_timezone_name(
				isset( $booking->timezone )
					? $booking->timezone
					: ''
			);

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

			$start =
				new DateTimeImmutable(
					$booking->booking_start,
					new DateTimeZone( 'UTC' )
				);

			$end =
				new DateTimeImmutable(
					$booking->booking_end,
					new DateTimeZone( 'UTC' )
				);

			$start =
				$start->setTimezone(
					$timezone
				);

			$end =
				$end->setTimezone(
					$timezone
				);
		} catch ( Exception $exception ) {
			return array();
		}

		$status =
			sanitize_key(
				isset( $booking->status )
					? $booking->status
					: ''
			);

		$location_type =
			sanitize_key(
				isset( $booking->location_type )
					? $booking->location_type
					: ''
			);

        $location_details =
            isset( $booking->location_details )
                ? sanitize_textarea_field(
                    $booking->location_details
                )
                : '';

		$cancellation_reason =
			isset( $booking->cancellation_reason )
				? sanitize_textarea_field(
					$booking->cancellation_reason
				)
				: '';
		
		$cancelled_by =
			isset( $booking->cancelled_by )
				? sanitize_key(
					$booking->cancelled_by
				)
				: '';

		if (
			! in_array(
				$cancelled_by,
				array(
					'student',
					'tutor',
				),
				true
			)
		) {
			$cancelled_by = '';
		}


		return array(
			'id' =>
				absint( $booking->id ),

			'title' =>
				$title,

            'colour' =>
		        $colour,
			
			'cancellation' =>
				$cancellation,

			'start' =>
				$start,

			'end' =>
				$end,

			'timezone' =>
				$timezone_name,

			'status' =>
				$status,

			'status_label' =>
				self::get_status_label(
					$status,
					$cancelled_by
				),

			'location_type' =>
				$location_type,

			'location_label' =>
				self::get_location_label(
					$location_type
				),
            
            'location_details' =>
	            $location_details,

			'cancellation_reason' =>
				$cancellation_reason,

			'cancelled_by' =>
				$cancelled_by,
		);
	}


	/**
	 * Return cancellation information for one booking.
	 *
	 * @param object      $booking      Booking record.
	 * @param object|null $booking_type Booking Type record.
	 *
	 * @return array
	 */
	private static function get_cancellation_data(
		$booking,
		$booking_type
	) {
		$notice_hours =
			$booking_type
			&& isset(
				$booking_type
					->cancellation_notice_hours
			)
				? absint(
					$booking_type
						->cancellation_notice_hours
				)
				: 0;

		$allowed =
			$booking_type
			&& ! empty(
				$booking_type
					->cancellations_allowed
			);

		$result = array(
			'allowed' =>
				$allowed,

			'notice_hours' =>
				$notice_hours,

			'can_cancel' =>
				false,

			'message' =>
				'',
		);

		if ( ! $allowed ) {
			$result['message'] =
				__(
					'Online cancellation is not available for this lesson.',
					'tutorhelper-bookings'
				);

			return $result;
		}

		$status =
			sanitize_key(
				isset( $booking->status )
					? $booking->status
					: ''
			);

		if (
			! in_array(
				$status,
				array(
					TutorHelper_Booking_Records::
						STATUS_UNCONFIRMED,

					TutorHelper_Booking_Records::
						STATUS_CONFIRMED,
				),
				true
			)
		) {
			return $result;
		}

		try {
			$start_utc =
				new DateTimeImmutable(
					$booking->booking_start,
					new DateTimeZone( 'UTC' )
				);

			$now_utc =
				new DateTimeImmutable(
					'now',
					new DateTimeZone( 'UTC' )
				);
		} catch ( Exception $exception ) {
			return $result;
		}

		if ( $start_utc <= $now_utc ) {
			return $result;
		}

		$deadline =
			$start_utc->modify(
				'-' . $notice_hours . ' hours'
			);

		if ( $now_utc > $deadline ) {
			$result['message'] =
				sprintf(
					/* translators: %d: minimum cancellation notice in hours. */
					_n(
						'Online cancellation closes %d hour before the lesson.',
						'Online cancellation closes %d hours before the lesson.',
						$notice_hours,
						'tutorhelper-bookings'
					),
					$notice_hours
				);

			return $result;
		}

		$result['can_cancel'] = true;

		return $result;
	}


	/**
	 * Check whether a student may cancel one booking now.
	 *
	 * @param int $booking_id      Booking ID.
	 * @param int $student_user_id Student WordPress user ID.
	 *
	 * @return true|WP_Error
	 */
	public static function can_student_cancel(
		$booking_id,
		$student_user_id
	) {
		$booking_id =
			absint( $booking_id );

		$student_user_id =
			absint( $student_user_id );

		if (
			! $booking_id
			|| ! $student_user_id
		) {
			return new WP_Error(
				'tutorhelper_student_cancel_invalid',
				__(
					'The booking could not be cancelled.',
					'tutorhelper-bookings'
				)
			);
		}

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

		if (
			! $booking
			|| $student_user_id
			!== absint(
				$booking->student_user_id
			)
		) {
			return new WP_Error(
				'tutorhelper_student_cancel_not_found',
				__(
					'The booking could not be found.',
					'tutorhelper-bookings'
				)
			);
		}

		$booking_type =
			TutorHelper_Booking_Types::
				get_by_id(
					absint(
						$booking
							->booking_type_id
					)
				);

		if ( ! $booking_type ) {
			return new WP_Error(
				'tutorhelper_student_cancel_type_missing',
				__(
					'The cancellation policy for this booking could not be found.',
					'tutorhelper-bookings'
				)
			);
		}

		$cancellation =
			self::get_cancellation_data(
				$booking,
				$booking_type
			);

		if ( empty( $cancellation['allowed'] ) ) {
			return new WP_Error(
				'tutorhelper_student_cancel_not_allowed',
				__(
					'Online cancellation is not available for this lesson.',
					'tutorhelper-bookings'
				)
			);
		}

		if ( empty( $cancellation['can_cancel'] ) ) {
			return new WP_Error(
				'tutorhelper_student_cancel_too_late',
				! empty(
					$cancellation['message']
				)
					? $cancellation['message']
					: __(
						'This lesson can no longer be cancelled online.',
						'tutorhelper-bookings'
					)
			);
		}

		return true;
	}



	/**
	 * Return a safe timezone name.
	 *
	 * @param string $timezone_name Stored timezone.
	 *
	 * @return string
	 */
	private static function get_timezone_name(
		$timezone_name
	) {
		$timezone_name =
			sanitize_text_field(
				(string) $timezone_name
			);

		if (
			$timezone_name
			&& in_array(
				$timezone_name,
				timezone_identifiers_list(),
				true
			)
		) {
			return $timezone_name;
		}

		$site_timezone =
			wp_timezone_string();

		return $site_timezone
			? $site_timezone
			: 'UTC';
	}

	/**
	 * Return the public status label.
	 *
	 * @param string $status Booking status.
	 *
	 * @return string
	 */
	private static function get_status_label(
		$status,
		$cancelled_by = ''
	) {
		switch ( $status ) {
			case TutorHelper_Booking_Records::
				STATUS_UNCONFIRMED:
				return __(
					'Awaiting confirmation',
					'tutorhelper-bookings'
				);

			case TutorHelper_Booking_Records::
				STATUS_CONFIRMED:
				return __(
					'Confirmed',
					'tutorhelper-bookings'
				);

			case TutorHelper_Booking_Records::
				STATUS_CANCELLED:
				if ( 'tutor' === $cancelled_by ) {
					return __(
						'Cancelled by tutor',
						'tutorhelper-bookings'
					);
				}

				return __(
					'Cancelled',
					'tutorhelper-bookings'
				);

			default:
				return ucfirst(
					str_replace(
						'_',
						' ',
						$status
					)
				);
		}
	}

	/**
	 * Return a public location label.
	 *
	 * @param string $location_type Location key.
	 *
	 * @return string
	 */
	private static function get_location_label(
		$location_type
	) {
		$labels = array(
			'online' =>
				__(
					'Online',
					'tutorhelper-bookings'
				),

			'phone' =>
				__(
					'Phone',
					'tutorhelper-bookings'
				),

			'in_person' =>
				__(
					'In person',
					'tutorhelper-bookings'
				),

			'custom' =>
				__(
					'Custom location',
					'tutorhelper-bookings'
				),

			'other' =>
				__(
					'Other',
					'tutorhelper-bookings'
				),
		);

		if (
			isset( $labels[ $location_type ] )
		) {
			return $labels[ $location_type ];
		}

		return __(
			'To be confirmed',
			'tutorhelper-bookings'
		);
	}
}
