<?php
/**
 * Fast Events Events Admin controller.
 *
 * @since 1.3
 */

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

defined( 'ABSPATH' ) || exit;

use FeData\FastEvents\Helpers\AuthHelper;
use FeData\FastEvents\Helpers\EventsHelper;
use FeData\FastEvents\Mail\MailerFactory;
use FeData\FastEvents\Storage\Options;
use FeData\FastEvents\Utilities\CheckHtml;
use FeData\FastEvents\Utilities\JsonUtil;
use stdClass;
use Throwable;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Fast Events events admin class.
 */
class EventsAdminController {

	private const NAMESPACE    = 'fast-events/v1';
	private const EVENTS_ROUTE = '/admin/events/';

	/**
	 * Fast Events admin constructor.
	 *
	 * @param AuthHelper $auth Auth helper.
	 *
	 * @since 1.3
	 */
	public function __construct( private readonly AuthHelper $auth ) {}


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

		// Get all events.
		register_rest_route(
			self::NAMESPACE,
			self::EVENTS_ROUTE . 'all',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'all_events' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::EVENT_READ );
				},
			)
		);

		// Get ticket types names per event.
		register_rest_route(
			self::NAMESPACE,
			self::EVENTS_ROUTE . 'all_ticket_names',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'all_ticket_names' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::EVENT_READ );
				},
			)
		);

		// Get a single event.
		register_rest_route(
			self::NAMESPACE,
			self::EVENTS_ROUTE . 'get',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'get_event' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::EVENT_READ );
				},
				'args'                => array(
					'id' => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
				),
			)
		);

		// Change stock.
		register_rest_route(
			self::NAMESPACE,
			self::EVENTS_ROUTE . 'savestock',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'change_stock' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::EVENT_UPDATE );
				},
				'args'                => array(
					'id'         => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
					'stock'      => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
					'stock_link' => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
				),
			)
		);

		// Sync events with each-other.
		register_rest_route(
			self::NAMESPACE,
			self::EVENTS_ROUTE . 'sync',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'sync_events' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::EVENT_SYNC );
				},
				'args'                => array(
					'source_id'            => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
					'target_ids'           => array(
						'required'          => true,
						'type'              => 'string',
						'sanitize_callback' => function ( $value ) {
							return preg_replace( '/\s+/', '', $value );
						},
						'validate_callback' => function ( $value ) {
							$pattern = '/^\d+(,\d+)*$/';
							return (bool) preg_match( $pattern, $value );
						},
					),
					'dates_sync'           => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
						'enum'              => array( 0, 1 ),
					),
					'flags_sync'           => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
						'enum'              => array( 0, 1 ),
					),
					'email_sync'           => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
						'enum'              => array( 0, 1 ),
					),
					'input_fields_sync'    => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
						'enum'              => array( 0, 1 ),
					),
					'ticket_types_sync'    => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
						'enum'              => array( 0, 1 ),
					),
					'ticket_template_sync' => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
						'enum'              => array( 0, 1 ),
					),
					'scan_keys_sync'       => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
						'enum'              => array( 0, 1 ),
					),
					'seats_sync'           => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
						'enum'              => array( 0, 1 ),
					),
					'tracking_sync'        => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
						'enum'              => array( 0, 1 ),
					),
					'saas_sync'            => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
						'enum'              => array( 0, 1 ),
					),
				),
			)
		);

		// Send an example email (normal email or confirmation email).
		register_rest_route(
			self::NAMESPACE,
			self::EVENTS_ROUTE . 'email_template_example',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'email_template_example' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::EMAIL_TEMPLATE_EXAMPLE );
				},
				'args'                => array(
					'id'            => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
					'email'         => array(
						'required' => true,
						'type'     => 'string',
						'format'   => 'email',
					),
					'sender_name'   => array(
						'type'              => 'string',
						'sanitize_callback' => 'sanitize_text_field',
					),
					'sender_email'  => array(
						'type' => array( 'string', 'null' ),
					),
					'email_subject' => array(
						'required'          => true,
						'type'              => 'string',
						'sanitize_callback' => 'sanitize_text_field',
					),
					'email_body'    => array(
						'required' => true,
						'type'     => 'string',
					),
				),
			)
		);

		// Duplicate event.
		register_rest_route(
			self::NAMESPACE,
			self::EVENTS_ROUTE . 'duplicate',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'event_duplicate' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::EVENT_ADD );
				},
				'args'                => array(
					'id' => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
				),
			)
		);

		// Delete all orders from an event.
		register_rest_route(
			self::NAMESPACE,
			self::EVENTS_ROUTE . 'delete_orders',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'event_orders_delete' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::EVENT_DELETE_ORDERS );
				},
				'args'                => array(
					'id' => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
				),
			)
		);

		// Get a list of WP Roles.
		register_rest_route(
			self::NAMESPACE,
			self::EVENTS_ROUTE . 'get_wp_roles',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'wp_roles_callback' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::EVENT_READ );
				},
			)
		);

		// Get a list of users with the 'fe_account' role.
		register_rest_route(
			self::NAMESPACE,
			self::EVENTS_ROUTE . 'get_fe_account_users',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'fe_users' ),
				'permission_callback' => function () {
					return is_user_logged_in() && in_array( 'administrator', wp_get_current_user()->roles, true );
				},
			)
		);

		// Get media and pages.
		register_rest_route(
			self::NAMESPACE,
			self::EVENTS_ROUTE . 'posts',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_posts' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::EVENT_TRACKING_UPLOAD );
				},
			)
		);
	}


	/**
	 * Get all events (except the direct payment one)
	 *
	 * @since 1.0
	 * @since 1.4 Event authorization restriction
	 * @since 1.6 Added 'input_fields' and 'pdf_info' to the query
	 * @since 1.8.4 Don't include event_type 0
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function all_events(): WP_Error|WP_REST_Response {
		global $wpdb;

		// Get all events the user is authorized for.
		$auth_query = $this->auth->get_restricted_events( $this->auth::FAST_EVENTS_AUTH_QUERY_EVENTS );

		$results = $wpdb->get_results(
			$wpdb->prepare(
				'SELECT id, start_date, end_date, event_name, event_date, event_date_format, ' .
				"stock, sold, event_group, group_type, input_fields, pdf_info FROM {$wpdb->prefix}fe_events " .
				( empty( $auth_query ) ? 'WHERE event_type != 0' : ( $auth_query . ' AND event_type != 0' ) ) . ' ORDER BY id DESC' //phpcs:ignore
			)
		);

		// Set the date format.
		foreach ( $results as $index => $value ) {
			$results[ $index ]->event_date = mysql2date( $value->event_date_format, $value->event_date );
		}

		return rest_ensure_response( $results );
	}


	/**
	 * Get all ticket names
	 *
	 * @since 2.2.0
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function all_ticket_names(): WP_Error|WP_REST_Response {
		global $wpdb;

		// Get all events the user is authorized for.
		$auth_query = $this->auth->get_restricted_events( $this->auth::FAST_EVENTS_AUTH_QUERY_EVENTS );

		$events = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT id, input_fields FROM {$wpdb->prefix}fe_events " .
				( empty( $auth_query ) ? 'WHERE event_type != 0' : ( $auth_query . ' AND event_type != 0' ) ) . ' ORDER BY id DESC' //phpcs:ignore
			)
		);

		$results = array();
		foreach ( $events as $event ) {
			$inp     = JsonUtil::get_input_fields( $event->input_fields );
			$tickets = array();
			foreach ( $inp->tickets as $ticket ) {
				$tickets[] = $ticket->name;
			}
			$data            = array();
			$data['id']      = $event->id;
			$data['tickets'] = $tickets;
			$results[]       = $data;
		}
		return rest_ensure_response( $results );
	}

	/**
	 * Get a single event
	 *
	 * @since 1.0
	 * @since 1.4   Event authorization restriction
	 * @since 1.8.4 Don't include event_type 0
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function get_event( WP_REST_Request $data ): WP_Error|WP_REST_Response {
		global $wpdb;

		// Check the event authorization.
		$resp = $this->auth->check_event_authorization( absint( $data['id'] ) );
		if ( is_wp_error( $resp ) ) {
			return $resp;
		}

		// Look up the event.
		$event = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fe_events WHERE id = %d AND event_type != 0", $data['id'] ) );

		if ( empty( $event ) ) {
			return new WP_Error( 'not_found', __( 'Event not found', 'fast-events' ), array( 'status' => 404 ) );
		}

		return rest_ensure_response( $event );
	}


	/**
	 * Change the stock of an event
	 *
	 * @since 1.0
	 * @since 1.4   Event authorization restriction
	 * @since 1.5   ROLLBACK after update error
	 * @since 1.10  Added 'stock_link'
	 * @since 2.2.1 An event cannot refer to itself as a linked-event
	 * @since 2.3.0 Use fast_events_json_encode.
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function change_stock( WP_REST_Request $data ): WP_Error|WP_REST_Response {
		global $wpdb;

		// Check the event authorization.
		$resp = $this->auth->check_event_authorization( absint( $data['id'] ) );
		if ( is_wp_error( $resp ) ) {
			return $resp;
		}

		// Start the transaction.
		$wpdb->query( 'START TRANSACTION' );

		// Look up the event.
		$event = $wpdb->get_row( $wpdb->prepare( "SELECT sold, input_fields FROM {$wpdb->prefix}fe_events WHERE id = %d FOR UPDATE", $data['id'] ) );
		if ( empty( $event ) ) {
			$wpdb->query( 'ROLLBACK' );
			return new WP_Error( 'not_found', __( 'Event not found', 'fast-events' ), array( 'status' => 404 ) );
		}

		// Check stock_link.
		if ( 0 !== $data['stock_link'] ) {
			$cnt = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}fe_events WHERE id = %d", $data['stock_link'] ) );
			if ( 0 === (int) $cnt ) {
				return new WP_Error( 'not_found', __( 'Event for stock_link parameter not found', 'fast-events' ), array( 'status' => 404 ) );
			}
			if ( $data['stock_link'] === $data['id'] ) {
				return new WP_Error( 'invalid_event', __( 'An event cannot refer to itself as a linked-event', 'fast-events' ), array( 'status' => 400 ) );
			}
		}

		// Update ticket stock.
		$fields = json_decode( $event->input_fields );
		if ( ! empty( $fields->tickets ) ) {
			foreach ( $fields->tickets as $key => $value ) {
				if ( ! isset( $data[ $value->id ] ) ) {
					$wpdb->query( 'ROLLBACK' );
					/* translators: %s is the name of the ticket-type */
					return new WP_Error( 'failed', sprintf( __( 'No value for ticket-type: %s', 'fast-events' ), $value->name ), array( 'status' => 412 ) );
				}
				if ( '' === sanitize_text_field( $data[ $value->id ] ) ) {
					$fields->tickets[ $key ]->stock = '';
				} else {
					$fields->tickets[ $key ]->stock = absint( $data[ $value->id ] );
				}
			}
		}

		// Change the stock.
		$result = $wpdb->update(
			$wpdb->prefix . 'fe_events',
			array(
				'stock'        => absint( $data['stock'] ),
				'stock_link'   => absint( $data['stock_link'] ),
				'input_fields' => JsonUtil::fast_events_json_encode( $fields ),
			),
			array( 'id' => $data['id'] )
		);

		// Was there an error?
		if ( false === $result ) {
			$wpdb->query( 'ROLLBACK' );
			return new WP_Error( 'failed', __( 'Update event failed', 'fast-events' ), array( 'status' => 409 ) );
		}

		$wpdb->query( 'COMMIT' );

		return rest_ensure_response( array( 'result' => true ) );
	}


	/**
	 * Sync the event with the target events.
	 *
	 * @since 1.6
	 * @since 2.3.0 Use fast_events_json_encode.
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_REST_Response|WP_Error|array
	 */
	public function sync_events( WP_REST_Request $data ): WP_Error|WP_REST_Response|array {
		global $wpdb;

		// Check the event authorization for the 'source_id' and 'target_ids'.
		$source_id  = absint( $data['source_id'] );
		$target_ids = array_map( 'intval', explode( ',', $data['target_ids'] ) );
		$resp       = $this->auth->check_event_authorization( $source_id );
		if ( is_wp_error( $resp ) ) {
			return $resp;
		}
		foreach ( $target_ids as $target_id ) {
			$resp = $this->auth->check_event_authorization( $target_id );
			if ( is_wp_error( $resp ) ) {
				return $resp;
			}
		}

		// Get the source event.
		$source = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fe_events WHERE id = %d", $source_id ) );
		if ( empty( $source ) ) {
			return new WP_Error( 'not_found', __( 'Event not found', 'fast-events' ), array( 'status' => 404 ) );
		}

		// Get the source seats row.
		$source_seats = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fe_event_seats WHERE event_id = %d", $source_id ) );
		if ( empty( $source_seats ) ) {
			return new WP_Error( 'not_found', __( 'Seats entry not found', 'fast-events' ), array( 'status' => 404 ) );
		}

		foreach ( $target_ids as $target_id ) {
			// Start the transaction.
			$wpdb->query( 'START TRANSACTION' );

			// Get the target event.
			$target = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fe_events WHERE id = %d FOR UPDATE", $target_id ) );
			if ( empty( $target ) ) {
				return new WP_Error( 'not_found', __( 'Event not found', 'fast-events' ), array( 'status' => 404 ) );
			}
			$s_inp = JsonUtil::get_input_fields( $source->input_fields );
			$t_inp = JsonUtil::get_input_fields( $target->input_fields );

			// Sync dates?
			if ( 1 === absint( $data['dates_sync'] ) ) {
				$target->start_date = $source->start_date;
				$target->end_date   = $source->end_date;
				$target->event_date = $source->event_date;
			}

			// Sync flags?
			if ( 1 === absint( $data['flags_sync'] ) ) {
				$target->emails_needed               = $source->emails_needed;
				$target->tickets_needed              = $source->tickets_needed;
				$target->invoice_needed              = $source->invoice_needed;
				$target->unique_users_needed         = $source->unique_users_needed;
				$target->user_groups_needed          = $source->user_groups_needed;
				$target->recaptcha_needed            = $source->recaptcha_needed;
				$target->confirmation_emails_needed  = $source->confirmation_emails_needed;
				$target->add_dashboard_orders_needed = $source->add_dashboard_orders_needed;
				$target->test_payments_needed        = $source->test_payments_needed;
				$target->seats_needed                = $source->seats_needed;
				$target->webhooks_needed             = $source->webhooks_needed;
				$target->reload_on_exit_needed       = $source->reload_on_exit_needed;
				$target->tracking_needed             = $source->tracking_needed;
				$target->coupons_needed              = $source->coupons_needed;
			}

			// Sync email?
			if ( 1 === absint( $data['email_sync'] ) ) {
				$target->email_body    = $source->email_body;
				$target->email_subject = $source->email_subject;
				$target->email_bcc     = $source->email_bcc;
				$target->sender_name   = $source->sender_name;
				$target->sender_email  = $source->sender_email;
			}

			// Sync input fields?
			if ( 1 === absint( $data['input_fields_sync'] ) ) {
				$t_inp->fields = $s_inp->fields;
			}

			// Sync ticket types? Preserve sold values.
			if ( 1 === absint( $data['ticket_types_sync'] ) ) {
				foreach ( $s_inp->tickets as $key => $value ) {
					$index = array_search( $value->name, array_column( $t_inp->tickets, 'name' ), true );
					if ( false === $index ) {
						continue;
					}
					$s_inp->tickets[ $key ]->sold = $t_inp->tickets[ $index ]->sold;
				}
				$t_inp->tickets = $s_inp->tickets;
			}

			// Sync PDF info?
			$old_attachment_ids = array();
			if ( 1 === absint( $data['ticket_template_sync'] ) ) {

				// Save all the old attachment ids in use.
				$old_attachment_ids = $this->get_attachment_ids();
				if ( is_wp_error( $old_attachment_ids ) ) {
					$wpdb->query( 'ROLLBACK' );

					return $old_attachment_ids;
				}

				$target->pdf_info = $source->pdf_info;
			}

			// Sync scan keys?
			if ( 1 === absint( $data['scan_keys_sync'] ) ) {
				$target->scan_keys = $source->scan_keys;
			}

			// Sync seats?
			if ( 1 === absint( $data['seats_sync'] ) ) {
				$target_seats = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fe_event_seats WHERE event_id = %d FOR UPDATE", $target_id ) );
				if ( empty( $target_seats ) ) {
					$wpdb->query( 'ROLLBACK' );

					return new WP_Error( 'not_found', __( 'Target seats row not found', 'fast-events' ), array( 'status' => 404 ) );
				}

				// Update target seats row.
				$result = $wpdb->update(
					$wpdb->prefix . 'fe_event_seats',
					array(
						'num_seats'    => $source_seats->num_seats,
						'seat_format'  => $source_seats->seat_format,
						'seat_config'  => $source_seats->seat_config,
						'seat_map'     => $source_seats->seat_map,
						'linked_event' => $source_seats->linked_event,
					),
					array(
						'event_id' => $target_id,
					)
				);
				if ( false === $result ) {
					$error = $wpdb->last_error;
					$wpdb->query( 'ROLLBACK' );

					return new WP_Error( 'failed', __( 'Seats sync failed: ', 'fast-events' ) . $error, array( 'status' => 400 ) );
				}
			}

			// Sync tracking?
			if ( 1 === absint( $data['tracking_sync'] ) ) {
				$target->tracking_info = $source->tracking_info;
			}

			// Sync SaaS?
			if ( 1 === absint( $data['saas_sync'] ) ) {
				$target->saas_fee     = $source->saas_fee;
				$target->saas_user_id = $source->saas_user_id;
			}

			// Update target event.
			$result = $wpdb->update(
				$wpdb->prefix . 'fe_events',
				array(
					'start_date'                  => $target->start_date,
					'end_date'                    => $target->end_date,
					'event_date'                  => $target->event_date,
					'email_body'                  => $target->email_body,
					'email_subject'               => $target->email_subject,
					'email_bcc'                   => $target->email_bcc,
					'sender_name'                 => $target->sender_name,
					'sender_email'                => $target->sender_email,
					'emails_needed'               => $target->emails_needed,
					'tickets_needed'              => $target->tickets_needed,
					'invoice_needed'              => $target->invoice_needed,
					'unique_users_needed'         => $target->unique_users_needed,
					'user_groups_needed'          => $target->user_groups_needed,
					'recaptcha_needed'            => $target->recaptcha_needed,
					'confirmation_emails_needed'  => $target->confirmation_emails_needed,
					'add_dashboard_orders_needed' => $target->add_dashboard_orders_needed,
					'test_payments_needed'        => $target->test_payments_needed,
					'seats_needed'                => $target->seats_needed,
					'webhooks_needed'             => $target->webhooks_needed,
					'reload_on_exit_needed'       => $target->reload_on_exit_needed,
					'tracking_needed'             => $target->tracking_needed,
					'coupons_needed'              => $target->coupons_needed,
					'input_fields'                => JsonUtil::fast_events_json_encode( $t_inp ),
					'pdf_info'                    => $target->pdf_info,
					'scan_keys'                   => $target->scan_keys,
					'tracking_info'               => $target->tracking_info,
					'saas_fee'                    => $target->saas_fee,
					'saas_user_id'                => $target->saas_user_id,
				),
				array(
					'id' => $target_id,
				)
			);

			// Analyse the update.
			if ( false === $result ) {
				$error = $wpdb->last_error;
				$wpdb->query( 'ROLLBACK' );

				return new WP_Error( 'failed', __( 'Event sync failed: ', 'fast-events' ) . $error, array( 'status' => 400 ) );
			}

			$wpdb->query( 'COMMIT' );

			// Save all the new attachment ids in use if PDF info was synced.
			if ( 1 === absint( $data['ticket_template_sync'] ) ) {
				$new_attachment_ids = $this->get_attachment_ids();
				if ( is_wp_error( $new_attachment_ids ) ) {
					return $new_attachment_ids;
				}

				// Delete old attachments that are not used any more.
				foreach ( array_diff( $old_attachment_ids, $new_attachment_ids ) as $value ) {
					wp_delete_attachment( $value, true );
				}
			}
		} // End foreach loop.

		return rest_ensure_response( array( 'result' => true ) );
	}


	/**
	 * Send an example email (normal email or confirmation email depending on type)
	 *
	 * @since 1.10
	 * @since 2.1.0 - Added redirect to query
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function email_template_example( WP_REST_Request $data ): WP_Error|WP_REST_Response {
		global $fevt_container;

		// Check the event authorization for the 'id'.
		$resp = $this->auth->check_event_authorization( absint( $data['id'] ) );
		if ( is_wp_error( $resp ) ) {
			return $resp;
		}

		// Boot the HTML Purifier.
		$purifier = CheckHtml::purify();

		$result                 = new stdClass();
		$result->id             = '0';
		$result->event_id       = '0';
		$result->uid            = 'R3FETmO6RJtFJ1hysKikMiZLM2DGHC9C3ONSvXUt';
		$result->name           = 'John Doe';
		$result->payment_status = 'John Doe';
		$result->sender_name    = sanitize_text_field( $data['sender_name'] );
		$result->sender_email   = sanitize_email( $data['sender_email'] );
		$result->email          = sanitize_email( $data['email'] );
		$result->created_at     = wp_date( 'Y-m-d H:i:s' );
		$result->email_subject  = sanitize_text_field( $data['email_subject'] );
		$result->email_body     = $purifier->purify( $data['email_body'] );
		$result->email_bcc      = '';
		$result->redirect       = '';
		$result->input_fields   = '{"fields":[{"name":"Phone","value":"0612345678"},{"name":"City","value":"Amsterdam"}],' .
								'"tickets":[{"name":"VIP ticket","price":"7.00","vat":"21","count":2},{"name":"Normal ticket",' .
								'"price":"5.00","vat":"21","count":1}],"order_amount":"19.00","events":[0],"orders":[0]}';

		// Save the message.
		$msg_id = $fevt_container->get( Options::class )->save_message( $result );

		// Send the email.
		try {
			$fevt_container->get( MailerFactory::class )->send_email( $result, $msg_id );
		} catch ( Throwable $e ) {
			return new WP_Error( 'email_error', $e->getMessage(), array( 'status' => 500 ) );
		}

		/* translators: %s is the emailaddress */
		return rest_ensure_response( sprintf( __( 'Testemail send to "%s"', 'fast-events' ), $data['email'] ) );
	}


	/**
	 * Duplicate event
	 *
	 * @since 1.10
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function event_duplicate( WP_REST_Request $data ): WP_Error|WP_REST_Response {
		global $fevt_container;

		// Check the event authorization.
		$resp = $this->auth->check_event_authorization( absint( $data['id'] ) );
		if ( is_wp_error( $resp ) ) {
			return $resp;
		}

		return rest_ensure_response( $fevt_container->get( EventsHelper::class )->fast_events_duplicate( $data ) );
	}


	/**
	 * Delete all orders from an event
	 *
	 * @since 1.10
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function event_orders_delete( WP_REST_Request $data ): WP_Error|WP_REST_Response {
		global $fevt_container;

		// Check the event authorization.
		$resp = $this->auth->check_event_authorization( absint( $data['id'] ) );
		if ( is_wp_error( $resp ) ) {
			return $resp;
		}

		return rest_ensure_response( $fevt_container->get( EventsHelper::class )->fast_events_event_delete_helper( $data ) );
	}


	/**
	 * Get an array of attachment ids for which the user is authorised
	 *
	 * @since 1.7
	 * @since 1.10 Added invoice id.
	 *
	 * @return array|WP_Error
	 */
	private function get_attachment_ids(): WP_Error|array {
		global $wpdb;

		// Save all the current attachment ids in use.
		$events = $wpdb->get_results( "SELECT id, input_fields, pdf_info FROM {$wpdb->prefix}fe_events" );
		if ( null === $events ) {
			return new WP_Error( 'query_error', __( 'No events found', 'fast-events' ), array( 'status' => 404 ) );
		}
		$attachment_ids = array();
		foreach ( $events as $event ) {
			$pdf_info = json_decode( $event->pdf_info );
			if ( ! empty( $pdf_info ) ) {
				$attachment_ids[] = $pdf_info->ticket->id;
				$attachment_ids[] = $pdf_info->invoice->id;
				if ( ! empty( $pdf->ticket->types ) ) {
					foreach ( $pdf_info->ticket->types as $value ) {
						$attachment_ids[] = $value;
					}
				}
			}
		}
		return array_unique( $attachment_ids, SORT_NUMERIC );
	}


	/**
	 * Get all WP Roles
	 *
	 * @since 1.10
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function wp_roles_callback(): WP_Error|WP_REST_Response {
		require_once ABSPATH . 'wp-admin/includes/user.php';

		$roles = array();
		foreach ( get_editable_roles() as $role => $details ) {
			$sub['role'] = esc_attr( $role );
			$sub['name'] = translate_user_role( $details['name'] );
			$roles[]     = $sub;
		}
		return rest_ensure_response( $roles );
	}


	/**
	 * Get all 'fe_account' role users
	 *
	 * @since 2.0
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function fe_users(): WP_Error|WP_REST_Response {
		$args  = array(
			'role'    => 'fe_account',
			'orderby' => 'user_login',
			'order'   => 'ASC',
		);
		$users = get_users( $args );

		$user_info = array();
		foreach ( $users as $user ) {
			$user_info[ $user->ID ] = $user->user_login;
		}
		return rest_ensure_response( $user_info );
	}


	/**
	 * Get media posts and pages
	 *
	 * @since 1.0
	 *
	 * @return WP_REST_Response
	 */
	public function get_posts(): WP_REST_Response {
		global $wpdb;

		return rest_ensure_response(
			array(
				'media' => $wpdb->get_results( "SELECT id, post_title FROM $wpdb->posts WHERE post_type='attachment'" ),
				'pages' => $wpdb->get_results( "SELECT id, post_title FROM $wpdb->posts WHERE post_type='page' AND post_status='publish'" ),
			)
		);
	}
}
