<?php

namespace SmartSlotAppointmentScheduler;

use SmartSlotAppointmentScheduler\Send_Mail;
use WP_Error;
use WP_REST_Response;

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

if ( ! class_exists( '\SmartSlotAppointmentScheduler\Post_User_Api' ) ) {

	/**
	 * Class to get booking details through the API
	 * and update the booking database table.
	 */
	class Post_User_Api {

		/**
		 * Initialize REST API endpoint.
		 *
		 * @return void
		 */
		public static function init() {

			add_action(
				'rest_api_init',
				[ __CLASS__, 'register_post_user' ]
			);
		}

		/**
		 * Register REST route.
		 *
		 * @return void
		 */
		public static function register_post_user() {

			register_rest_route(
				'ssas/v1',
				'/postuser',
				[
					'methods'             => 'POST',
					'callback'            => [ __CLASS__, 'receive_user_request' ],
					'permission_callback' => [ __CLASS__, 'permission_check' ],
				]
			);
		}

		/**
		 * Verify REST request permission.
		 *
		 * @param \WP_REST_Request $request REST request.
		 * @return bool|WP_Error
		 */
		public static function permission_check( $request ) {

			$nonce = $request->get_header( 'X-WP-Nonce' );

			if ( empty( $nonce ) ) {
				$nonce = $request->get_param( '_wpnonce' );
			}

			if (
				! empty( $nonce ) &&
				wp_verify_nonce( $nonce, 'wp_rest' )
			) {
				return true;
			}

			return new WP_Error(
				'rest_forbidden',
				__(
					'You do not have permission to create an appointment.',
					'smartslot-appointment-scheduler'
				),
				[
					'status' => rest_authorization_required_code(),
				]
			);
		}

		/**
		 * Receive and process booking request.
		 *
		 * @param \WP_REST_Request $request REST request.
		 * @return WP_REST_Response|WP_Error
		 */
		public static function receive_user_request( $request ) {

			$user_detail = $request->get_param( 'userDetail' );

			$user_detail = is_array( $user_detail )
				? $user_detail
				: [];

			$first_name = sanitize_text_field(
				$user_detail['firstName'] ?? ''
			);

			$last_name = sanitize_text_field(
				$user_detail['lastName'] ?? ''
			);

			$email = sanitize_email(
				$user_detail['email'] ?? ''
			);

			$phone = sanitize_text_field(
				$user_detail['mobileNumber'] ?? ''
			);

			$address = sanitize_text_field(
				$user_detail['address'] ?? ''
			);

			$notes = sanitize_textarea_field(
				$user_detail['notes'] ?? ''
			);

			$appointment_date = sanitize_text_field(
				$request->get_param( 'bookedDate' )
			);

			$appointment_time = sanitize_text_field(
				$request->get_param( 'bookedTime' )
			);

			$token = self::generate_token_id();

			$status = 1;

			/**
			 * Read category IDs.
			 */
			$category_ids = array_filter(
				array_map(
					'absint',
					(array) $request->get_param( 'categories' )
				)
			);

			/**
			 * Read service IDs.
			 */
			$service_ids = array_filter(
				array_map(
					'absint',
					(array) $request->get_param( 'services' )
				)
			);

			/**
			 * Support old parameter names.
			 */
			if ( empty( $category_ids ) ) {

				$category_ids = array_filter(
					array_map(
						'absint',
						(array) $request->get_param( 'specialities' )
					)
				);
			}

			if ( empty( $service_ids ) ) {

				$service_ids = array_filter(
					array_map(
						'absint',
						(array) $request->get_param( 'checkups' )
					)
				);
			}

			/**
			 * Support old ssas_* parameter names.
			 */
			if ( empty( $category_ids ) ) {

				$category_ids = array_filter(
					array_map(
						'absint',
						(array) $request->get_param( 'ssas_specialities' )
					)
				);
			}

			if ( empty( $service_ids ) ) {

				$service_ids = array_filter(
					array_map(
						'absint',
						(array) $request->get_param( 'ssas_checkups' )
					)
				);
			}

			/**
			 * Remove duplicates and reset indexes.
			 */
			$category_ids = array_values(
				array_unique( $category_ids )
			);

			$service_ids = array_values(
				array_unique( $service_ids )
			);

			/**
			 * Validate email.
			 */
			if ( ! is_email( $email ) ) {

				return new WP_Error(
					'invalid_email',
					__(
						'A valid email address is required.',
						'smartslot-appointment-scheduler'
					),
					[
						'status' => 400,
					]
				);
			}

			/**
			 * Validate appointment date.
			 */
			$date_object = \DateTime::createFromFormat(
				'!Y-m-d',
				$appointment_date
			);

			if (
				! $date_object ||
				$date_object->format( 'Y-m-d' ) !== $appointment_date
			) {

				return new WP_Error(
					'invalid_date',
					__(
						'Invalid date format.',
						'smartslot-appointment-scheduler'
					),
					[
						'status' => 400,
					]
				);
			}

			/**
			 * Validate appointment time.
			 */
			$time_object = \DateTime::createFromFormat(
				'H:i',
				$appointment_time
			);

			if ( ! $time_object ) {

				$time_object = \DateTime::createFromFormat(
					'H:i:s',
					$appointment_time
				);
			}

			if ( ! $time_object ) {

				return new WP_Error(
					'invalid_time',
					__(
						'Invalid time format.',
						'smartslot-appointment-scheduler'
					),
					[
						'status' => 400,
					]
				);
			}

			/**
			 * Resolve provider schedule.
			 */
			$provider_id = self::update_schedule(
				$category_ids,
				$service_ids,
				$token
			);

			/**
			 * Keep existing category_id column,
			 * but store categories and services separately.
			 */
			$category_service_data = [
				'categories' => $category_ids,
				'services'   => $service_ids,
			];

			$data = [
				'token_number'     => $token,
				'first_name'       => $first_name,
				'last_name'        => $last_name,
				'email'            => $email,
				'phone'            => $phone,
				'address'          => $address,
				'notes'            => $notes,
				'appointment_date' => $appointment_date,
				'appointment_time' => $time_object->format( 'H:i:s' ),
				'provider_id'      => $provider_id,
				'category_id'      => wp_json_encode(
					$category_service_data
				),
				'status'           => $status,
			];

			/**
			 * Save appointment.
			 */
			$result = self::update_user_table( $data );

			if ( false === $result ) {

				return new WP_Error(
					'db_insert_error',
					__(
						'Failed to save user information to database.',
						'smartslot-appointment-scheduler'
					),
					[
						'status' => 500,
					]
				);
			}

			/**
			 * Send notification email.
			 */
			new Send_Mail( $data );

			/**
			 * Clear report cache.
			 */
			wp_cache_set(
				'reports_cache_version',
				(string) microtime( true ),
				'ssas_reports'
			);

			return new WP_REST_Response(
				[
					'message' => __(
						'User information successfully saved',
						'smartslot-appointment-scheduler'
					),
					'data'    => [
						'token' => $token,
					],
				],
				200
			);
		}

		/**
		 * Resolve available schedule posts.
		 *
		 * @param array  $category_ids Category IDs.
		 * @param array  $service_ids Service IDs.
		 * @param string $token Booking token.
		 * @return string JSON encoded provider IDs.
		 */
		public static function update_schedule(
			$category_ids,
			$service_ids,
			$token
		) {

			// phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_tax_query

			$category_ids = array_values(
				array_filter(
					array_map(
						'absint',
						(array) $category_ids
					)
				)
			);

			$service_ids = array_values(
				array_filter(
					array_map(
						'absint',
						(array) $service_ids
					)
				)
			);

			$cache_key = 'ssas_schedule_posts_' . md5(
				wp_json_encode(
					[
						'categories' => $category_ids,
						'services'   => $service_ids,
					]
				)
			);

			$cache_group = 'ssas_booking';

			$cached_posts = wp_cache_get(
				$cache_key,
				$cache_group
			);

			if ( false !== $cached_posts ) {

				return wp_json_encode(
					$cached_posts
				);
			}

			$tax_query = [];

			if ( ! empty( $category_ids ) ) {

				$tax_query[] = [
					'taxonomy' => 'ssas_categories',
					'field'    => 'term_id',
					'terms'    => $category_ids,
					'operator' => 'IN',
				];
			}

			if ( ! empty( $service_ids ) ) {

				$tax_query[] = [
					'taxonomy' => 'ssas_services',
					'field'    => 'term_id',
					'terms'    => $service_ids,
					'operator' => 'IN',
				];
			}

			$all_post_ids = [];

			if ( ! empty( $tax_query ) ) {

				if ( count( $tax_query ) > 1 ) {
					$tax_query['relation'] = 'AND';
				}

				$all_post_ids = get_posts(
					[
						'post_type'              => Schedule_Posttype::SLUG,
						'posts_per_page'         => -1,
						'fields'                 => 'ids',
						'tax_query'              => $tax_query,
						'no_found_rows'          => true,
						'update_post_meta_cache' => false,
						'update_post_term_cache' => false,
					]
				);

				wp_cache_set(
					$cache_key,
					$all_post_ids,
					$cache_group,
					HOUR_IN_SECONDS
				);
			}

			// phpcs:enable WordPress.DB.SlowDBQuery.slow_db_query_tax_query

			return wp_json_encode(
				$all_post_ids
			);
		}

		/**
		 * Generate unique booking token.
		 *
		 * @return string Booking token.
		 */
		public static function generate_token_id() {

			global $wpdb;

			$token_length = (int) get_option(
				'booking_schedule_token_length',
				4
			);

			$token_prefix = sanitize_text_field(
				get_option(
					'booking_schedule_appintment_token_prefix',
					''
				)
			);

			/**
			 * Atomically increment sequence.
			 */
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Required atomic update for booking token sequence.
			$wpdb->query(
				$wpdb->prepare(
					'UPDATE %i
					SET option_value = CAST(option_value AS UNSIGNED) + 1
					WHERE option_name = %s',
					$wpdb->options,
					'ssas_booking_sequence'
				)
			);

			$token_sequence = (int) get_option(
				'ssas_booking_sequence',
				1
			);

			return $token_prefix . str_pad(
				(string) $token_sequence,
				max( 1, $token_length ),
				'0',
				STR_PAD_LEFT
			);
		}

		/**
		 * Update the booking appointment table.
		 *
		 * The custom appointment table cannot be written through
		 * the WordPress post API.
		 *
		 * @param array $data Booking data.
		 * @return int|false Number of rows inserted, or false on failure.
		 */
		public static function update_user_table( $data ) {

			global $wpdb;

			$table_name = 'pkart_ssas_booking_appointment';

			$data_formats = [
				'%s',
				'%s',
				'%s',
				'%s',
				'%s',
				'%s',
				'%s',
				'%s',
				'%s',
				'%s',
				'%s',
				'%d',
			];

			/**
			 * The appointment table is a plugin-owned custom table,
			 * so wpdb::insert() is the appropriate WordPress API.
			 *
			 * The insert operation is intentionally not cached because
			 * it changes persistent database state. The report cache is
			 * invalidated after a successful insert.
			 */
			// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Required write to the plugin-owned custom appointment table.
			$result = $wpdb->insert(
				$table_name,
				$data,
				$data_formats
			);
			// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching

			if ( false !== $result ) {

				wp_cache_set(
					'reports_cache_version',
					(string) microtime( true ),
					'ssas_reports'
				);
			}

			return $result;
		}
	}

	add_action(
		'init',
		[
			'\\SmartSlotAppointmentScheduler\\Post_User_Api',
			'init',
		],
		11
	);
}
