<?php
/**
 * FE Admin Tickets controller.
 *
 * @since 1.0
 * @since 1.4 Event authorization restriction
 */

declare( strict_types=1 );
namespace FeData\FastEvents\RestAPI\Public;

defined( 'ABSPATH' ) || exit;

use FeData\FastEvents\Helpers\AuthHelper;
use FeData\FastEvents\Helpers\OrderHelper;
use FeData\FastEvents\Helpers\SeatsHelper;
use stdClass;
use WP_Error;
use WP_REST_Controller;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;


/**
 * FE Admin Tickets class.
 */
class TicketsController extends WP_REST_Controller {


	/**
	 * Admin Tickets constructor.
	 *
	 * @param AuthHelper $auth The auth helper.
	 * @since 1.0
	 */
	public function __construct( private readonly AuthHelper $auth ) {
		$this->namespace = 'fast-events/v1';
		$this->rest_base = 'admin/tickets';
	}


	/**
	 * Register the routes for the objects of the controller.
	 *
	 * @since 1.0
	 * @since 2.0 Refactored for a new permission system
	 */
	public function register_routes(): void {

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>[\d]+)',
			array(
				'args'   => array(
					'id' => array(
						'description'       => __( 'The unique id of the order', 'fast-events' ),
						'type'              => 'integer',
						'required'          => true,
						'sanitize_callback' => 'absint',
					),
				),
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_item' ),
					'permission_callback' => function () {
						return $this->fast_events_admin_permissions( $this->auth::TICKETS_READ );
					},
					'args'                => array(
						'context' => $this->get_context_param( array( 'default' => 'view' ) ),
					),
				),
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'create_item' ),
					'permission_callback' => function () {
						return $this->fast_events_admin_permissions( $this->auth::TICKETS_CREATE );
					},
					'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
				),
				array(
					'methods'             => WP_REST_Server::DELETABLE,
					'callback'            => array( $this, 'delete_item' ),
					'permission_callback' => function () {
						return $this->fast_events_admin_permissions( $this->auth::TICKETS_DELETE );
					},
				),
				'schema' => array( $this, 'get_public_item_schema' ),
			)
		);
	}


	/**
	 * Get the authorization for the Tickets endpoints.
	 *
	 * @param int $action The action on the controller.
	 * @return WP_Error|bool
	 *
	 * @since 1.0
	 * @since 2.0 Refactored for a new permission system
	 */
	public function fast_events_admin_permissions( int $action ): WP_Error|bool {
		return $this->auth->authorize( $action );
	}


	/**
	 * Retrieve tickets.
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
	 *
	 * @since 1.0
	 */
	public function get_item( $request ): WP_Error|WP_REST_Response {

		// Look up the order.
		$tickets = $this->get_tickets( $request['id'] );
		if ( empty( $tickets ) ) {
			return rest_ensure_response( array() );
		}

		return rest_ensure_response( $this->prepare_item_for_response( $tickets, $request ) );
	}

	/**
	 * Retrieve tickets.
	 *
	 * @param int $order_id The unique id of the order.
	 * @return ?array Response object.
	 *
	 * @since 1.0
	 * @since 1.4 Event authorization restriction
	 */
	private function get_tickets( int $order_id ): ?array {
		global $wpdb;
		global $fevt_container;

		// Get all the qrcodes.
		$event_table  = $wpdb->prefix . 'fe_events';
		$qrcode_table = $wpdb->prefix . 'fe_event_qrcodes';
		$order_table  = $wpdb->prefix . 'fe_event_orders';
		// phpcs:disable
		$result       = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT $order_table.id, $order_table.uid, $order_table.name, $order_table.email, $order_table.created_at, " .
				"$event_table.event_name, $event_table.event_date, $event_table.event_date_format, $event_table.seats_needed, " .
				"$event_table.pdf_info, $qrcode_table.qrcode, $qrcode_table.ticket_type, $qrcode_table.event_id " .
				"FROM $qrcode_table " .
				"INNER JOIN $order_table ON $order_table.id = $qrcode_table.order_id " .
				"INNER JOIN $event_table ON $event_table.id = $qrcode_table.event_id " .
				"WHERE $qrcode_table.order_id=%d" .
				$this->auth->get_restricted_events( $this->auth::FAST_EVENTS_AUTH_QUERY_ORDERS, $event_table ),
				$order_id
			)
		);
		// phpcs:enable

		// Check if seats are used and add them if needed.
		foreach ( $result as $key => $value ) {
			if ( 1 === (int) $value->seats_needed ) {
				$result[ $key ]->ticket_type .= $fevt_container->get( SeatsHelper::class )->get_seat( (int) $value->event_id, $value->qrcode );
			}
		}

		return $result;
	}


	/**
	 * Create new tickets for order.
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
	 *
	 * @since 1.0
	 */
	public function create_item( $request ): WP_Error|WP_REST_Response {
		global $fevt_container;

		$result = $fevt_container->get( OrderHelper::class )->qrcode_create( absint( $request['id'] ) );
		if ( is_wp_error( $result ) ) {
			return $result;
		} else {
			$request->set_param( 'context', 'view' );
			$tickets = $this->get_tickets( absint( $request['id'] ) );

			return rest_ensure_response( $this->prepare_item_for_response( $tickets, $request ) );
		}
	}


	/**
	 * Delete all tickets from an order.
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
	 *
	 * @since 1.0
	 */
	public function delete_item( $request ): WP_Error|WP_REST_Response {
		global $fevt_container;

		$result = $fevt_container->get( OrderHelper::class )->qrcode_delete( absint( $request['id'] ) );
		if ( is_wp_error( $result ) ) {
			return $result;
		} else {
			return rest_ensure_response( array( 'deleted' => true ) );
		}
	}


	/**
	 * Retrieves the query params for the collections.
	 *
	 * @return array Query parameters for the collection.
	 *
	 * @since 2.0
	 */
	public function get_collection_params(): array {
		return array(
			'context' => $this->get_context_param( array( 'default' => 'view' ) ),
		);
	}

	/**
	 * Prepares tickets output for response.
	 *
	 * @param mixed           $item    Ticket objects.
	 * @param WP_REST_Request $request Request object.
	 *
	 * @return WP_REST_Response Response object.
	 * @since 2.4.0 Sorting: date has priority over level.
	 *
	 * @since 1.0
	 */
	public function prepare_item_for_response( $item, $request ): WP_REST_Response {
		global $wpdb;

		$data   = array();
		$fields = $this->get_fields_for_response( $request );

		if ( rest_is_field_included( 'id', $fields ) ) {
			$data['order_id'] = (int) $item[0]->id;
		}

		if ( rest_is_field_included( 'name', $fields ) ) {
			$data['name'] = $item[0]->name;
		}

		if ( rest_is_field_included( 'email', $fields ) ) {
			$data['email'] = $item[0]->email;
		}

		if ( rest_is_field_included( 'created_at', $fields ) ) {
			$data['created_at'] = $item[0]->created_at;
		}

		if ( rest_is_field_included( 'tickets', $fields ) ) {
			$item_arr = array();
			foreach ( $item as $ticket ) {
				$tmp['ticket_id']   = $ticket->qrcode;
				$tmp['event_id']    = (int) $ticket->event_id;
				$tmp['event_name']  = $ticket->event_name;
				$tmp['event_date']  = $ticket->event_date;
				$tmp['ticket_type'] = $ticket->ticket_type;

				// Include the scan info.
				$scans = $wpdb->get_results(
					$wpdb->prepare(
						"SELECT 0 as scan_level, scan_date, scan_location FROM {$wpdb->prefix}fe_event_qrcodes WHERE qrcode = %s AND scan_date IS NOT NULL " .
						'UNION ' .
						"SELECT scan_level, scan_date, scan_location FROM {$wpdb->prefix}fe_event_entries WHERE scancode = %s " .
						'ORDER BY scan_date, scan_level',
						$ticket->qrcode,
						$ticket->qrcode
					)
				);

				$scans_arr = array();
				foreach ( $scans as $scan ) {
					$tmp_scan['level']    = (int) $scan->scan_level;
					$tmp_scan['date']     = $scan->scan_date;
					$tmp_scan['location'] = $scan->scan_location;
					$scans_arr[]          = $tmp_scan;
				}
				$tmp['scans'] = $scans_arr;
				$item_arr[]   = $tmp;
			}

			$data['tickets'] = $item_arr;
		}

		$context = ! empty( $request['context'] ) ? $request['context'] : 'embed';

		$data = $this->add_additional_fields_to_object( $data, $request );
		$data = $this->filter_response_by_context( $data, $context );

		// Wrap the data in a response object.
		$response = rest_ensure_response( $data );

		if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
			$response->add_links( $this->prepare_links( $item[0] ) );
		}

		return $response;
	}


	/**
	 * Prepares links for the tickets request.
	 *
	 * @param stdClass $tickets Tickets object.
	 * @return array Links for the given tickets.
	 *
	 * @since 1.0
	 */
	protected function prepare_links( stdClass $tickets ): array {
		return array(
			'self'     => array(
				'href' => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $tickets->id ) ),
			),
			'download' => array(
				'href' => rest_url( sprintf( '%s/tickets/%s', $this->namespace, $tickets->uid ) ),
			),
		);
	}


	/**
	 * Return the tickets' schema.
	 *
	 * @return array
	 *
	 * @since 1.0
	 * @since 2.4.0 Added level 7 (Temporarily leave) and level 8 (Re-enter).
	 * @since 2.5.0 Added level 6 (Reset).
	 */
	public function get_item_schema(): array {
		if ( $this->schema ) {
			return $this->add_additional_fields_schema( $this->schema );
		}

		$this->schema = array(
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => 'tickets',
			'type'       => 'object',
			'properties' => array(
				'id'         => array(
					'description' => __( 'The unique id of the order.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'integer',
					'readonly'    => true,
				),
				'name'       => array(
					'description' => __( 'The name of the customer.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'string',
					'readonly'    => true,
				),
				'email'      => array(
					'description' => __( 'The emailaddress of the customer.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'string',
					'format'      => 'email',
					'readonly'    => true,
				),
				'created_at' => array(
					'description' => __( 'The time the order was created.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'string',
					'format'      => 'date-time',
					'readonly'    => true,
				),
				'tickets'    => array(
					'description' => __( 'Array of tickets.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'array',
					'readonly'    => true,
					'items'       => array(
						'type'       => 'object',
						'properties' => array(
							'ticket_id'   => array(
								'description' => __( 'The unique id of the ticket.', 'fast-events' ),
								'context'     => array( 'view', 'embed' ),
								'type'        => 'integer',
								'readonly'    => true,
							),
							'event_id'    => array(
								'description' => __( 'The unique id of the event.', 'fast-events' ),
								'context'     => array( 'view', 'embed' ),
								'type'        => 'integer',
								'readonly'    => true,
							),
							'event_name'  => array(
								'description' => __( 'The name of the event.', 'fast-events' ),
								'context'     => array( 'view', 'embed' ),
								'type'        => 'string',
								'readonly'    => true,
							),
							'event_date'  => array(
								'description' => __( 'The date of the event.', 'fast-events' ),
								'context'     => array( 'view', 'embed' ),
								'type'        => 'string',
								'format'      => 'date-time',
								'readonly'    => true,
							),
							'ticket_type' => array(
								'description' => __( 'The name of the ticket.', 'fast-events' ),
								'context'     => array( 'view', 'embed' ),
								'type'        => 'string',
								'readonly'    => true,
							),
							'scans'       => array(
								'description' => __( 'All scans of the ticket.', 'fast-events' ),
								'context'     => array( 'view', 'embed' ),
								'type'        => 'array',
								'items'       => array(
									'type'       => 'object',
									'properties' => array(
										'level'    => array(
											'description' => __( 'The scan level (0=Entry, 1=Staged, 6=Reset, 7=Temporarily leave, 8= Re-enter, 9=Exit).', 'fast-events' ),
											'context'     => array( 'view', 'embed' ),
											'type'        => 'integer',
											'enum'        => array( 0, 1, 6, 7, 8, 9 ),
											'readonly'    => true,
										),
										'date'     => array(
											'description' => __( 'The date of the scan.', 'fast-events' ),
											'context'     => array( 'view', 'embed' ),
											'type'        => 'string',
											'readonly'    => true,
										),
										'location' => array(
											'description' => __( 'The location of the scan.', 'fast-events' ),
											'context'     => array( 'view', 'embed' ),
											'type'        => 'string',
											'format'      => 'email',
											'readonly'    => true,
										),
									),
								),
							),
						),
					),
				),
			),
		);

		return $this->add_additional_fields_schema( $this->schema );
	}
}
