<?php

namespace SugarCalendarBookings\Appointments\Admin;

use SugarCalendarBookings\Appointments\AppointmentModel;
use SugarCalendarBookings\Payments\Gateways\Onsite\OnSitePaymentGateway;
use SugarCalendarBookings\Payments\Gateways\PaymentGatewayRegistry;
use SugarCalendarBookings\Payments\PaymentModel;
use SugarCalendarBookings\Payments\PaymentsHelpers;
use SugarCalendarBookings\Plugin;
use SugarCalendarBookings\Support\Admin\AbstractPage;
use SugarCalendarBookings\Support\DataProcessing\InputProcessor;
use SugarCalendarBookings\Support\Helpers\Date;
use SugarCalendarBookings\Support\Helpers\UI;
use Exception;

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

/**
 * Appointment Details page.
 *
 * @since 1.0.0
 */
class AppointmentDetailsPage extends AbstractPage {

	/**
	 * Current appointment being viewed.
	 *
	 * @since 1.0.0
	 *
	 * @var AppointmentModel|null
	 */
	private $appointment;

	/**
	 * Page title.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	public static function get_title() {

		return esc_html__( 'Appointment Details', 'sugar-calendar-bookings-scheduling-appointments-lite' );
	}

	/**
	 * Page slug.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	public static function get_slug() {

		return 'appointments';
	}

	/**
	 * View slug.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	public static function get_view_slug() {

		return 'details';
	}

	/**
	 * Hooks.
	 *
	 * @since 1.0.0
	 */
	public function hooks() {

		add_action( 'current_screen', [ $this, 'load_appointment' ], 5 );
		add_action( 'current_screen', [ $this, 'add_meta_boxes' ] );
		add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
		add_filter( 'screen_options_show_screen', '__return_false' );
		add_action(
			'sugar_calendar_bookings_admin_page_subheader_actions_left',
			[ $this, 'display_subheader_actions_left' ]
		);
		add_action(
			'sugar_calendar_bookings_admin_page_subheader_actions_right',
			[ $this, 'display_subheader_actions_right' ]
		);
	}

	/**
	 * Enqueue postbox scripts for metabox functionality.
	 *
	 * @since 1.0.0
	 * @since 1.2.1 Enqueue media and editor for service edit functionality.
	 */
	public function enqueue_scripts() {

		// Enqueue the postbox script for drag and drop functionality.
		wp_enqueue_script( 'postbox' );

		// Required for Service feature image (when editing service from appointment sidebar).
		wp_enqueue_media();

		// Required for WYSIWYG editor (when editing service from appointment sidebar).
		wp_enqueue_editor();
	}

