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

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

defined( 'ABSPATH' ) || exit;

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

/**
 * FE Admin Mail Lists class.
 */
class MailListsController extends WP_REST_Controller {


	/**
	 * Admin Mail Lists 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/emaillists';
	}


	/**
	 * Register the routes for the objects of the controller.
	 *
	 * @since 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::EMAIL_LIST_READ );
					},
					'args'                => $this->get_collection_params(),
				),
				'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::READABLE,
					'callback'            => array( $this, 'get_item' ),
					'permission_callback' => function () {
						return $this->fast_events_admin_permissions( $this->auth::EMAIL_LIST_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::EMAIL_LIST_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::EMAIL_LIST_CHANGE );
					},
				),
				'schema' => array( $this, 'get_public_item_schema' ),
			)
		);
	}


	/**
	 * Get the authorization for the Email Lists 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 emaillists.
	 *
	 * @param WP_REST_Request $request Request instance.
	 * @return WP_REST_Response|WP_Error|array
	 *
	 * @since 2.0
	 */
	public function get_items( $request ): WP_Error|WP_REST_Response|array {
		global $wpdb;

		$auth_query = $this->auth->get_restricted_events( $this->auth::FAST_EVENTS_AUTH_QUERY_ORDER_LOOKUP );

		// The 'WHERE event_id > 0' is needed for a possible limit in available events for the user
		// as it may be concatenated with some like 'AND event_id IN (3,4)'.
		$result = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT event_id, COUNT(event_id) AS total FROM {$wpdb->prefix}fe_event_user_groups WHERE event_id > 0 " .
				$auth_query . ' GROUP BY event_id' // phpcs:ignore
			)
		);
		if ( is_wp_error( $result ) ) {
			return $result;
		}

		$mail_lists = array();

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

		$response = rest_ensure_response( $mail_lists );

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

		return $response;
	}


	/**
	 * Get a single emaillist.
	 *
	 * @param WP_REST_Request $request Request instance.
	 * @return WP_REST_Response|WP_Error|array
	 *
	 * @since 2.0
	 */
	public function get_item( $request ): WP_Error|WP_REST_Response|array {
		global $wpdb;

		$auth_query = $this->auth->get_restricted_events( $this->auth::FAST_EVENTS_AUTH_QUERY_ORDER_LOOKUP );

		$result = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT event_id, COUNT(event_id) AS total FROM {$wpdb->prefix}fe_event_user_groups WHERE event_id = %d " .
				$auth_query . ' GROUP BY event_id', // phpcs:ignore
				$request['id']
			)
		);
		if ( empty( $result ) ) {
			return new WP_Error( 'not_found', __( 'Email list not found', 'fast-events' ), array( 'status' => 404 ) );
		}

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


	/**
	 * Create a new emaillist for event.
	 *
	 * @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.0
	 * @since 2.2.0 - Added optional header.
	 *              - Field separator now is ';'.
	 */
	public function create_item( $request ): WP_Error|WP_REST_Response {
		global $wpdb;

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

		if ( isset( $_FILES['file']['tmp_name'] ) && empty( $_FILES['file']['error'] ) ) { // phpcs:ignore

			// Read the file.
			$fname = sanitize_text_field( wp_unslash( $_FILES['file']['tmp_name'] ) ); // phpcs:ignore
			$arr   = file( $fname, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );

			// Remove temp file.
			wp_delete_file( $fname );

			// Create the query.
			$values       = array();
			$placeholders = array();
			$query        = "INSERT INTO {$wpdb->prefix}fe_event_user_groups (event_id,name,email,num_tickets) VALUES ";
			$cnt          = 0;
			$mask         = ' "';
			foreach ( $arr as $items ) {
				if ( 0 === $cnt && 1 === $request['header'] ) {
					++$cnt;
					continue;
				}
				$element = explode( ';', $items );
				if ( 3 !== count( $element ) ) {
					/* translators: %1$d is a line number */
					return new WP_Error( 'invalid_columns', sprintf( __( 'Invalid number of columns in line #%1$d', 'fast-events' ), $cnt + 1 ), array( 'status' => 400 ) );
				}
				if ( empty( $element[0] ) || ! is_email( $element[1] ) ) {
					/* translators: %1$d is a line number */
					return new WP_Error( 'invalid_columns', sprintf( __( 'Invalid content in line #%1$d', 'fast-events' ), $cnt + 1 ), array( 'status' => 400 ) );
				}
				array_push(
					$values,
					$event_id,
					sanitize_text_field( trim( $element[0], $mask ) ),
					sanitize_email( trim( $element[1], $mask ) ),
					absint( trim( $element[2], $mask ) )
				);
				$placeholders[] = '(%d,%s,%s,%d)';
				++$cnt;
			}
			if ( empty( $placeholders ) ) {
				return new WP_Error( 'no_coupons', __( 'No entries found in file', 'fast-events' ), array( 'status' => 404 ) );
			}
			$query .= implode( ', ', $placeholders );
			$query .= ' ON DUPLICATE KEY UPDATE name = VALUES(name)';

			// Add the entries to the user_group table.
			$result = $wpdb->query( $wpdb->prepare( $query, $values ) ); // phpcs:ignore

			// Was there a SQL error?
			if ( false === $result ) {
				/* translators: %1$d is the event number and %2$s is the error string */
				return new WP_Error( 'not_found', sprintf( __( 'Error adding usergroup to eventid #%1$d (%2$s)', 'fast-events' ), $event_id, $wpdb->print_error() ), array( 'status' => 404 ) );
			}

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

			$item           = new stdClass();
			$item->event_id = $event_id;
			$item->total    = 0 === $request['header'] ? $cnt : $cnt - 1;

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

		} else {
			return new WP_Error( 'file_error', __( 'No file found for upload', 'fast-events' ), array( 'status' => 409 ) );
		}
	}


	/**
	 * Delete e-maillist from event.
	 *
	 * @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.0
	 */
	public function delete_item( $request ): WP_Error|WP_REST_Response|bool|array {
		global $wpdb;

		$auth_query = $this->auth->get_restricted_events( $this->auth::FAST_EVENTS_AUTH_QUERY_ORDER_LOOKUP );
		$result     = $wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$wpdb->prefix}fe_event_user_groups WHERE event_id = %d" . $auth_query, // phpcs:ignore
				absint( $request['id'] )
			)
		);
		if ( is_wp_error( $result ) ) {
			return $result;
		}
		$response = new WP_REST_Response();
		$response->set_data(
			array(
				'deleted' => true,
			)
		);

		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 maillist output for response.
	 *
	 * @param mixed           $item    Ticket objects.
	 * @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( 'event_id', $fields ) ) {
			$data['event_id'] = (int) $item->event_id;
		}

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

		$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 maillist request.
	 *
	 * @param stdClass $maillist Tickets object.
	 * @return array Links for the given tickets.
	 *
	 * @since 2.0
	 */
	protected function prepare_links( stdClass $maillist ): array {
		return array(
			'self'       => array(
				'href' => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $maillist->event_id ) ),
			),
			'collection' => array(
				'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
			),
		);
	}


	/**
	 * Return the maillists 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'      => 'maillist',
			'type'       => 'object',
			'properties' => array(
				'event_id' => array(
					'description' => __( 'The unique id of the event.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'integer',
					'readonly'    => true,
				),
				'total'    => array(
					'description' => __( 'The total number of entries.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'integer',
					'readonly'    => true,
				),
				'header'   => array(
					'required'          => true,
					'context'           => array( 'edit' ),
					'type'              => 'integer',
					'sanitize_callback' => 'absint',
					'enum'              => array( 0, 1 ),
				),
			),
		);

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