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

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

defined( 'ABSPATH' ) || exit;

use Exception;
use FeData\FastEvents\Helpers\AuthHelper;
use FeData\FastEvents\Helpers\OrderHelper;
use FeData\FastEvents\Helpers\SeatsHelper;
use FeData\FastEvents\Payment\PaymentFactory;
use stdClass;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Fast Events orders admin class.
 */
class OrdersAdminController {

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

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


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

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

		// Get order details.
		register_rest_route(
			self::NAMESPACE,
			self::ORDERS_ROUTE . 'details',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'order_details' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::ORDER_DETAILS );
				},
				'args'                => array(
					'id' => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
				),
			)
		);

		// Get payment details.
		register_rest_route(
			self::NAMESPACE,
			self::ORDERS_ROUTE . 'paymentdetails',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'payment_details' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::ORDER_PAYMENT_DETAILS );
				},
				'args'                => array(
					'id' => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
				),
			)
		);

		// Export orders.
		register_rest_route(
			self::NAMESPACE,
			self::ORDERS_ROUTE . 'export',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'orders_export' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::ORDER_EXPORT );
				},
				'args'                => array(
					'id' => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
				),
			)
		);

		// Get order details via qrcode.
		register_rest_route(
			self::NAMESPACE,
			self::ORDERS_ROUTE . 'qrdetails',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'qrcode_details' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::ORDER_DETAILS_VIA_QR );
				},
				'args'                => array(
					'qrcode' => array(
						'required'          => true,
						'type'              => 'string',
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
			)
		);

		// Send order email.
		register_rest_route(
			self::NAMESPACE,
			self::ORDERS_ROUTE . 'email',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'order_email' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::ORDER_EMAIL );
				},
				'args'                => array(
					'id'    => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
					'email' => array(
						'required' => true,
						'type'     => 'string',
						'format'   => 'email',
					),
				),
			)
		);

		// Export tickets.
		register_rest_route(
			self::NAMESPACE,
			self::ORDERS_ROUTE . 'tickets_export',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'tickets_export' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::TICKETS_EXPORT ) ||
						$this->auth->authorize( $this->auth::TICKETS_EXPORT );
				},
				'args'                => array(
					'id'    => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
					'scans' => array(
						'required' => true,
						'type'     => 'boolean',
					),
				),
			)
		);

		// Get qrcodes and checkin info.
		register_rest_route(
			self::NAMESPACE,
			self::ORDERS_ROUTE . 'gettickets',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'order_get_tickets' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::ORDER_DETAILS ) ||
						$this->auth->authorize( $this->auth::ORDER_DETAILS_VIA_QR );
				},
				'args'                => array(
					'id' => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
				),
			)
		);

		// Refund order.
		register_rest_route(
			self::NAMESPACE,
			self::ORDERS_ROUTE . 'refund',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'order_refund' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::ORDER_REFUND );
				},
				'args'                => array(
					'id' => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
				),
			)
		);

		// Add a new dashboard order.
		register_rest_route(
			self::NAMESPACE,
			self::ORDERS_ROUTE . 'adddashboardorder',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'add_dashboard_order' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::ORDER_ADD );
				},
				'args'                => array(
					'name'        => array(
						'required'          => true,
						'type'              => 'string',
						'sanitize_callback' => 'sanitize_text_field',
					),
					'email'       => array(
						'required' => true,
						'type'     => 'string',
						'format'   => 'email',
					),
					'event_info'  => array(
						'required'          => true,
						'type'              => 'string',
						'sanitize_callback' => 'sanitize_text_field',
					),
					'event_nonce' => array(
						'required'          => true,
						'type'              => 'string',
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
			)
		);

		// Get checkin info on order_id.
		register_rest_route(
			self::NAMESPACE,
			self::ORDERS_ROUTE . 'checkin_read',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'checkin_read' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::ORDER_CHECKIN );
				},
				'args'                => array(
					'id' => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
				),
			)
		);

		// Process checkin for order_id.
		register_rest_route(
			self::NAMESPACE,
			self::ORDERS_ROUTE . 'checkin_write',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'checkin_write' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::ORDER_CHECKIN );
				},
				'args'                => array(
					'orderid' => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
				),
			)
		);
	}


	/**
	 * Get all orders of an event
	 *
	 * @since 1.0
	 * @since 1.4 Event authorization restriction
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function get_all_orders( 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 orders.
		$orders = $wpdb->get_results(
			$wpdb->prepare(
				'SELECT id, name, email, payment_status, custom_status, created_at, num_tickets, total ' .
				" FROM {$wpdb->prefix}fe_event_orders WHERE event_id = %d ORDER BY id DESC",
				$data['id']
			)
		);

		return rest_ensure_response( $orders );
	}


	/**
	 * Get order details
	 *
	 * @since 1.0
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function order_details( WP_REST_Request $data ): WP_Error|WP_REST_Response {
		$info = $this->order_helper->order_details( absint( $data['id'] ) );
		if ( is_wp_error( $info ) ) {
			return $info;
		}

		// Do an internal call to the 'tickets' REST API endpoint.
		$key     = sanitize_text_field( wp_unslash( $data->get_header( 'x_fe_api_key' ) ) );
		$request = new WP_REST_Request( 'POST', '/fast-events/v1/admin/orders/gettickets' );
		$request->set_headers(
			array(
				'Content-Type' => 'application/json',
				'X-FE-API-KEY' => $key,
			)
		);
		$request->set_body( '{"id":"' . $data['id'] . '"}' );
		$response = rest_do_request( $request );
		if ( $response->is_error() ) {
			return $response->as_error();
		}

		return rest_ensure_response(
			array(
				'info'    => $info,
				'tickets' => $response->get_data(),
			)
		);
	}


	/**
	 * Get detailed payment information
	 *
	 * @since 2.0
	 * @since 2.4.0 Get User-Agent from utility
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function payment_details( WP_REST_Request $data ): WP_Error|WP_REST_Response {
		global $wpdb;
		global $fevt_container;

		// Look up the order.
		$order_table = $wpdb->prefix . 'fe_event_orders';
		$event_table = $wpdb->prefix . 'fe_events';
		// phpcs:disable
		$result = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT $order_table.event_id,  $order_table.payment_id, $event_table.saas_user_id, $event_table.test_payments_needed, " .
				"$order_table.payment_status, $order_table.created_at, $order_table.input_fields FROM $order_table " .
				"INNER JOIN $event_table ON $event_table.id = $order_table.event_id " .
				"WHERE $order_table.id = %d" .
				$this->auth->get_restricted_events( $this->auth::FAST_EVENTS_AUTH_QUERY_ORDERS, $event_table ),
				absint( $data['id'] )
			)
		);
		// phpcs:enable
		if ( empty( $result ) || empty( $result->payment_id ) ) {
			return new WP_Error( 'not_found', __( 'Order or payment ID not found', 'fast-events' ), array( 'status' => 404 ) );
		}

		$payment = $fevt_container->get( PaymentFactory::class )->get_payment( $result->payment_id, (int) $result->saas_user_id, $result, (bool) $result->test_payments_needed );
		if ( is_wp_error( $payment ) ) {
			return $payment;
		}

		// Set the creation date and time.
		$resp              = new stdClass();
		$resp->payment_id  = $result->payment_id;
		$resp->description = $payment['description'];
		$resp->amount      = $payment['currency'] . ' ' . number_format_i18n( $payment['amount'], 2 );
		if ( isset( $payment['customer'] ) ) {
			$resp->customer = $payment['customer'];
		}
		if ( isset( $payment['actions'] ) ) {
			$resp->actions = $payment['actions'];
		}
		if ( isset( $payment['refunds'] ) ) {
			$resp->refunds = $payment['refunds'];
		}
		if ( isset( $payment['application_fee'] ) ) {
			$resp->application_fee = $payment['application_fee'];
		}
		if ( isset( $payment['dashboard_link'] ) ) {
			$resp->dashboard_link = $payment['dashboard_link'];
		}

		return rest_ensure_response( array( 'info' => $resp ) );
	}


	/**
	 * Export all orders of an event
	 *
	 * @since 1.6
	 * @since 2.2.0 Added 'discount' field.
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function orders_export( 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 orders.
		$orders = $wpdb->get_results(
			$wpdb->prepare(
				'SELECT id, event_date, payment_status, custom_status, created_at, name, email, input_fields, num_tickets, total, discount ' .
				" FROM {$wpdb->prefix}fe_event_orders WHERE event_id = %d ORDER BY id DESC",
				$data['id']
			)
		);

		return rest_ensure_response( $orders );
	}


	/**
	 * Get order details via qrcode
	 *
	 * @since 1.6
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function qrcode_details( WP_REST_Request $data ): WP_Error|WP_REST_Response {
		global $wpdb;

		// Look up the qrcode.
		$qrcode = $wpdb->get_row( $wpdb->prepare( "SELECT order_id FROM {$wpdb->prefix}fe_event_qrcodes WHERE qrcode = %s", $data['qrcode'] ) );
		if ( empty( $qrcode ) ) {
			return new WP_Error( 'not_found', __( 'Qrcode not found', 'fast-events' ), array( 'status' => 404 ) );
		}

		$info = $this->order_helper->order_details( absint( $qrcode->order_id ) );
		if ( is_wp_error( $info ) ) {
			return $info;
		}

		// Do an internal call to the 'tickets' REST API endpoint.
		$key     = sanitize_text_field( wp_unslash( $data->get_header( 'x_fe_api_key' ) ) );
		$request = new WP_REST_Request( 'POST', '/fast-events/v1/admin/orders/gettickets' );
		$request->set_headers(
			array(
				'Content-Type' => 'application/json',
				'X-FE-API-KEY' => $key,
			)
		);
		$request->set_body( '{"id":"' . $qrcode->order_id . '"}' );
		$response = rest_do_request( $request );
		if ( $response->is_error() ) {
			return $response->as_error();
		}

		return rest_ensure_response(
			array(
				'info'    => $info,
				'tickets' => $response->get_data(),
			)
		);
	}


	/**
	 * Send order email
	 *
	 * @since 1.0
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function order_email( WP_REST_Request $data ): WP_Error|WP_REST_Response {
		$result = $this->order_helper->order_email( absint( $data['id'] ), $data['email'] );
		if ( is_wp_error( $result ) ) {
			return $result;
		} else {
			return rest_ensure_response( $result );
		}
	}


	/**
	 * Export all tickets of an event
	 *
	 * @since 1.6
	 * @since 2.1.0 Return all scans or just the tickets with the personalised_input.
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function tickets_export( WP_REST_Request $data ): WP_Error|WP_REST_Response {
		global $wpdb;
		global $fevt_container;

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

		// Get the event.
		$event = $wpdb->get_row( $wpdb->prepare( "SELECT id, tickets_needed, seats_needed FROM {$wpdb->prefix}fe_events WHERE id = %d", $event_id ) );
		if ( empty( $event ) ) {
			/* translators: 1: event ID  */
			return new WP_Error( 'not_found', sprintf( __( 'Event [%1$s] not found', 'fast-events' ), $event_id ), array( 'status' => 404 ) );
		}

		// Is this event using tickets?
		if ( ! $event->tickets_needed ) {
			/* translators: 1: event ID  */
			return new WP_Error( 'not_found', sprintf( __( 'Event [%1$s] is not using tickets', 'fast-events' ), $event_id ), array( 'status' => 400 ) );
		}

		// Create the query.
		$qtable = $wpdb->prefix . 'fe_event_qrcodes';
		$otable = $wpdb->prefix . 'fe_event_orders';
		$etable = $wpdb->prefix . 'fe_event_entries';
		// phpcs:disable
		if ( $data['scans'] ) {
			$query = "SELECT $otable.id, $otable.created_at, $otable.name, $otable.email, " .
					 "$qtable.ticket_type, $qtable.qrcode, $qtable.amount, COALESCE($qtable.scan_date,'') AS scan_date, $qtable.scan_location " .
					 "FROM $qtable " .
					 "INNER JOIN $otable ON $otable.id = $qtable.order_id WHERE $otable.event_id = " . absint( $event->id ) .

					 ' UNION ' .

					 "SELECT $otable.id, $otable.created_at,  $otable.name, $otable.email, " .
					 't.ticket_type AS ' . __( 'Tickettype', 'fast-events' ) . ',' .
					 't.qrcode, t.amount, t.scan_date, t.scan_location  FROM (' .

					 "SELECT $qtable.order_id, $qtable.ticket_type, $qtable.qrcode, $qtable.amount, $etable.scan_date, $etable.scan_location FROM $qtable " .
					 "RIGHT JOIN $etable ON $qtable.id = $etable.qrcode_id WHERE $qtable.event_id = " . absint( $event->id ) . ') t ' .

					 "INNER JOIN $otable ON $otable.id = t.order_id";
		} else {
			$query = "SELECT $otable.id, $otable.created_at, $otable.name, $otable.email, " .
					 "$qtable.ticket_type, $qtable.qrcode, $qtable.amount, $qtable.personalised_input " .
					 "FROM $qtable " .
					 "INNER JOIN $otable ON $otable.id = $qtable.order_id WHERE $otable.event_id = " . absint( $event->id );
		}
		// phpcs:enable

		if ( 0 === (int) $event->seats_needed ) {
			return rest_ensure_response( $wpdb->get_results( $query ) ); // phpcs:ignore
		} else {
			$result = $wpdb->get_results( $query ); // phpcs:ignore
			$fevt_container->get( SeatsHelper::class )->get_bulk_seats( (int) $event->id, $result );
			return rest_ensure_response( $result );
		}
	}


	/**
	 * Get tickets and checkin info
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @since 1.0
	 *
	 * @return array|WP_Error|WP_REST_Response
	 */
	public function order_get_tickets( WP_REST_Request $data ): WP_Error|WP_REST_Response|array {
		$result = $this->order_helper->get_qrcodes( absint( $data['id'] ) );
		if ( is_wp_error( $result ) ) {
			return $result;
		} else {
			return rest_ensure_response( $result );
		}
	}


	/**
	 * Order refund
	 *
	 * @since 1.0
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function order_refund( WP_REST_Request $data ): WP_Error|WP_REST_Response {
		$result = $this->order_helper->order_refund( absint( $data['id'] ) );
		if ( is_wp_error( $result ) ) {
			return $result;
		} else {
			return rest_ensure_response( $result );
		}
	}


	/**
	 * Add a new dashboard order
	 *
	 * @since 1.0
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @throws Exception Ignored.
	 */
	public function add_dashboard_order( WP_REST_Request $data ): WP_Error|WP_REST_Response {
		$result = $this->order_helper->new_dashboard_order( $data );
		if ( is_wp_error( $result ) ) {
			return $result;
		} else {
			return rest_ensure_response( $result );
		}
	}


	/**
	 * Get qrcodes and checkin info belonging to order
	 *
	 * @since 1.10
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return array|WP_Error|WP_REST_Response
	 */
	public function checkin_read( WP_REST_Request $data ): WP_Error|WP_REST_Response|array {
		$result = $this->order_helper->get_qrcodes( absint( $data['id'] ) );
		if ( is_wp_error( $result ) ) {
			return $result;
		} else {
			return rest_ensure_response( $result );
		}
	}


	/**
	 * Process checking for order_id
	 *
	 * @since 1.10
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function checkin_write( WP_REST_Request $data ): WP_Error|WP_REST_Response {
		$result = $this->order_helper->process_checkin( $data );
		if ( is_wp_error( $result ) ) {
			return $result;
		} else {
			return rest_ensure_response( $result );
		}
	}
}
