<?php
/**
 * FE Admin Logs controller.
 *
 * @since 2.0
 */

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

defined( 'ABSPATH' ) || exit;

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


/**
 * FE Admin Log controller class.
 */
class LogsController extends WP_REST_Controller {

	/**
	 * Admin Log controller 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/logs';
	}


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

		// Get all logs.
		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::LOG_READ );
					},
					'args'                => $this->get_collection_params(),
				),
				'schema' => array( $this, 'get_public_item_schema' ),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/order/(?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_order_items' ),
					'permission_callback' => function () {
						return $this->fast_events_admin_permissions( $this->auth::LOG_READ ) ||
							$this->fast_events_admin_permissions( $this->auth::ORDER_LOG_READ );
					},
					'args'                => array(
						'context' => $this->get_context_param( array( 'default' => 'view' ) ),
					),
				),
				'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 order', 'fast-events' ),
						'type'              => 'integer',
						'required'          => true,
						'sanitize_callback' => 'absint',
					),
				),
				array(
					'methods'             => WP_REST_Server::DELETABLE,
					'callback'            => array( $this, 'delete_item' ),
					'permission_callback' => function () {
						return $this->fast_events_admin_permissions( $this->auth::LOG_DELETE ) ||
							$this->fast_events_admin_permissions( $this->auth::ORDER_LOG_DELETE );
					},
				),
				'schema' => array( $this, 'get_public_item_schema' ),
			)
		);
	}


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


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

		// Get all events the user is authorized for.
		$auth_query = $this->auth->get_restricted_events( $this->auth::FAST_EVENTS_AUTH_QUERY_EVENTS );
		$auth_query = str_replace( 'id', 'event_id', $auth_query ); // Hack.

		$result = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT id, event_id, order_id, created_at, log_type, description FROM {$wpdb->prefix}fe_event_log" .
				$auth_query . ' ORDER BY id DESC' // phpcs:ignore
			)
		);

		$logs = array();

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

		$response = rest_ensure_response( $logs );

		// Generate the totals headers.
		$response->header( 'X-WP-Total', count( $logs ) );
		$response->header( 'X-WP-TotalPages', 1 );

		return $response;
	}


	/**
	 * Retrieve all log entries of an order.
	 *
	 * @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.0
	 */
	public function get_order_items( WP_REST_Request $request ): WP_Error|WP_REST_Response|array {
		global $wpdb;

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

		$result = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT id, event_id, order_id, created_at, log_type, description FROM {$wpdb->prefix}fe_event_log WHERE order_id = %d" .
				$auth_query . ' ORDER BY id DESC', // phpcs:ignore
				$request['id']
			)
		);

		$logs = array();

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

		$response = rest_ensure_response( $logs );

		// Generate the totals headers.
		$response->header( 'X-WP-Total', count( $logs ) );
		$response->header( 'X-WP-TotalPages', 1 );

		return $response;
	}


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

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

		$log_entry = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT id, event_id, order_id, created_at, log_type, description FROM {$wpdb->prefix}fe_event_log WHERE id = %d" . $auth_query, // phpcs:ignore
				$request['id']
			)
		);
		if ( empty( $log_entry ) ) {
			return new WP_Error( 'not_found', __( 'Log entry not found', 'fast-events' ), array( 'status' => 404 ) );
		}

		$wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$wpdb->prefix}fe_event_log WHERE id = %d" . $auth_query, // phpcs:ignore
				$request['id']
			)
		);

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

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

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

		return $response;
	}


	/**
	 * 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 a single log entry output for response.
	 *
	 * @param mixed           $item    Scan key object.
	 * @param WP_REST_Request $request Request object.
	 * @return WP_REST_Response        Response object.
	 *
	 * @since 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( 'event_id', $fields ) ) {
			$data['event_id'] = (int) $item->event_id;
		}

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

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

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

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

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

		return $response;
	}


	/**
	 * Prepares links for the log request.
	 *
	 * @param stdClass        $field   Log object.
	 * @param WP_REST_Request $request Request object.
	 * @return array Links for the given order.
	 *
	 * @since 2.0
	 */
	protected function prepare_links( stdClass $field, WP_REST_Request $request ): array {
		if ( str_contains( $request->get_route(), '/order/' ) ) {
			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/order/%d', $this->namespace, $this->rest_base, $field->order_id ) ),
				),
			);
		} else {
			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 ) ),
				),
			);
		}
	}


	/**
	 * Return the logs' schema.
	 *
	 * @return array
	 *
	 * @since 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'      => 'logs',
			'type'       => 'object',
			'properties' => array(
				'id'          => array(
					'description' => __( 'The unique id of the log.', '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,
				),
				'order_id'    => array(
					'description' => __( 'The unique id of the order.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'integer',
					'readonly'    => true,
				),
				'created_at'  => array(
					'description' => __( 'The date of the log entry.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'string',
					'readonly'    => true,
				),
				'log_type'    => array(
					'description' => __( 'The type of log entry.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'string',
					'readonly'    => true,
				),
				'description' => array(
					'description' => __( 'The description of the log entry.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'string',
					'readonly'    => true,
				),

			),
		);

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