<?php
/**
 * FE Admin PDF templates controller.
 *
 * @since 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 PDF Template class.
 */
class PdfTemplatesController extends WP_REST_Controller {
	private const TEMPLATE_TYPE = 'fast_events_template_type';

	/**
	 * Upload directory information.
	 *
	 * @since 2.0
	 *
	 * @var array
	 */
	private array $upload_dir;

	/**
	 * Cached array of usernames.
	 *
	 * @since 2.0
	 *
	 * @var array
	 */
	private array $usernames = array();

	/**
	 * Admin PDF Template constructor.
	 *
	 * @param AuthHelper $auth Auth helper instance.
	 *
	 * @since 2.0
	 */
	public function __construct( private readonly AuthHelper $auth ) {
		$this->namespace  = 'fast-events/v1';
		$this->rest_base  = 'admin/pdf-templates';
		$this->upload_dir = wp_upload_dir();
	}


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

		// Get input fields.
		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->auth->authorize( $this->auth::PDF_TEMPLATE_READ );
					},
					'args'                => $this->get_collection_params(),
				),
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'create_item' ),
					'permission_callback' => function () {
						return $this->auth->authorize( $this->auth::PDF_TEMPLATE_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 PDF template', '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->auth->authorize( $this->auth::PDF_TEMPLATE_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->auth->authorize( $this->auth::PDF_TEMPLATE_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->auth->authorize( $this->auth::PDF_TEMPLATE_CHANGE );
					},
				),
				'schema' => array( $this, 'get_public_item_schema' ),
			)
		);
	}


	/**
	 * Get all PDF templates the user is allowed to see.
	 * Users with the 'administrator' role can see all templates
	 *
	 * @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;

		// Check if administrator user.
		$condition = '';
		if ( ! $this->auth->is_admin_user() ) {
			$condition = 'AND post_author = ' . wp_get_current_user()->ID;
		}
		$result = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT ID, post_author, post_title, post_date, post_modified FROM $wpdb->posts" .
				" WHERE post_type='attachment' AND post_mime_type='application/pdf' $condition" // phpcs:ignore
			),
			ARRAY_A
		);

		$pdf_templates = array();

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

		$response = rest_ensure_response( $pdf_templates );

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

		return $response;
	}


	/**
	 * Retrieve a single PDF template.
	 *
	 * @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_item( $request ): WP_Error|WP_REST_Response|array {
		$pdf_template = $this->get_pdf_template( $request['id'] );
		if ( is_wp_error( $pdf_template ) ) {
			return $pdf_template;
		}

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


	/**
	 * Create a new PDF template.
	 *
	 * @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 create_item( $request ): WP_Error|WP_REST_Response|bool|array {
		require_once ABSPATH . 'wp-admin/includes/image.php';
		require_once ABSPATH . 'wp-admin/includes/file.php';
		require_once ABSPATH . 'wp-admin/includes/media.php';

		// Save the new PDF.
		if ( isset( $_FILES['file']['tmp_name'] ) && isset( $_FILES['file']['name'] ) && empty( $_FILES['file']['error'] ) ) { // phpcs:ignore

			// Insert it in the media library.
			$args = array( 'post_title' => $request['title'] );
			if ( isset( $request['author_id'] ) && $this->auth->is_admin_user() && 0 !== $request['author_id'] ) {

				// Check if the user exists and has the 'fe_account' role.
				$user = get_user_by( 'ID', $request['author_id'] );
				if ( false === $user || ( ! in_array( 'fe_account', $user->roles, true ) && ! in_array( 'administrator', $user->roles, true ) ) ) {
					return new WP_Error( 'wrong_user', esc_html__( 'Wrong user id', 'fast-events' ), array( 'status' => 409 ) );
				}
				$args['post_author'] = $request['author_id'];
			}
			$attachment_id = media_handle_upload( 'file', 0, $args );
			if ( is_wp_error( $attachment_id ) ) {
				return $attachment_id;
			}
		} else {
			return new WP_Error( 'file_error', esc_html__( 'No file found for upload', 'fast-events' ), array( 'status' => 409 ) );
		}
		add_post_meta( $attachment_id, self::TEMPLATE_TYPE, $request['type'], true );

		$pdf_template = $this->get_pdf_template( $attachment_id );
		if ( is_wp_error( $pdf_template ) ) {
			return $pdf_template;
		}
		$request->set_param( 'context', 'view' );

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


	/**
	 * Update a single PDF template.
	 *
	 * @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 update_item( $request ): WP_Error|WP_REST_Response|bool|array {
		$pdf_template = $this->get_pdf_template( $request['id'] );
		if ( is_wp_error( $pdf_template ) ) {
			return $pdf_template;
		}

		// Save the changed PDF if it exists.
		if ( isset( $_FILES['file']['tmp_name'] ) && isset( $_FILES['file']['name'] ) && empty( $_FILES['file']['error'] ) ) { // phpcs:ignore
			$path = get_post_meta( $request['id'], '_wp_attached_file', true );

			// Check if the file exists.
			$file = $this->upload_dir['basedir'] . '/' . $path;
			if ( ! file_exists( $file ) ) {
				return new WP_Error( 'file_not_exist', esc_html__( 'File does not exist', 'fast-events' ), array( 'status' => 409 ) );
			}
			if ( ! copy( $_FILES['file']['tmp_name'], $file ) ) { // phpcs:ignore
				return new WP_Error( 'copy_failed', esc_html__( 'Failed to replace PDF attachment', 'fast-events' ), array( 'status' => 409 ) );
			}
		}

		// Update the post-info.
		$args = array( 'ID' => $request['id'] );
		if ( isset( $request['title'] ) ) {
			$args['post_title'] = $request['title'];
		}

		// Did the post_author change?
		if ( isset( $request['author_id'] ) && $this->auth->is_admin_user() ) {

			if ( 0 === $request['author_id'] ) {
				$args['post_author'] = wp_get_current_user()->ID;
			} else {
				// Check if users exist and have the 'fe_account' role.
				$user = get_user_by( 'ID', $request['author_id'] );
				if ( false === $user || ( ! in_array( 'fe_account', $user->roles, true ) && ! in_array( 'administrator', $user->roles, true ) ) ) {
					return new WP_Error( 'wrong_user', esc_html__( 'Wrong user id', 'fast-events' ), array( 'status' => 409 ) );
				}
				$args['post_author'] = $request['author_id'];
			}
		}
		wp_update_post( $args );
		update_post_meta( $request['id'], self::TEMPLATE_TYPE, $request['type'] );

		$pdf_template = $this->get_pdf_template( $request['id'] );
		if ( is_wp_error( $pdf_template ) ) {
			return $pdf_template;
		}
		$request->set_param( 'context', 'edit' );

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


	/**
	 * Delete a single PDF template.
	 *
	 * @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 {
		$old_pdf_template = $this->get_pdf_template( $request['id'] );
		if ( is_wp_error( $old_pdf_template ) ) {
			return $old_pdf_template;
		}

		wp_delete_attachment( $request['id'], true );

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

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

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

		return $response;
	}


	/**
	 * Retrieve PDF template.
	 *
	 * @param int $id Unique id of the PDF template.
	 * @return array|WP_Error Response object on success, or WP_Error object on failure.
	 *
	 * @since 2.0
	 */
	private function get_pdf_template( int $id ): WP_Error|array {
		global $wpdb;

		// Check if administrator user.
		$condition = '';
		if ( ! $this->auth->is_admin_user() ) {
			$condition = 'AND post_author = ' . wp_get_current_user()->ID;
		}
		$pdf_template = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT ID, post_author, post_title, post_date, post_modified FROM $wpdb->posts" .
				" WHERE post_type='attachment' AND post_mime_type='application/pdf' AND ID=%d $condition", // phpcs:ignore
				$id
			),
			ARRAY_A
		);
		if ( empty( $pdf_template ) ) {
			return new WP_Error( 'not_found', __( 'PDF template not found', 'fast-events' ), array( 'status' => 404 ) );
		}

		return $pdf_template;
	}


	/**
	 * Prepares a single PDF template for response.
	 *
	 * @param mixed           $item    PDF template 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( 'title', $fields ) ) {
			$data['title'] = $item['post_title'];
		}

		if ( rest_is_field_included( 'type', $fields ) ) {
			$type         = get_post_meta( $item['ID'], self::TEMPLATE_TYPE, true );
			$data['type'] = empty( $type ) ? 'ticket' : $type;
		}

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

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

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

		if ( rest_is_field_included( 'url', $fields ) ) {
			$path        = get_post_meta( $item['ID'], '_wp_attached_file', true );
			$data['url'] = $this->upload_dir['baseurl'] . '/' . $path;
		}

		if ( rest_is_field_included( 'username', $fields ) ) {
			if ( ! in_array( $item['ID'], $this->usernames, true ) ) {
				$this->usernames[ $item['ID'] ] = get_user_by( 'ID', (int) $item['post_author'] )->user_login;
			}
			$data['username'] = $this->usernames[ $item['ID'] ];
		}

		$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 PDF templates request.
	 *
	 * @param array $field PDF template object.
	 * @return array Links for the given order.
	 *
	 * @since 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, (int) $field['ID'] ) ),
			),
			'collection' => array(
				'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
			),
		);
	}


	/**
	 * Return the input fields 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'      => 'pdf-templates',
			'type'       => 'object',
			'properties' => array(
				'id'        => array(
					'description' => __( 'The unique id of the PDF template.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'integer',
					'readonly'    => true,
				),
				'title'     => array(
					'description' => __( 'The title of the PDF template.', 'fast-events' ),
					'context'     => array( 'view', 'edit', 'embed' ),
					'type'        => 'string',
					'required'    => true,
					'minLength'   => 1,
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'type'      => array(
					'description' => __( 'The type of PDF template.', 'fast-events' ),
					'context'     => array( 'view', 'edit', 'embed' ),
					'type'        => 'string',
					'enum'        => array( 'ticket', 'invoice' ),
					'required'    => true,
					'default'     => 'ticket',
				),
				'author_id' => array(
					'description' => __( 'The unique id of the author.', 'fast-events' ),
					'context'     => array( 'view', 'edit', 'embed' ),
					'type'        => 'integer',
					'arg_options' => array(
						'sanitize_callback' => 'absint',
					),
				),
				'created'   => array(
					'description' => __( 'Creation date of the PDF template.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'string',
					'format'      => 'date-time',
					'readonly'    => true,
				),
				'modified'  => array(
					'description' => __( 'Creation date of the PDF template.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'string',
					'format'      => 'date-time',
					'readonly'    => true,
				),
				'url'       => array(
					'description' => __( 'Modification date of the PDF template.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'string',
					'format'      => 'uri',
					'readonly'    => true,
				),
				'username'  => array(
					'description' => __( 'The login name of the owner of the PDF template.', 'fast-events' ),
					'context'     => array( 'view', 'embed' ),
					'type'        => 'string',
					'format'      => 'uri',
					'readonly'    => true,
				),
			),
		);

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