<?php
/**
 * FE Admin Coupons controller.
 *
 * @since 2.2.0
 */

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

defined( 'ABSPATH' ) || exit;

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

/**
 * FE Admin Coupons class.
 */
class CouponsController extends WP_REST_Controller {

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


	/**
	 * Register the routes for the objects of the controller.
	 *
	 * @since 2.2.0
	 */
	public function register_routes(): void {

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base,
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_items' ),
					'permission_callback' => function () {
						return $this->fast_events_admin_permissions( $this->auth::COUPON_READ );
					},
					'args'                => $this->get_collection_params(),
				),
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'create_item' ),
					'permission_callback' => function () {
						return $this->fast_events_admin_permissions( $this->auth::COUPON_CHANGE );
					},
					'args'                => $this->get_endpoint_args_for_item_schema(),
				),
				'schema' => array( $this, 'get_public_item_schema' ),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>[\d]+)',
			array(
				'args'   => array(
					'id' => array(
						'description'       => __( 'The unique id of the coupon', '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::COUPON_READ );
					},
					'args'                => array(
						'context' => $this->get_context_param( array( 'default' => 'view' ) ),
					),
				),
				array(
					'methods'             => WP_REST_Server::EDITABLE,
					'callback'            => array( $this, 'update_item' ),
					'permission_callback' => function () {
						return $this->fast_events_admin_permissions( $this->auth::COUPON_CHANGE );
					},
					'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::COUPON_CHANGE );
					},
				),
				'schema' => array( $this, 'get_public_item_schema' ),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>[\d]+)/reset-counters',
			array(
				'args'   => array(
					'id' => array(
						'description'       => __( 'The unique id of the coupon', 'fast-events' ),
						'type'              => 'integer',
						'required'          => true,
						'sanitize_callback' => 'absint',
					),
				),
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'reset_counters' ),
					'permission_callback' => function () {
						return $this->fast_events_admin_permissions( $this->auth::COUPON_CHANGE );
					},
					'args'                => array(
						'context' => $this->get_context_param( array( 'default' => 'view' ) ),
					),
				),
				'schema' => array( $this, 'get_public_item_schema' ),
			)
		);
	}


	/**
	 * Get the authorization for the Coupons endpoints.
	 *
	 * @param int $action The action on the controller.
	 * @return WP_Error|bool
	 *
	 * @since 2.2.0
	 */
	public function fast_events_admin_permissions( int $action ): WP_Error|bool {
		return $this->auth->authorize( $action );
	}


	/**
	 * Get all coupons.
	 *
	 * @param WP_REST_Request $request Request instance.
	 * @return WP_REST_Response|WP_Error|array
	 *
	 * @since 2.2.0
	 */
	public function get_items( $request ): WP_Error|WP_REST_Response|array {
		global $wpdb;

		// Look up the orders.
		$coupon_table = $wpdb->prefix . 'fe_event_coupons';

		// Include specific order ids.
		$where = 'WHERE id>0';
		if ( ! empty( $request['include'] ) ) {
			$where .= ' AND id IN (' . implode( ',', $request['include'] ) . ')';
		}

		// Exclude specific order ids.
		if ( ! empty( $request['exclude'] ) ) {
			$where .= ' AND id NOT IN (' . implode( ',', $request['exclude'] ) . ')';
		}

		// Include a possible email search clause.
		if ( ! empty( $request['search'] ) ) {
			$where .= " AND email LIKE '%%" . $request['search'] . "%%'";
		}

		// Get the sort-order.
		$order = ' ORDER BY ' . $request['orderby'] . ' ' . $request['order'];

		// Set pagination.
		$limit = 'LIMIT ' . $request['per_page'] . ' OFFSET ';
		if ( ! empty( $request['offset'] ) ) {
			$limit .= $request['offset'];
			$offset = $request['offset'];
		} else {
			$limit .= ( $request['page'] - 1 ) * $request['per_page'];
			$offset = ( $request['page'] - 1 ) * $request['per_page'];
		}

		$result = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $coupon_table $where $order $limit" ), ARRAY_A ); // phpcs:ignore
		if ( empty( $result ) ) {
			return new WP_Error( 'no_coupons_found', __( 'No coupons found', 'fast-events' ), array( 'status' => 404 ) );
		}

		$orders = array();

		foreach ( $result as $order ) {
			$data     = $this->prepare_item_for_response( $order, $request );
			$orders[] = $this->prepare_response_for_collection( $data );
		}

		$response = rest_ensure_response( $orders );

		// Generate the totals headers.
		$per_page     = (int) $request['per_page'];
		$page         = (int) ceil( ( $offset / $per_page ) + 1 );
		$total_orders = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $coupon_table $where" ) ); // phpcs:ignore

		$max_pages = ceil( (int) $total_orders / $per_page );
		$response->header( 'X-WP-Total', (int) $total_orders );
		$response->header( 'X-WP-TotalPages', ceil( (int) $max_pages ) );

		$base = add_query_arg( urlencode_deep( $request->get_query_params() ), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) );
		if ( $page > 1 ) {
			$prev_page = $page - 1;

			if ( $prev_page > $max_pages ) {
				$prev_page = $max_pages;
			}

			$prev_link = add_query_arg( 'page', $prev_page, $base );
			$response->link_header( 'prev', $prev_link );
		}
		if ( $max_pages > $page ) {
			$next_page = $page + 1;
			$next_link = add_query_arg( 'page', $next_page, $base );

			$response->link_header( 'next', $next_link );
		}

		return $response;
	}


	/**
	 * Retrieve a single coupon.
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @return WP_REST_Response|WP_Error|array Response object on success, or WP_Error object on failure.
	 *
	 * @since 2.2.0
	 */
	public function get_item( $request ): WP_Error|WP_REST_Response|array {
		global $wpdb;

		$coupon = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fe_event_coupons WHERE id = %d", $request['id'] ), ARRAY_A );
		if ( empty( $coupon ) ) {
			return new WP_Error( 'not_found', __( 'Coupon not found', 'fast-events' ), array( 'status' => 404 ) );
		}

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


	/**
	 * Create a new coupon.
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @return WP_REST_Response|WP_Error|bool|array Response object on success, or WP_Error object on failure.
	 *
	 * @since 2.2.0
	 */
	public function create_item( $request ): WP_Error|WP_REST_Response|bool|array {
		global $wpdb;

		$coupon = $this->prepare_item_for_database( $request );
		if ( is_wp_error( $coupon ) ) {
			return $coupon;
		}

		// Insert into database.
		$result = $wpdb->insert( $wpdb->prefix . 'fe_event_coupons', $coupon );
		if ( false === $result ) {
			return new WP_Error( 'failed', __( 'Insert coupon failed', 'fast-events' ) . ': ' . $wpdb->last_error, array( 'status' => 400 ) );
		}

		$coupon = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fe_event_coupons WHERE id = %d", $wpdb->insert_id ), ARRAY_A );
		if ( empty( $coupon ) ) {
			return new WP_Error( 'not_found', __( 'Coupon not found', 'fast-events' ), array( 'status' => 404 ) );
		}

		$request->set_param( 'context', 'view' );

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


	/**
	 * Update a single coupon.
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @return WP_REST_Response|WP_Error|bool|array Response object on success, or WP_Error object on failure.
	 *
	 * @since 2.2.0
	 */
	public function update_item( $request ): WP_Error|WP_REST_Response|bool|array {
		global $wpdb;

		$coupon = $this->prepare_item_for_database( $request );
		if ( is_wp_error( $coupon ) ) {
			return $coupon;
		}

		// Update event.
		$result = $wpdb->update( $wpdb->prefix . 'fe_event_coupons', $coupon, array( 'id' => $request['id'] ) );
		if ( false === $result ) {
			return new WP_Error( 'failed', __( 'Update coupon failed', 'fast-events' ) . ': ' . $wpdb->last_error, array( 'status' => 400 ) );
		}
		$coupon = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fe_event_coupons WHERE id = %d", $request['id'] ), ARRAY_A );
		if ( empty( $coupon ) ) {
			return new WP_Error( 'not_found', __( 'Coupon not found', 'fast-events' ), array( 'status' => 404 ) );
		}

		$request->set_param( 'context', 'view' );

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


	/**
	 * Reset counters of a single coupon.
	 *
	 * @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 2.2.0
	 */
	public function reset_counters( WP_REST_Request $request ): WP_Error|WP_REST_Response {
		global $wpdb;

		$result = $wpdb->update(
			$wpdb->prefix . 'fe_event_coupons',
			array(
				'used' => 0,
			),
			array(
				'id' => $request['id'],
			)
		);
		if ( false === $result ) {
			return new WP_Error( 'counter_reset_failed', esc_html__( 'Update coupon counters failed', 'fast-events' ) . ': ' . $wpdb->last_error, array( 'status' => 400 ) );
		}
		$coupon = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fe_event_coupons WHERE id = %d", $request['id'] ), ARRAY_A );
		if ( empty( $coupon ) ) {
			return new WP_Error( 'not_found', __( 'Coupon not found', 'fast-events' ), array( 'status' => 404 ) );
		}

		$request->set_param( 'context', 'view' );

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


	/**
	 * Delete a single coupon.
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @return WP_REST_Response|WP_Error|bool|array Response object on success, or WP_Error object on failure.
	 *
	 * @since 2.2.0
	 */
	public function delete_item( $request ): WP_Error|WP_REST_Response|bool|array {
		global $wpdb;

		$old_coupon = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fe_event_coupons WHERE id = %d", $request['id'] ), ARRAY_A );
		if ( empty( $old_coupon ) ) {
			return new WP_Error( 'not_found', __( 'Coupon not found', 'fast-events' ), array( 'status' => 404 ) );
		}

		$coupon = $wpdb->delete( $wpdb->prefix . 'fe_event_coupons', array( 'id' => $request['id'] ) );

		if ( false === $coupon || 0 === $coupon ) {
			return new WP_Error( 'not_found', __( 'Coupon not found', 'fast-events' ), array( 'status' => 404 ) );
		}

		$request->set_param( 'context', 'view' );

		$previous = $this->prepare_item_for_response( $old_coupon, $request );

		$response = new WP_REST_Response();
		$response->set_data(
			array(
				'deleted'  => true,
				'previous' => $previous->get_data(),
			)
		);

		return $response;
	}


	/**
	 * Prepares the object for the database.
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return WP_Error|array Response object.
	 *
	 * @since 2.2.0
	 */
	public function prepare_item_for_database( $request ): WP_Error|array {
		global $wpdb;

		// Is this an update or create?
		$create = str_ends_with( $request->get_route(), 'coupons' );

		// Check for empty fields.
		if ( $create && ( empty( $request['code'] ) || empty( $request['description'] ) || empty( $request['amount'] ) || empty( $request['start_date'] ) || empty( $request['end_date'] ) ) ) {
			return new WP_Error( 'failed', __( 'Empty fields are not allowed', 'fast-events' ), array( 'status' => 400 ) );
		}

		$coupon = array();
		if ( isset( $request['code'] ) ) {
			if ( '' === trim( $request['code'] ) ) {
				return new WP_Error( 'empty_field', __( 'Empty "code" field is not allowed', 'fast-events' ), array( 'status' => 400 ) );
			} else {
				$coupon['code'] = $request['code'];
			}
		}

		if ( isset( $request['description'] ) ) {
			if ( '' === trim( $request['description'] ) ) {
				return new WP_Error( 'empty_field', __( 'Empty "description" field is not allowed', 'fast-events' ), array( 'status' => 400 ) );
			} else {
				$coupon['description'] = $request['description'];
			}
		}

		if ( $create && isset( $request['date_created'] ) ) {
			$coupon['date_created'] = $request['date_created'];
		}

		if ( isset( $request['date_modified'] ) ) {
			$coupon['date_modified'] = $request['date_modified'];
		}

		if ( isset( $request['type'] ) ) {
			$coupon['type'] = $request['type'];
		}

		if ( isset( $request['amount'] ) ) {
			if ( 0.00 === $request['amount'] ) {
				return new WP_Error( 'empty_field', __( 'The percentage or fixed amount should not be zero', 'fast-events' ), array( 'status' => 400 ) );
			} else {
				$coupon['amount'] = $request['amount'];
			}
		}

		if ( isset( $request['usage_limit'] ) ) {
			$coupon['usage_limit'] = $request['usage_limit'];
		}

		if ( isset( $request['usage_limit_per_user'] ) ) {
			$coupon['usage_limit_per_user'] = $request['usage_limit_per_user'];
		}

		if ( isset( $request['start_date'] ) ) {
			$coupon['start_date'] = $request['start_date'];
		}

		if ( isset( $request['end_date'] ) ) {
			$coupon['end_date'] = $request['end_date'];
		}

		$events       = array();
		$events_given = false;
		if ( ! empty( $request['events'] ) ) {
			$events = array_map( 'absint', explode( ',', $request['events'] ) );
			$count  = absint( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}fe_events WHERE id IN(" . implode( ',', $events ) . ')' ) ); // phpcs:ignore
			if ( count( $events ) !== $count ) {
				return new WP_Error( 'no_event', __( 'One or more event ids do not exist.', 'fast-events' ), array( 'status' => 400 ) );
			}
			$coupon['events'] = implode( ',', $events );
			$events_given     = true;
		} elseif ( isset( $request['events'] ) && '' === $request['events'] ) {
			$coupon['events'] = null;
			$events_given     = true;
		}

		if ( ! empty( $request['tickets'] ) ) {
			$tickets = explode( ',', $request['tickets'] );
			$result  = $this->check_ticket_names( $request, $tickets, $events, $events_given );
			if ( is_wp_error( $result ) ) {
				return $result;
			}
			$coupon['tickets'] = implode( ',', $tickets );
		} elseif ( isset( $request['tickets'] ) && '' === $request['tickets'] ) {
			$coupon['tickets'] = null;
		}

		if ( isset( $request['minimum_tickets'] ) ) {
			$coupon['minimum_tickets'] = 0.0 === (float) $request['minimum_tickets'] ? null : $request['minimum_tickets'];
		}

		if ( isset( $request['maximum_tickets'] ) ) {
			$coupon['maximum_tickets'] = 0.0 === (float) $request['maximum_tickets'] ? null : $request['maximum_tickets'];
		}

		if ( isset( $request['minimum_amount'] ) ) {
			$coupon['minimum_amount'] = 0.0 === (float) $request['minimum_amount'] ? null : $request['minimum_amount'];
		}

		if ( isset( $request['maximum_amount'] ) ) {
			$coupon['maximum_amount'] = 0.0 === (float) $request['maximum_amount'] ? null : $request['maximum_amount'];
		}

		if ( ! empty( $request['email'] ) ) {
			if ( ! is_email( sanitize_email( $request['email'] ) ) ) {
				return new WP_Error( 'invalid_email', __( 'Invalid email address', 'fast-events' ), array( 'status' => 400 ) );
			}
			$coupon['email'] = sanitize_email( $request['email'] );
		} else {
			$coupon['email'] = null;
		}

		if ( ! $create && 0 === count( $coupon ) ) {
			return new WP_Error( 'no_fields_found', __( 'Nothing to update', 'fast-events' ), array( 'status' => 400 ) );
		} elseif ( ! $create && ! isset( $request['date_modified'] ) ) {
			$coupon['date_modified'] = current_time( 'mysql' );
		}

		return $coupon;
	}


	/**
	 * Check if tickets exist in the active events.
	 *
	 * @param WP_REST_Request $request Request object.
	 * @param array           $tickets Array of ticket names.
	 * @param array           $events Array of event ids.
	 * @param bool            $events_given If the 3rd parameter needs to be used.
	 * @return bool|WP_Error.
	 *
	 * @since 2.2.0
	 */
	private function check_ticket_names( WP_REST_Request $request, array $tickets, array $events, bool $events_given ): bool|WP_Error {
		global $wpdb;

		$search = $tickets;
		if ( ! $events_given ) {
			$coupon = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fe_event_coupons WHERE id = %d", $request['id'] ), ARRAY_A );
			if ( empty( $coupon ) ) {
				return new WP_Error( 'not_found', __( 'Coupon not found', 'fast-events' ), array( 'status' => 404 ) );
			}
			if ( ! is_null( $coupon['events'] ) ) {
				$events = explode( ',', $coupon['events'] );
			}
		}

		$where = '';
		if ( ! empty( $events ) ) {
			$where = ' WHERE id IN(' . implode( ',', $events ) . ')';
		}

		// Walk though all input_fields.
		$results = $wpdb->get_results( 'SELECT input_fields FROM ' . $wpdb->prefix . 'fe_events' . $where ); // phpcs:ignore
		if ( ! empty( $results ) ) {
			foreach ( $results as $result ) {
				$inp = json_decode( $result->input_fields );
				foreach ( $inp->tickets as $ticket ) {
					$key = array_search( $ticket->name, $search, true );
					if ( false !== $key ) {
						unset( $search[ $key ] );
						if ( empty( $search ) ) {
							return true;
						}
					}
				}
			}
		}

		return new WP_Error( 'no_ticket', __( 'One or more ticket names do not exist.', 'fast-events' ), array( 'status' => 400 ) );
	}


	/**
	 * Prepares a single coupon output for response.
	 *
	 * @param mixed           $item    Coupon object.
	 * @param WP_REST_Request $request Request object.
	 * @return WP_REST_Response Response object.
	 *
	 * @since 2.2.0
	 */
	public function prepare_item_for_response( $item, $request ): WP_REST_Response {

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

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

		if ( rest_is_field_included( 'code', $fields ) ) {
			$data['code'] = $item['code'];
		}

		if ( rest_is_field_included( 'description', $fields ) ) {
			$data['description'] = $item['description'];
		}

		if ( rest_is_field_included( 'date_created', $fields ) ) {
			$data['date_created'] = $item['date_created'];
		}

		if ( rest_is_field_included( 'date_modified', $fields ) ) {
			$data['date_modified'] = $item['date_modified'];
		}

		if ( rest_is_field_included( 'type', $fields ) ) {
			$data['type'] = $item['type'];
		}

		if ( rest_is_field_included( 'amount', $fields ) ) {
			$data['amount'] = (float) $item['amount'];
		}

		if ( rest_is_field_included( 'usage_limit', $fields ) ) {
			$data['usage_limit'] = (int) $item['usage_limit'];
		}

		if ( rest_is_field_included( 'usage_limit_per_user', $fields ) ) {
			$data['usage_limit_per_user'] = (int) $item['usage_limit_per_user'];
		}

		if ( rest_is_field_included( 'used', $fields ) ) {
			$data['used'] = (int) $item['used'];
		}

		if ( rest_is_field_included( 'last_used', $fields ) ) {
			$data['last_used'] = $item['last_used'] ?? '';
		}

		if ( rest_is_field_included( 'start_date', $fields ) ) {
			$data['start_date'] = $item['start_date'];
		}

		if ( rest_is_field_included( 'end_date', $fields ) ) {
			$data['end_date'] = $item['end_date'];
		}

		if ( rest_is_field_included( 'events', $fields ) ) {
			$data['events'] = $item['events'] ?? '';
		}

		if ( rest_is_field_included( 'tickets', $fields ) ) {
			$data['tickets'] = $item['tickets'] ?? '';
		}

		if ( rest_is_field_included( 'minimum_tickets', $fields ) ) {
			$data['minimum_tickets'] = (int) $item['minimum_tickets'] ?? '';
		}

		if ( rest_is_field_included( 'maximum_tickets', $fields ) ) {
			$data['maximum_tickets'] = (int) $item['maximum_tickets'] ?? '';
		}

		if ( rest_is_field_included( 'minimum_amount', $fields ) ) {
			$data['minimum_amount'] = (float) $item['minimum_amount'] ?? '';
		}

		if ( rest_is_field_included( 'maximum_amount', $fields ) ) {
			$data['maximum_amount'] = (float) $item['maximum_amount'] ?? '';
		}

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

		$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 ) );
		}

		return $response;
	}


	/**
	 * Prepares links for the coupon request.
	 *
	 * @param array $field Coupon object.
	 * @return array Links for the given order.
	 *
	 * @since 2.2.0
	 */
	protected function prepare_links( array $field ): array {
		return array(
			'self'       => array(
				'href' => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $field['id'] ) ),
			),
			'collection' => array(
				'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
			),
		);
	}


	/**
	 * Retrieves the query params for order collections.
	 *
	 * @return array
	 *
	 * @since 1.0
	 */
	public function get_collection_params(): array {
		$query_params = parent::get_collection_params();

		$query_params['context']['default'] = 'view';

		$query_params['exclude'] = array(
			'description' => __( 'Ensure result set excludes specific IDs.' ),
			'type'        => 'array',
			'items'       => array(
				'type' => 'integer',
			),
			'default'     => array(),
		);

		$query_params['include'] = array(
			'description' => __( 'Limit result set to specific IDs.' ),
			'type'        => 'array',
			'items'       => array(
				'type' => 'integer',
			),
			'default'     => array(),
		);

		$query_params['offset'] = array(
			'description' => __( 'Offset the result set by a specific number of items.' ),
			'type'        => 'integer',
		);

		$query_params['order'] = array(
			'default'     => 'asc',
			'description' => __( 'Order sort attribute ascending or descending.' ),
			'enum'        => array( 'asc', 'desc' ),
			'type'        => 'string',
		);

		$query_params['orderby'] = array(
			'default'     => 'id',
			'description' => __( 'Sort collection by object attribute.' ),
			'enum'        => array(
				'id',
				'code',
				'email',
			),
			'type'        => 'string',
		);

		return $query_params;
	}


	/**
	 * Return the coupons' schema.
	 *
	 * @return array
	 *
	 * @since 2.2.0
	 */
	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'      => 'coupons',
			'type'       => 'object',
			'properties' => array(
				'id'                   => array(
					'description' => __( 'The unique id of the coupon.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'string',
					'readonly'    => true,
				),
				'code'                 => array(
					'description' => __( 'The coupon code.', 'fast-events' ),
					'context'     => array( 'view', 'edit', 'embed' ),
					'type'        => 'string',
					'minLength'   => 1,
					'maxLength'   => 32,
					'required'    => true,
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'description'          => array(
					'description' => __( 'The description of the coupon.', 'fast-events' ),
					'context'     => array( 'view', 'edit', 'embed' ),
					'type'        => 'string',
					'minLength'   => 1,
					'maxLength'   => 100,
					'required'    => true,
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'date_created'         => array(
					'description' => __( 'The date when the coupon was created.' ),
					'type'        => array( 'string', 'null' ),
					'format'      => 'date-time',
					'context'     => array( 'view', 'embed' ),
					'readonly'    => true,
				),
				'date_modified'        => array(
					'description' => __( 'The date when the coupon was last changed.' ),
					'type'        => array( 'string', 'null' ),
					'format'      => 'date-time',
					'context'     => array( 'view', 'edit', 'embed' ),
				),
				'type'                 => array(
					'description' => __( 'The type (percent or fixed) of coupon.', 'fast-events' ),
					'context'     => array( 'view', 'edit', 'embed' ),
					'type'        => 'string',
					'enum'        => array(
						'percent',
						'fixed',
					),
					'default'     => 'fixed',
				),
				'amount'               => array(
					'description' => __( 'The percentage or fixed amount of the coupon.', 'fast-events' ),
					'context'     => array( 'view', 'edit', 'embed' ),
					'type'        => 'number',
					'required'    => true,
				),
				'usage_limit'          => array(
					'description' => __( 'Limit for coupon usage.', 'fast-events' ),
					'context'     => array( 'view', 'edit', 'embed' ),
					'type'        => 'integer',
					'required'    => true,
					'default'     => 1,
					'arg_options' => array(
						'sanitize_callback' => 'absint',
					),
				),
				'usage_limit_per_user' => array(
					'description' => __( 'Limit for coupon usage per user.', 'fast-events' ),
					'context'     => array( 'view', 'edit', 'embed' ),
					'type'        => 'integer',
					'required'    => true,
					'default'     => 1,
					'arg_options' => array(
						'sanitize_callback' => 'absint',
					),
				),
				'used'                 => array(
					'description' => __( 'How often was the coupon used.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'integer',
					'readonly'    => true,
				),
				'last_used'            => array(
					'description' => __( 'The date the coupon was last used.' ),
					'type'        => array( 'string', 'null' ),
					'format'      => 'date-time',
					'context'     => array( 'view', 'embed' ),
				),
				'start_date'           => array(
					'description' => __( 'The date from when the coupon can be used.' ),
					'type'        => array( 'string', 'null' ),
					'format'      => 'date-time',
					'context'     => array( 'view', 'edit', 'embed' ),
					'required'    => true,
				),
				'end_date'             => array(
					'description' => __( 'The date up to when the coupon can be used.' ),
					'type'        => array( 'string', 'null' ),
					'format'      => 'date-time',
					'context'     => array( 'view', 'edit', 'embed' ),
					'required'    => true,
				),
				'events'               => array(
					'description' => __( 'A comma separated list of event ids.', 'fast-events' ),
					'context'     => array( 'view', 'edit', 'embed' ),
					'type'        => 'string',
					'maxLength'   => 64,
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'tickets'              => array(
					'description' => __( 'A comma separated list of ticket names.', 'fast-events' ),
					'context'     => array( 'view', 'edit', 'embed' ),
					'type'        => 'string',
					'maxLength'   => 256,
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'minimum_tickets'      => array(
					'description' => __( 'The minimum number of tickets required for the coupon.', 'fast-events' ),
					'context'     => array( 'view', 'edit', 'embed' ),
					'type'        => 'integer',
					'arg_options' => array(
						'sanitize_callback' => 'absint',
					),
				),
				'maximum_tickets'      => array(
					'description' => __( 'The maximum number of tickets allowed for the coupon.', 'fast-events' ),
					'context'     => array( 'view', 'edit', 'embed' ),
					'type'        => 'integer',
					'arg_options' => array(
						'sanitize_callback' => 'absint',
					),
				),
				'minimum_amount'       => array(
					'description' => __( 'The minimum amount required for the coupon.', 'fast-events' ),
					'context'     => array( 'view', 'edit', 'embed' ),
					'type'        => 'number',
				),
				'maximum_amount'       => array(
					'description' => __( 'The maximum amount allowed for the coupon.', 'fast-events' ),
					'context'     => array( 'view', 'edit', 'embed' ),
					'type'        => 'number',
				),
				'email'                => array(
					'description' => __( 'The email address that can use this coupon.', 'fast-events' ),
					'context'     => array( 'view', 'edit', 'embed' ),
					'type'        => 'string',
				),
			),
		);

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