<?php

namespace SugarCalendarBookings\Appointments;

use SugarCalendarBookings\Admin\Assets;
use SugarCalendarBookings\BookingForm\BookingFormService;
use SugarCalendarBookings\Plugin;
use SugarCalendarBookings\Support\Bootable;
use SugarCalendarBookings\Support\DataProcessing\InputProcessor;
use SugarCalendarBookings\Support\Helpers\Helpers;

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

/**
 * Handler for processing appointment cancellation requests from frontend.
 *
 * @since 1.1.0
 */
class CancellationHandler implements Bootable {

	/**
	 * Hook into WordPress.
	 *
	 * @since 1.1.0
	 */
	public function hooks() {

		if ( ! $this->allow_load() ) {
			return;
		}

		add_action( 'template_redirect', [ $this, 'display_cancellation_page' ] );
		add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ] );
	}

	/**
	 * Check if handler should load.
	 *
	 * @since 1.1.0
	 *
	 * @return bool True if should load, false otherwise.
	 */
	public function allow_load(): bool {

		// Only load on frontend, not in admin.
		return ! is_admin();
	}

	/**
	 * Display cancellation page (GET request).
	 *
	 * @since 1.1.0
	 */
	public function display_cancellation_page() {

		// Only handle if this is a cancellation page request.
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( ! isset( $_GET['scbookings_cancel_appointment'] ) || $_GET['scbookings_cancel_appointment'] !== '1' ) {
			return;
		}

		// Get and validate cancellation request data.
		$request_data = $this->get_cancellation_request_data();

		// If there's an error (invalid link, expired token, etc.), show error page.
		if ( isset( $request_data['error'] ) ) {
			$this->display_page( 'error', null, null, $request_data['error'] );

			return;
		}

		// If appointment is already cancelled, don't show cancellation page at all.
		// Let WordPress handle the request normally (will show 404 or default page).
		if ( $request_data['appointment']->status === AppointmentModel::STATUS_CANCELLED ) {
			return;
		}

		// Display confirmation page.
		// Note: Success and error states after AJAX cancellation are handled inline by the React component.
		$this->display_page( 'confirmation', $request_data['appointment'] );
	}

	/**
	 * Get and validate cancellation request data.
	 *
	 * This method handles all the validation and returns the appointment and token if valid.
	 * Returns error message if validation fails.
	 *
	 * @since 1.1.0
	 *
	 * @return array Array with 'appointment', 'token', and optionally 'error' keys.
	 */
	private function get_cancellation_request_data() {

		// Process GET parameters.
		// phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$get_data = InputProcessor::process(
			wp_unslash( $_GET ?? [] ),
			[
				'scbookings_cancel_appointment' => 'required|string|in:1',
				'id'                            => 'required|string',
				'token'                         => 'required|string',
			]
		);
		// phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

		// Bail if validation failed.
		if ( is_wp_error( $get_data ) || empty( $get_data['scbookings_cancel_appointment'] ) || empty( $get_data['id'] ) || empty( $get_data['token'] ) ) {
			return [
				'error' => esc_html__( 'Invalid cancellation link. Please contact support if you need to cancel your appointment.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			];
		}

		$token_service = new CancellationTokenService();

		// Decode appointment ID.
		$appointment_id = $token_service->decode_appointment_id( $get_data['id'] );

		if ( ! $appointment_id ) {
			return [
				'error' => esc_html__( 'Invalid cancellation link. Please contact support if you need to cancel your appointment.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			];
		}

		// Get appointment by ID.
		$appointment = AppointmentModel::get( $appointment_id );

		if ( ! $appointment ) {
			return [
				'error' => esc_html__( 'Appointment not found.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			];
		}

		// Verify token.
		if ( ! $token_service->verify_cancellation_token( $get_data['token'], $appointment ) ) {
			return [
				'error' => esc_html__( 'Invalid cancellation link. Please contact support if you need to cancel your appointment.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			];
		}

		return [
			'appointment' => $appointment,
			'token'       => $get_data['token'],
		];
	}

	/**
	 * Prepare cancellation page data for React component.
	 *
	 * @since 1.1.0
	 *
	 * @param AppointmentModel|null $appointment Appointment model.
	 * @param string                $mode        Display mode: 'confirmation', 'success', or 'error'.
	 * @param string|null           $message     Success message.
	 * @param string|null           $error       Error message.
	 *
	 * @return array Cancellation data array.
	 */
	private function prepare_cancellation_data( ?AppointmentModel $appointment, string $mode, ?string $message = null, ?string $error = null ): array {

		// Prepare appointment data for React component using the same method as booking form.
		$appointment_data = null;

		if ( $appointment ) {
			$booking_form_service = Plugin::get( BookingFormService::class );
			$appointment_data     = $booking_form_service->get_appointment_form_data( $appointment );
		}

		return [
			'mode'        => $mode,
			'appointment' => $appointment_data,
			'message'     => $message,
			'error'       => $error,
		];
	}

	/**
	 * Display cancellation page.
	 *
	 * @since 1.1.0
	 *
	 * @param string                $mode        Display mode: 'confirmation', 'success', or 'error'.
	 * @param AppointmentModel|null $appointment Optional appointment model.
	 * @param string|null           $message     Optional success message.
	 * @param string|null           $error       Optional error message.
	 */
	private function display_page( string $mode, ?AppointmentModel $appointment = null, ?string $message = null, ?string $error = null ) {

		$cancellation_data = $this->prepare_cancellation_data( $appointment, $mode, $message, $error );

		Helpers::template(
			'cancellation-page.php',
			[
				'cancellation_data' => $cancellation_data,
			]
		);

		exit;
	}

	/**
	 * Enqueue cancellation page assets.
	 *
	 * @since 1.1.0
	 */
	public function enqueue_assets() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh

		// Only enqueue if this is a cancellation page request.
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( ! isset( $_GET['scbookings_cancel_appointment'] ) || $_GET['scbookings_cancel_appointment'] !== '1' ) {
			return;
		}

		$assets = Plugin::get( Assets::class );

		// Enqueue dev runtime if in dev mode.
		if ( $assets->is_dev() ) {
			$assets->enqueue_dev_runtime();
		}

		// Register React JSX Runtime if not already registered.
		if ( ! wp_script_is( 'react-jsx-runtime', 'registered' ) ) {
			wp_register_script(
				'react-jsx-runtime',
				SCBOOKINGS_PLUGIN_URL . 'assets/lib/react-jsx-runtime.js',
				[ 'react' ],
				'18.3.1',
				true
			);
		}

		// Get manifest and enqueue cancellation script.
		$manifest = $assets->get_asset_manifest( 'frontend/templates/cancellation' );

		$dependencies = $manifest['dependencies'] ?? [ 'wp-element', 'react-jsx-runtime' ];
		$version      = $manifest && $assets->is_dev() ? $manifest['version'] : SCBOOKINGS_PLUGIN_VERSION;

		wp_enqueue_script(
			'scbookings-cancellation-script',
			$assets->get_src( 'frontend/templates/cancellation.js' ),
			$dependencies,
			$version,
			true
		);

		wp_set_script_translations(
			'scbookings-cancellation-script',
			'sugar-calendar-bookings-scheduling-appointments-lite',
			SCBOOKINGS_PLUGIN_PATH . 'assets/languages'
		);

		// Enqueue booking form styles.
		$style_manifest = $assets->get_asset_manifest( 'booking-form/styles' );

		wp_enqueue_style(
			'scbookings-booking-form-style',
			$assets->get_src( 'booking-form/styles.css' ),
			[],
			$style_manifest && $assets->is_dev() ? $style_manifest['version'] : SCBOOKINGS_PLUGIN_VERSION
		);

		// Localize script data for API calls.
		wp_localize_script(
			'scbookings-cancellation-script',
			'sugar_calendar_bookings',
			Plugin::get( BookingFormService::class )->get_script_data()
		);
	}
}