	/**
	 * Add metaboxes for the appointment details page.
	 *
	 * @since 1.0.0
	 */
	public function add_meta_boxes() {

		$screen = get_current_screen();

		// Main content metaboxes.
		add_meta_box(
			'appointment-overview',
			esc_html(
				sprintf(
				/* translators: %1$d - appointment ID; %2$s - service name; %3$s - customer name. */
					__( '#%1$d - %2$s (%3$s)', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					$this->appointment->id,
					$this->appointment->service->name,
					$this->appointment->customer->name
				)
			),
			[ $this, 'appointment_overview_metabox' ],
			$screen->id,
			'normal',
			'high'
		);

		add_meta_box(
			'appointment-details',
			esc_html__( 'Details', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			[ $this, 'appointment_details_metabox' ],
			$screen->id,
			'side',
			'default'
		);

		add_meta_box(
			'payment-details',
			esc_html__( 'Payment Details', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			[ $this, 'payment_details_metabox' ],
			$screen->id,
			'side',
			'default'
		);

		add_meta_box(
			'actions-list',
			esc_html__( 'Actions', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			[ $this, 'actions_metabox' ],
			$screen->id,
			'side',
			'default'
		);
	}

	/**
	 * Render the appointment overview metabox.
	 *
	 * @since 1.0.0
	 * @since 1.2.0 Move form rows to render_appointment_overview_metabox_rows().
	 */
	public function appointment_overview_metabox() {

		if ( empty( $this->appointment ) ) {
			echo '<p>' . esc_html__( 'No appointment data available.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) . '</p>';

			return;
		}

		?>
		<table class="form-table striped">
			<?php $this->render_appointment_overview_metabox_rows(); ?>
		</table>
		<?php
	}

	/**
	 * Render appointment overview metabox rows.
	 *
	 * @since 1.2.0
	 */
	protected function render_appointment_overview_metabox_rows() {

		$rows = $this->get_appointment_overview_metabox_rows();

		foreach ( $rows as $row_id => $row_data ) {
			$label    = $row_data['label'] ?? '';
			$callback = $row_data['callback'] ?? null;

			if ( ! is_callable( $callback ) ) {
				continue;
			}

			$content = call_user_func( $callback );

			// Skip if callback returns null (conditional rows).
			if ( $content === null ) {
				continue;
			}

			?>
			<tr>
				<th scope="row"><?php echo esc_html( $label ); ?></th>
				<td><?php echo wp_kses_post( $content ); ?></td>
			</tr>
			<?php
		}
	}

	/**
	 * Get appointment overview metabox rows.
	 *
	 * Returns array of row definitions with labels and content callbacks.
	 *
	 * @since 1.2.0
	 *
	 * @return array<string, array{label: string, callback: callable}> Row definitions.
	 */
	protected function get_appointment_overview_metabox_rows() {

		return [
			'appointment_status' => [
				'label'    => __( 'Appointment Status', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				'callback' => [ $this, 'get_appointment_status_content' ],
			],
			'appointment_date'   => [
				'label'    => __( 'Appointment Date', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				'callback' => [ $this, 'get_appointment_date_content' ],
			],
			'customer'           => [
				'label'    => __( 'Customer', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				'callback' => [ $this, 'get_customer_content' ],
			],
			'service'            => [
				'label'    => __( 'Service', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				'callback' => [ $this, 'get_service_content' ],
			],
			'total'              => [
				'label'    => __( 'Total', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				'callback' => [ $this, 'get_total_content' ],
			],
			'location'           => [
				'label'    => __( 'Location', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				'callback' => [ $this, 'get_location_content' ],
			],
			'customer_notes'     => [
				'label'    => __( 'Customer Notes', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				'callback' => [ $this, 'get_customer_notes_content' ],
			],
		];
	}

	/**
	 * Get appointment status content.
	 *
	 * @since 1.2.0
	 *
	 * @return string
	 */
	protected function get_appointment_status_content() {

		return sprintf(
			'<span class="status-badge %1$s">%2$s</span>',
			esc_attr( $this->appointment->status ),
			esc_html( $this->appointment->get_status_label() )
		);
	}

	/**
	 * Get appointment date content.
	 *
	 * @since 1.2.0
	 *
	 * @return string
	 */
	protected function get_appointment_date_content() {

		return esc_html( Date::format_display_date( $this->appointment->start_at ) );
	}

	/**
	 * Get customer content.
	 *
	 * @since 1.2.0
	 *
	 * @return string
	 */
	protected function get_customer_content() {

		$output = esc_html( $this->appointment->customer->name );

		if ( ! empty( $this->appointment->customer->email ) ) {
			$output .= ' ' . wp_kses(
				sprintf(
					'(<a href="mailto:%1$s">%1$s</a>)',
					$this->appointment->customer->email
				),
				[
					'a' => [
						'href' => [],
					],
				]
			);
		}

		return $output;
	}

	/**
	 * Get service content.
	 *
	 * @since 1.2.0
	 * @since 1.2.1 Add service edit functionality.
	 *
	 * @return string
	 */
	protected function get_service_content() {

		return sprintf(
			'<a href="#" data-service-id="%1$d" role="button" aria-label="%3$s">%2$s</a>',
			(int) $this->appointment->service->id,
			esc_html( $this->appointment->service->name ),
			esc_attr(
				sprintf(
					/* translators: %s: service name. */
					esc_html__( 'Edit service: %s', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					$this->appointment->service->name
				)
			)
		);
	}

	/**
	 * Get total content.
	 *
	 * @since 1.2.0
	 *
	 * @return string
	 */
	protected function get_total_content() {

		$price = $this->appointment->service->price ?? 0;

		return wp_kses_post( PaymentsHelpers::format_currency( $price ) );
	}

	/**
	 * Get location content.
	 *
	 * @since 1.2.0
	 *
	 * @return string
	 */
	protected function get_location_content() {

		$location = $this->appointment->get_location();
		$output   = esc_html( $location->get_display_name() );

		if ( $location->get_type() === 'in_person' && ! empty( $this->appointment->location ) ) {
			$address  = $this->appointment->location->address ?? '';
			$maps_url = 'https://www.google.com/maps/search/' . rawurlencode( $address );

			$output .= sprintf(
				' <a href="%1$s" target="_blank" rel="noopener noreferrer" class="button-text-small map-link"><span class="button-text">%2$s</span><span class="button-icon dashicons dashicons-external"></span></a>',
				esc_url( $maps_url ),
				esc_html__( 'Open on Map', 'sugar-calendar-bookings-scheduling-appointments-lite' )
			);
		} elseif ( $location->get_type() === 'online' && ! empty( $this->appointment->online_location['join_url'] ) ) {
			$output = sprintf(
				'<a href="%1$s" target="_blank" rel="noopener noreferrer">%2$s</a>',
				esc_url( $this->appointment->online_location['join_url'] ),
				$output
			);
		}

		return $output;
	}

	/**
	 * Get customer notes content.
	 *
	 * @since 1.2.0
	 *
	 * @return string|null Null if no notes (skip row).
	 */
	protected function get_customer_notes_content() {

		if ( empty( $this->appointment->customer_notes ) ) {
			return null;
		}

		return esc_html( $this->appointment->customer_notes );
	}

	/**
	 * Render the appointment details metabox.
	 *
	 * @since 1.0.0
	 */
	public function appointment_details_metabox() {

		if ( empty( $this->appointment ) ) {
			echo '<div class="appointment-details-info"><p>' . esc_html__( 'No appointment data available.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) . '</p></div>';

			return;
		}

		?>
		<div class="appointment-details-info">
			<p>
				<span class="dashicons dashicons-admin-network"></span>
				<?php
				/* translators: %1$s - appointment ID. */
				printf(
				/* translators: %1$s - appointment ID. */
					esc_html__( 'Appointment ID: %1$s', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					'<strong>#' . esc_html( $this->appointment->id ) . '</strong>'
				);
				?>
			</p>
			<p>
				<span class="dashicons dashicons-calendar-alt"></span>
				<?php
				printf(
				/* translators: %1$s - formatted creation date. */
					esc_html__( 'Submitted: %1$s', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					'<strong>' . esc_html(
						Date::format_display_date( $this->appointment->created_at )
					) . '</strong>'
				);
				?>
			</p>
			<p>
				<span class="dashicons dashicons-edit"></span>
				<?php
				printf(
				/* translators: %1$s - formatted creation date. */
					esc_html__( 'Modified: %1$s', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					'<strong>' . esc_html(
						Date::format_display_date( $this->appointment->updated_at )
					) . '</strong>'
				);
				?>
			</p>
			<p>
				<span class="dashicons dashicons-admin-users"></span>
				<?php
				printf(
				/* translators: %1$s - customer name. */
					esc_html__( 'Customer: %1$s', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					'<strong>' . esc_html( $this->appointment->customer->name ) . '</strong>'
				);
				?>
			</p>
		</div>
		<div class="appointment-details-footer">
			<button type="button" class="button button-link button-link-delete">
				<?php esc_html_e( 'Delete', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?>
			</button>
			<div class="appointment-footer-actions">
				<?php if ( current_user_can( 'manage_options' ) && $this->appointment->status !== 'cancelled' ) { ?>
					<button type="button" class="button button-link-delete">
						<?php esc_html_e( 'Cancel', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?>
					</button>
				<?php } ?>
				<button type="button" class="button button-primary">
					<?php esc_html_e( 'Edit', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?>
				</button>
			</div>
		</div>
		<?php
	}

	/**
	 * Render the payment details metabox.
	 *
	 * @since 1.0.0
	 */
	public function payment_details_metabox() {

		if ( empty( $this->appointment ) ) {
			echo '<div class="payment-details-info"><p>' . esc_html__( 'No appointment data available.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) . '</p></div>';

			return;
		}

		// Get the first payment for this appointment.
		$payment = PaymentsHelpers::get_payment( $this->appointment );

		if ( ! $payment ) {
			echo '<div class="payment-details-info"><p>' . esc_html__( 'No payment information available.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) . '</p></div>';

			return;
		}

		?>
		<div class="payment-details-info">
			<p>
				<?php UI::svg_icon( 'status' ); ?>
				<?php
				printf(
				/* translators: %1$s - payment status. */
					esc_html__( 'Status: %1$s', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					'<strong class="payment-status-' . esc_attr( $payment->status ) . '">' . esc_html( $payment->get_status_label() ) . '</strong>'
				);
				?>
			</p>

			<p>
				<?php UI::svg_icon( 'cart' ); ?>
				<?php
				printf(
				/* translators: %1$s - formatted amount with currency. */
					esc_html__( 'Total: %1$s', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					'<strong>' . wp_kses_post( PaymentsHelpers::format_currency( (float) $payment->total_amount, $payment->currency ) ) . '</strong>'
				);
				?>
			</p>

			<p>
				<?php UI::svg_icon( 'icon-card' ); ?>
				<?php
				$gateway_name = PaymentsHelpers::get_payment_gateway_display_name( $payment );

				printf(
				/* translators: %1$s - payment gateway name. */
					esc_html__( 'Processor: %1$s', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					'<strong>' . esc_html( $gateway_name ) . '</strong>'
				);
				?>
			</p>
		</div>

		<!-- Footer -->
		<div class="payment-details-footer">
			<?php
			// Show refund link if appointment is cancelled and payment is refundable.
			if ( $this->appointment->status === 'cancelled' && $payment->gateway !== OnSitePaymentGateway::ID && $payment->status === PaymentModel::STATUS_COMPLETED ) :
				?>
				<a href="#" class="refund-payment-btn" data-payment-id="<?php echo esc_attr( $payment->id ); ?>">
					<?php esc_html_e( 'Refund', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?>
				</a>
			<?php endif; ?>
			<a href="<?php echo esc_url( admin_url( 'admin.php?page=sugar-calendar-bookings-payments&view=details&payment_id=' . $payment->id ) ); ?>" class="button button-secondary">
				<?php esc_html_e( 'View Payment', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?>
			</a>
		</div>
		<?php
	}

	/**
	 * Render the actions metabox.
	 *
	 * @since 1.0.0
	 */
	public function actions_metabox() {

		if ( empty( $this->appointment ) ) {
			echo '<p>' . esc_html__( 'No appointment data available.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) . '</p>';

			return;
		}

		$actions = $this->get_available_notification_actions( $this->appointment );

		if ( empty( $actions ) ) {
			echo '<p>' . esc_html__( 'No notification actions available.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) . '</p>';

			return;
		}

		?>
		<div class="actions-list">
			<?php foreach ( $actions as $action ) : ?>
				<p>
					<a href="#"
						class="resend-notification-action"
						data-notification-action="<?php echo esc_attr( $action['id'] ); ?>"
						data-notification-types="<?php echo esc_attr( wp_json_encode( $action['notification_types'] ) ); ?>"
						data-action-label="<?php echo esc_attr( $action['label'] ); ?>">
						<?php UI::svg_icon( $action['icon'] ); ?>
						<?php echo esc_html( $action['label'] ); ?>
					</a>
				</p>
			<?php endforeach; ?>
		</div>
		<?php
	}

	/**
	 * Get available notification actions for an appointment.
	 *
	 * @since 1.1.0
	 *
	 * @param AppointmentModel $appointment Appointment model.
	 *
	 * @return array Array of available notification actions with id, label, icon, and notification_types.
	 */
	private function get_available_notification_actions( $appointment ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh

		$actions = [];

		// Pending notification - show if status is pending.
		if ( $appointment->status === AppointmentModel::STATUS_PENDING ) {
			$actions[] = [
				'id'                 => 'pending',
				'label'              => esc_html__( 'Resend Pending Notification', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				'icon'               => 'envelope',
				'notification_types' => [ 'booking_pending_attendee', 'booking_pending_employee' ],
			];
		}

		// Confirmation and reminder notifications - show if status is approved.
		if ( $appointment->status === AppointmentModel::STATUS_APPROVED ) {
			$actions[] = [
				'id'                 => 'confirmation',
				'label'              => esc_html__( 'Resend Confirmation Notification', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				'icon'               => 'envelope',
				'notification_types' => [ 'booking_confirmation_attendee', 'booking_confirmation_employee' ],
			];
			$actions[] = [
				'id'                 => 'reminder',
				'label'              => esc_html__( 'Resend Reminder Notification', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				'icon'               => 'envelope',
				'notification_types' => [ 'appointment_reminder_attendee', 'appointment_reminder_employee' ],
			];
		}

		// Update notification - show for all appointments.
		$actions[] = [
			'id'                 => 'update',
			'label'              => esc_html__( 'Resend Update Notification', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			'icon'               => 'envelope',
			'notification_types' => [ 'booking_update_attendee', 'booking_update_employee' ],
		];

		// Cancellation notification - show if status is cancelled.
		if ( $appointment->status === AppointmentModel::STATUS_CANCELLED ) {
			$actions[] = [
				'id'                 => 'cancellation',
				'label'              => esc_html__( 'Resend Cancellation Notification', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				'icon'               => 'envelope',
				'notification_types' => [ 'booking_cancellation_attendee', 'booking_cancellation_employee' ],
			];
		}

		// Refund notification - show if appointment has payment with refunded status.
		if ( ! empty( $appointment->payments ) && is_array( $appointment->payments ) ) {
			foreach ( $appointment->payments as $payment ) {
				if ( $payment instanceof PaymentModel && $payment->status === PaymentModel::STATUS_REFUNDED ) {
					$actions[] = [
						'id'                 => 'refund',
						'label'              => esc_html__( 'Resend Refund Notification', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
						'icon'               => 'envelope',
						'notification_types' => [ 'payment_refund_attendee' ],
					];

					break;
				}
			}
		}

		return $actions;
	}

	/**
	 * Load the appointment from the URL parameter.
	 *
	 * @since 1.0.0
	 */
	public function load_appointment() {

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$appointment_id = isset( $_GET['appointment_id'] ) ? absint( $_GET['appointment_id'] ) : 0;

		if ( empty( $appointment_id ) ) {
			wp_die( esc_html__( 'Invalid appointment ID.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		// Load the appointment with all relationships.
		$this->appointment = AppointmentModel::query()
			->with(
				[
					'service'       => fn( $q ) => $q->select( [ 'id', 'name', 'color', 'duration', 'price' ] ),
					'customer'      => fn( $q ) => $q->select( [ 'id', 'name', 'email', 'phone' ] ),
					'employee'      => fn( $q ) => $q->select( [ 'id', 'name', 'email' ] ),
					'location'      => fn( $q ) => $q->select( [ 'id', 'name', 'address' ] ),
					'payments'      => fn( $q ) => $q->select( [ 'id', 'status', 'total_amount', 'currency', 'gateway' ] ),
					'notes'         => fn( $q ) => $q->order_by( 'created_at', 'DESC' ),
					'activity_logs' => fn( $q ) => $q->order_by( 'created_at', 'DESC' ),
				]
			)
			->where( 'id', '=', $appointment_id, '%d' )
			->first();

		if ( empty( $this->appointment ) ) {
			wp_die( esc_html__( 'Appointment not found.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		// Check user capabilities.
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'You are not allowed to view this appointment.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}
	}

	/**
	 * Display the admin subheader left actions.
	 *
	 * @since 1.0.0
	 */
	public function display_subheader_actions_left() {

		// Back to appointments list button.
		UI::button(
			[
				'text'  => esc_html__( 'Back to Appointments', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				'class' => 'sugar-calendar-bookings-back-to',
				'size'  => 'sm',
				'link'  => AppointmentsListPage::get_url(),
			]
		);
	}

	/**
	 * Get pagination data for the current appointment.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	private function get_pagination_data() {

		if ( empty( $this->appointment ) ) {
			return [
				'current' => 0,
				'total'   => 0,
				'prev_id' => null,
				'next_id' => null,
			];
		}

		return $this->get_appointment_navigation_data( $this->appointment->id );
	}

	/**
	 * Get efficient navigation data for an appointment.
	 *
	 * @since 1.0.0
	 *
	 * @param int $appointment_id The appointment ID.
	 *
	 * @return array Navigation data with current position, total count, and previous/next IDs.
	 */
	private function get_appointment_navigation_data( $appointment_id ) {

		// Get total count efficiently.
		$total = AppointmentModel::query()
			->count();

		if ( $total === 0 ) {
			return [
				'current' => 0,
				'total'   => 0,
				'prev_id' => null,
				'next_id' => null,
			];
		}

		// Find current position efficiently using subquery.
		$current_position = $this->get_appointment_position_by_id( $appointment_id );

		if ( $current_position === null ) {
			return [
				'current' => 0,
				'total'   => $total,
				'prev_id' => null,
				'next_id' => null,
			];
		}

		// Get previous appointment ID efficiently.
		$prev_id = $this->get_previous_appointment_id( $appointment_id );

		// Get next appointment ID efficiently.
		$next_id = $this->get_next_appointment_id( $appointment_id );

		return [
			'current' => $current_position,
			'total'   => $total,
			'prev_id' => $prev_id,
			'next_id' => $next_id,
		];
	}

	/**
	 * Get the position of an appointment by ID.
	 *
	 * @since 1.0.0
	 *
	 * @param int $appointment_id The appointment ID.
	 *
	 * @return int|null Position (1-based) or null if not found.
	 */
	private function get_appointment_position_by_id( $appointment_id ) {

		return AppointmentModel::query()
			->where( 'id', '<=', $appointment_id, '%d' )
			->count();
	}

	/**
	 * Get the previous appointment ID.
	 *
	 * @since 1.0.0
	 *
	 * @param int $appointment_id The current appointment ID.
	 *
	 * @return int|null Previous appointment ID or null if none.
	 */
	private function get_previous_appointment_id( $appointment_id ) {

		$appointment = AppointmentModel::query()
			->where( 'id', '<', $appointment_id, '%d' )
			->order_by( 'id', 'DESC' )
			->limit( 1 )
			->first();

		return $appointment ? $appointment->id : null;
	}

	/**
	 * Get the next appointment ID.
	 *
	 * @since 1.0.0
	 *
	 * @param int $appointment_id The current appointment ID.
	 *
	 * @return int|null Next appointment ID or null if none.
	 */
	private function get_next_appointment_id( $appointment_id ) {

		$appointment = AppointmentModel::query()
			->where( 'id', '>', $appointment_id, '%d' )
			->order_by( 'id', 'ASC' )
			->limit( 1 )
			->first();

		return $appointment ? $appointment->id : null;
	}

	/**
	 * Display the admin subheader right actions.
	 *
	 * @since 1.0.0
	 */
	public function display_subheader_actions_right() {

		if ( empty( $this->appointment ) ) {
			return;
		}

		$pagination = $this->get_pagination_data();

		if ( $pagination['total'] <= 1 ) {
			return;
		}
		?>
		<div class="sugar-calendar-bookings-pagination">
			<span>
				<?php
				printf(
				/* translators: %1$d - current appointment number; %2$d - total appointments. */
					esc_html__( 'Appointment %1$d of %2$d', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
					(int) $pagination['current'],
					(int) $pagination['total']
				);
				?>
			</span>
			<div class="sugar-calendar-bookings-pagination__controls">
				<?php // phpcs:disable Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace ?>
				<a <?php if ( ! is_null( $pagination['prev_id'] ) ) : ?> href="<?php echo esc_url( self::get_url( $pagination['prev_id'] ) ); ?>"<?php endif; ?>
					class="sugar-calendar-bookings-pagination__control sugar-calendar-bookings-pagination__control--prev"
					<?php disabled( is_null( $pagination['prev_id'] ) ); ?>></a>
				<span class="sugar-calendar-bookings-pagination__control sugar-calendar-bookings-pagination__control--current"><?php echo esc_html( (int) $pagination['current'] ); ?></span>
				<a <?php if ( ! is_null( $pagination['next_id'] ) ) : ?> href="<?php echo esc_url( self::get_url( $pagination['next_id'] ) ); ?>"<?php endif; ?>
					class="sugar-calendar-bookings-pagination__control sugar-calendar-bookings-pagination__control--next"
					<?php disabled( is_null( $pagination['next_id'] ) ); ?>></a>
				<?php // phpcs:enable Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace ?>
			</div>
		</div>
		<?php
	}

	/**
	 * Page URL.
	 *
	 * @since 1.0.0
	 *
	 * @param null|int $appointment_id Appointment ID.
	 *
	 * @return string
	 */
	public static function get_url( $appointment_id = null ) {

		$url = parent::get_url();

		if ( ! is_null( $appointment_id ) ) {
			$url = add_query_arg( 'appointment_id', $appointment_id, $url );
			$url = add_query_arg( 'view', 'details', $url );
		}

		return $url;
	}

	/**
	 * Get the current appointment.
	 *
	 * @since 1.0.0
	 *
	 * @return AppointmentModel|null
	 */
	public function get_appointment() {

		return $this->appointment;
	}

	/**
	 * Display the page content.
	 *
	 * @since 1.0.0
	 */
	public function display_content() {

		if ( empty( $this->appointment ) ) {
			?>
			<div class="notice notice-error">
				<p><?php esc_html_e( 'Appointment not found.', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?></p>
			</div>
			<?php

			return;
		}

		$screen = get_current_screen();

		?>
		<div class="appointment-details-page wrap">
			<div id="poststuff">
				<div id="post-body" class="metabox-holder columns-2">
					<!-- Main content area -->
					<div id="post-body-content">
						<div id="postbox-container-2" class="postbox-container">
							<div id="normal-sortables" class="meta-box-sortables ui-sortable">
								<?php do_meta_boxes( $screen->id, 'normal', $this->appointment ); ?>
							</div>
						</div>
						<div id="postbox-container-3" class="postbox-container">
							<div id="advanced-sortables" class="meta-box-sortables ui-sortable">
								<?php do_meta_boxes( $screen->id, 'advanced', $this->appointment ); ?>
							</div>
						</div>
					</div>

					<!-- Sidebar -->
					<div id="postbox-container-1" class="postbox-container">
						<div id="side-sortables" class="meta-box-sortables ui-sortable">
							<?php do_meta_boxes( $screen->id, 'side', $this->appointment ); ?>
						</div>
					</div>
				</div>
			</div>
		</div>

		<!-- Placeholder for sidebar (will be added when integrating with existing appointment form) -->
		<div id="sugar-calendar-bookings-sidebar"></div>

		<?php UI::confirmation_modal(); ?>

		<?php
		// Add nonce for metabox ordering AJAX requests.
		wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
		wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
		?>
		<?php
	}

	/**
	 * Get page script data.
	 *
	 * @since 1.1.0
	 *
	 * @return array
	 */
	public function get_script_data() {

		$script_data = [
			'screen_id' => get_current_screen()->id ?? null,
		];

		// Check if we should show refund prompt after cancellation.
		// phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$get_data = InputProcessor::process(
			wp_unslash( $_GET ?? [] ),
			[ 'show_refund_prompt' => 'string|in:1' ]
		);
		// phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

		if ( ! empty( $get_data['show_refund_prompt'] ) && $this->appointment && $this->appointment->status === 'cancelled' ) {
			$payment = PaymentsHelpers::get_refundable_payment( $this->appointment );

			if ( $payment ) {
				$script_data['show_refund_prompt'] = true;
				$script_data['payment_id']         = $payment->id;
			}
		}

		return $script_data;
	}
}
