<?php
/**
 * Media routes and media api manager
 *
 * @package kirki
 */

namespace Kirki\API;

if (!defined('ABSPATH')) {
	exit; // Exit if accessed directly.
}
use Kirki\HelperFunctions;
use WP_Error;
use WP_REST_Controller;
use WP_REST_Server;


/**
 * Media Class
 * @deprecated
 * @see \Kirki\App\Http\Controllers\Api\MediaController
 */
class Media extends WP_REST_Controller
{

	/**
	 * Initialize the media class
	 *
	 * @return void
	 */
	public function __construct()
	{
		$this->namespace = 'kirki/v1';
		$this->rest_base = 'media';
	}

	/**
	 * Register media routes
	 *
	 * @return void
	 */
	public function register_routes()
	{
		// Get media.
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/',
			array(
				array(
					'methods' => WP_REST_Server::READABLE,
					'callback' => array($this, 'get_items'),
					'permission_callback' => array($this, 'get_items_permissions_check'),
					'args' => $this->get_collection_params(),
				),
				'schema' => array($this, 'get_item_schema'),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/' . 'home',
			array(
				array(
					'methods' => WP_REST_Server::READABLE,
					'callback' => array($this, 'get_home_items'),
					'permission_callback' => array($this, 'get_items_permissions_check'),
					'args' => $this->get_collection_params(),
				),
				'schema' => array($this, 'get_item_schema'),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/update-media',
			array(
				array(
					'methods' => 'POST',
					'callback' => array($this, 'update_media_item'),
					'permission_callback' => array($this, 'get_items_permissions_check'),
					'args' => $this->get_endpoint_args_for_item_schema(WP_REST_Server::CREATABLE),
				),
				'schema' => array($this, 'get_item_schema'),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<media_type>\S+)',
			array(
				'args' => array(
					'media_type' => array(
						'description' => __('media type name to get specific media.', 'kirki'),
						'type' => 'string',
					),
				),
				array(
					'methods' => WP_REST_Server::READABLE,
					'callback' => array($this, 'get_items'),
					'permission_callback' => array($this, 'get_items_permissions_check'),
					'args' => $this->get_collection_params(),
				),
				'schema' => array($this, 'get_item_schema'),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/' . 'trash-restore' . '/(?P<post_id>\S+)',
			array(
				'args' => array(
					'post_id' => array(
						'description' => __('Unique post id for the object.', 'kirki'),
						'type' => 'integer',
					),
				),
				array(
					'methods' => WP_REST_Server::EDITABLE,
					'callback' => array($this, 'trash_or_restore_item'),
					'permission_callback' => array($this, 'post_items_permissions_check'),
					'args' => $this->get_collection_params(),
				),
				'schema' => array($this, 'get_item_schema'),
			)
		);
	}

	/**
	 * Checks if a given request has access to read contacts.
	 *
	 * @param \WP_REST_Request $request user request(not used right now).
	 *
	 * @return \WP_REST_Response
	 */
	public function get_items_permissions_check($request)
	{
		if (HelperFunctions::is_api_call_from_editor_preview() && HelperFunctions::is_api_header_post_editor_preview_token_valid()) {
			return true;
		}

		return HelperFunctions::has_access(
			array(
				KIRKI_ACCESS_LEVELS['FULL_ACCESS'],
				KIRKI_ACCESS_LEVELS['CONTENT_ACCESS'],
			)
		);
	}

	/**
	 * Checks if a given request has access to read contacts.
	 *
	 * @param \WP_REST_Request $request user request(not used right now).
	 *
	 * @return \WP_REST_Response
	 */
	public function post_items_permissions_check($request)
	{
		return HelperFunctions::has_access(array(KIRKI_ACCESS_LEVELS['FULL_ACCESS']));
	}

	/**
	 * Retrieves a list of address items.
	 *
	 * @param \WP_REST_Request $request user request.
	 *
	 * @return \WP_REST_Response|WP_Error
	 */
	public function get_items($request)
	{
		$args = array();
		$media_type = 'normal';
		$params = $this->get_collection_params();

		foreach ($params as $key => $value) {
			if (isset($request[$key])) {
				$args[$key] = $request[$key];
			}
		}

		if (isset($request['media_type'])) {
			if ($request['media_type'] === 'trash') {
				$media_type = 'trash';
			} else {
				exit();
			}
		}

		$args['number'] = HelperFunctions::sanitize_text(isset($args['per_page']) ? $args['per_page'] : 20);
		//phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$args['offset'] = HelperFunctions::sanitize_text(isset($_GET['offset']) ? $_GET['offset'] : 0);
		//phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$args['category'] = HelperFunctions::sanitize_text(isset($_GET['category']) ? $_GET['category'] : '');

		// unset others.
		unset($args['per_page']);
		unset($args['page']);

		$data = array();
		$media = $this->bx_get_media($args, $media_type);

		if (!$media) {
			return array(
				'mediaData' => array(),
				'hasMore' => false,
			);
		}

		foreach ($media as $media_item) {
			$response = $this->prepare_item_for_response($media_item, $request);
			$data[] = $this->prepare_response_for_collection($response);
		}

		$total = $this->bx_media_count($media_type);
		$max_pages = ceil($total / (int) $args['number']);

		$response_data = array(
			'mediaData' => array_slice($data, 0, $args['number']),
			'hasMore' => count($data) === $args['number'] + 1,
		);
		$response = rest_ensure_response($response_data);

		$response->header('X-WP-Total', (int) $total);
		$response->header('X-WP-TotalPages', (int) $max_pages);

		return $response;
	}

	/**
	 * Update media item
	 *
	 * @param media_data
	 */
	public function update_media_item($request)
	{
		$media = HelperFunctions::sanitize_text(isset($request['mediaData']) ? $request['mediaData'] : '[]');
		$media_data = json_decode($media, true);

		$media_id = $this->save_media_item_data($media_data);

		if ($media_id === -1) {
			return new WP_Error(
				'rest_media_update_media_item',
				__('Media item can not be updated.', 'kirki'),
				array('status' => 400)
			);
		}

		$media_item = get_post($media_id);

		$response = $this->prepare_item_for_response($media_item, $request);
		$response = rest_ensure_response($response);
		return $response;
	}

	/**
	 * Update media item data
	 *
	 * @param media_data
	 */

	private function save_media_item_data($media_data)
	{
		$media_item = get_post($media_data['id']);

		if (!$media_item || $media_item->post_type !== 'attachment') {
			return -1;
		}

		$args = array(
			'ID' => $media_data['id'],
			'post_title' => $media_data['alt'],
		);

		$media_id = wp_update_post($args);

		if (!$media_id) {
			return -1;
		}

		return $media_id;
	}


	/**
	 * Retrieves a list of home media items.
	 *
	 * @param \WP_REST_Request $request user request parameter.
	 *
	 * @return \WP_REST_Response|WP_Error
	 */
	public function get_home_items($request)
	{
		$media_number = 3;
		$img_args = array(
			'number' => $media_number,
			'category' => 'image',
		);
		$audio_args = array(
			'number' => $media_number,
			'category' => 'audio',
		);
		$video_args = array(
			'number' => $media_number,
			'category' => 'video',
		);
		$svg_args = array(
			'number' => $media_number,
			'category' => 'svg',
		);
		$pdf_args = array(
			'number' => $media_number,
			'category' => 'pdf',
		);

		$params = $this->get_collection_params();
		foreach ($params as $key => $value) {
			if (isset($request[$key])) {
				$img_args[$key] = $request[$key];
				$audio_args[$key] = $request[$key];
				$video_args[$key] = $request[$key];
				$svg_args[$key] = $request[$key];
				$pdf_args[$key] = $request[$key];
			}
		}

		$img_data = array();
		$img_media = $this->bx_get_media($img_args);

		foreach ($img_media as $media_item) {
			$response = $this->prepare_item_for_response($media_item, $request);
			$img_data[] = $this->prepare_response_for_collection($response);
		}

		$audio_data = array();
		$audio_media = $this->bx_get_media($audio_args);

		foreach ($audio_media as $media_item) {
			$response = $this->prepare_item_for_response($media_item, $request);
			$audio_data[] = $this->prepare_response_for_collection($response);
		}

		$video_data = array();
		$video_media = $this->bx_get_media($video_args);

		foreach ($video_media as $media_item) {
			$response = $this->prepare_item_for_response($media_item, $request);
			$video_data[] = $this->prepare_response_for_collection($response);
		}

		$svg_data = array();
		$svg_media = $this->bx_get_media($svg_args);

		foreach ($svg_media as $media_item) {
			$response = $this->prepare_item_for_response($media_item, $request);
			$svg_data[] = $this->prepare_response_for_collection($response);
		}

		$pdf_data = array();
		$pdf_media = $this->bx_get_media($pdf_args);

		foreach ($pdf_media as $media_item) {
			$response = $this->prepare_item_for_response($media_item, $request);
			$pdf_data[] = $this->prepare_response_for_collection($response);
		}

		$response_data = array(
			'image' => array(
				'data' => $img_data,
				'total' => $this->bx_media_category_count(HelperFunctions::get_media_type_query_string('image'), ),
			),
			'audio' => array(
				'data' => $audio_data,
				'total' => $this->bx_media_category_count(HelperFunctions::get_media_type_query_string('audio'), ),
			),
			'video' => array(
				'data' => $video_data,
				'total' => $this->bx_media_category_count(HelperFunctions::get_media_type_query_string('video'), ),
			),
			'svg' => array(
				'data' => $svg_data,
				'total' => $this->bx_media_category_count(HelperFunctions::get_media_type_query_string('svg'), ),
			),
			'pdf' => array(
				'data' => $pdf_data,
				'total' => $this->bx_media_category_count(HelperFunctions::get_media_type_query_string('pdf'), ),
			),
		);

		$response = rest_ensure_response($response_data);
		return $response;
	}

	/**
	 * Remove or restore a media item.
	 *
	 * @param \WP_REST_Request $request user request parameter.
	 *
	 * @return \WP_REST_Response|WP_Error
	 */
	public function trash_or_restore_item($request)
	{
		$input_post_id = HelperFunctions::sanitize_text(isset($request['post_id']) ? $request['post_id'] : '');
		$post_id = $this->bx_trash_restore_media($input_post_id);

		if ($post_id === -1) {
			return new WP_Error(
				'rest_media_to_trash_or_restore',
				__('Media can not move to trash or restore.', 'kirki'),
				array('status' => 400)
			);
		}

		$post = get_post($post_id);

		$response = $this->prepare_item_for_response($post, $request);
		$response = rest_ensure_response($response);
		return $response;
	}

	/**
	 * Prepare media items for response
	 *
	 * @param object $item item info.
	 * @param array  $request user request.
	 */
	public function prepare_item_for_response($item, $request)
	{
		$data = array();
		$fields = $this->get_fields_for_response($request);
		$categories = KIRKI_SUPPORTED_MEDIA_TYPES;

		if (in_array('id', $fields, true)) {
			$data['id'] = (int) $item->ID;
		}

		if (in_array('name', $fields, true)) {
			$data['name'] = $item->post_name;
		}

		if (in_array('alt', $fields, true)) {
			$alt = get_post_meta($item->ID, '_wp_attachment_image_alt', true);

			$data['alt'] = $alt ? $alt : $item->post_title;
		}

		if (in_array('type', $fields, true)) {
			$data['type'] = $item->post_mime_type;
		}

		if (in_array('url', $fields, true)) {
			$data['url'] = $item->guid;
		}

		$data['thumbnail'] = wp_get_attachment_image_url($item->ID);

		if (in_array('category', $fields, true)) {
			$category = '';

			foreach ($categories as $cat => $mime_types) {
				if (in_array($item->post_mime_type, $mime_types, true)) {
					$category = 'svg' === $cat ? 'image' : $cat;
					break;
				}
			}

			$data['category'] = $category;
		}

		if (in_array('trash', $fields, true)) {
			$data['trash'] = 'trash' === $item->post_status;
		}

		// media file size converting to human readable format
		$data['file_size'] = filesize(get_attached_file($item->ID));
		$data['file_size'] = size_format($data['file_size']);

		// media file extension
		$file_path = get_attached_file($item->ID);
		$data['file_extension'] = pathinfo($file_path, PATHINFO_EXTENSION);

		$context = !empty($request['context']) ? $request['context'] : 'view';
		$data = $this->filter_response_by_context($data, $context);

		$response = rest_ensure_response($data);

		return $response;
	}

	/**
	 * Retrieves the contact schema, conforming to JSON Schema.
	 *
	 * @return array
	 */
	public function get_item_schema()
	{
		// response from the cache.
		if ($this->schema) {
			return $this->add_additional_fields_schema($this->schema);
		}

		$schema = array(
			'$schema' => 'http://json-schema.org/draft-04/schema',
			'title' => 'media',
			'type' => 'object',
			'properties' => array(
				'id' => array(
					'description' => __('Unique identifier for the object.', 'kirki'),
					'type' => 'integer',
					'context' => array('view', 'edit'),
					'readonly' => true,
				),
				'name' => array(
					'description' => __('Name of the media item.', 'kirki'),
					'type' => 'string',
					'context' => array('view', 'edit'),
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'alt' => array(
					'description' => __('Alt of the media item.', 'kirki'),
					'type' => 'string',
					'context' => array('view', 'edit'),
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'type' => array(
					'description' => __('Media mime type.', 'kirki'),
					'type' => 'string',
					'context' => array('view', 'edit'),
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'category' => array(
					'description' => __('Media category.', 'kirki'),
					'type' => 'string',
					'context' => array('view', 'edit'),
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'url' => array(
					'description' => __('Media url.', 'kirki'),
					'type' => 'string',
					'context' => array('view', 'edit'),
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'trash' => array(
					'description' => __('Media trash or not.', 'kirki'),
					'type' => 'string',
					'context' => array('view', 'edit'),
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'total' => array(
					'description' => __('Total media items.', 'kirki'),
					'type' => 'integer',
					'context' => array('view', 'edit'),
					'readonly' => true,
				),
				'date' => array(
					'description' => __("The date the object was published, in the site's timestamp.", 'kirki'),
					'type' => 'string',
					'format' => 'date-time',
					'context' => array('view'),
					'readonly' => true,
				),
			),
		);

		$this->schema = $schema;

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

	/**
	 * Retrieves the query params from collections.
	 *
	 * @return array
	 */
	public function get_collection_params()
	{
		$params = parent::get_collection_params();
		return $params;
	}

	/**
	 * Fetch media
	 *
	 * @param array  $args params.
	 * @param string $media_type normal.
	 *
	 * @return array
	 */
	private function bx_get_media($args = array(), $media_type = 'normal')
	{
		global $wpdb;

		$order_list = array('ASC', 'DESC');

		//phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$order = isset($_GET['order']) && in_array(strtoupper(HelperFunctions::sanitize_text($_GET['order'])), $order_list, true) ? strtoupper(HelperFunctions::sanitize_text($_GET['order'])) : 'DESC';
		$defaults = array(
			'number' => 20,
			'offset' => 0,
			'orderby' => 'post_date',
			'order' => $order,
			'search' => '',
			'category' => '',
		);
		$mime_types = array(
			'image' => HelperFunctions::get_media_type_query_string('image'),
			'audio' => HelperFunctions::get_media_type_query_string('audio'),
			'svg' => HelperFunctions::get_media_type_query_string('svg'),
			'video' => HelperFunctions::get_media_type_query_string('video'),
			'lottie' => HelperFunctions::get_media_type_query_string('lottie'),
			'pdf' => HelperFunctions::get_media_type_query_string('pdf'),
		);

		$categories = explode(',', $args['category']);
		$mime_types_str = '';

		foreach ($categories as $key => $category) {
			if ($category) {
				$mime_types_str .= $key > 0 ? ',' . $mime_types[$category] : $mime_types[$category];
			}
		}

		$args = wp_parse_args($args, $defaults);

		$limit_plus_one = (int) HelperFunctions::sanitize_text($args['number']) + 1;
		$sarch_query = HelperFunctions::sanitize_text($args['search']);
		$mime_types_str = HelperFunctions::sanitize_text($mime_types_str);

		$post_status_condition = $media_type === 'normal' ? $wpdb->prepare('post_status<>%s', 'trash') : $wpdb->prepare('post_status=%s', 'trash');
		$base_condition = $wpdb->prepare('post_type=%s', 'attachment') . " AND {$post_status_condition}";
		$where_conditions = $mime_types_str ? $base_condition . " AND post_mime_type IN ($mime_types_str)" : $base_condition;
		$where_conditions = $args['search']
			? $where_conditions . $wpdb->prepare(
				' AND post_name LIKE %s',
				'%' . $wpdb->esc_like($sarch_query) . '%'
			)
			: $where_conditions;

		$order_by = HelperFunctions::sanitize_text($args['orderby']);
		$offset = (int) HelperFunctions::sanitize_text($args['offset']);

		$sql = $wpdb->prepare(
			"SELECT * FROM {$wpdb->prefix}posts WHERE {$where_conditions} ORDER BY {$order_by} {$order} LIMIT %d, %d",
			$offset,
			$limit_plus_one
		);
		//phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
		$media = $wpdb->get_results($sql);

		return $media;
	}

	/**
	 * Get the count of total posts
	 *
	 * @param string $media_type normal.
	 * @return int
	 */
	private function bx_media_count($media_type = 'normal')
	{
		global $wpdb;

		$post_status_condition = $media_type === 'normal' ? $wpdb->prepare('post_status<>%s', 'trash') : $wpdb->prepare('post_status=%s', 'trash');

		$query = $wpdb->prepare(
			"SELECT COUNT(id) FROM {$wpdb->prefix}posts WHERE post_type = %s AND ",
			'attachment',
		) . $post_status_condition;
		//phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
		$count = (int) $wpdb->get_var($query);

		return $count;
	}

	/**
	 * Get the count of total media category items
	 *
	 * @param string $mime_types file type.
	 * @return int
	 */
	private function bx_media_category_count($mime_types)
	{
		global $wpdb;
		$query = $wpdb->prepare(
			"SELECT COUNT(id) FROM {$wpdb->prefix}posts WHERE post_status <> %s",
			'trash'
		) . ' AND post_mime_type IN (' . $mime_types . ')';
		//phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
		$count = (int) $wpdb->get_var($query);
		return $count;
	}

	/**
	 * Set post status to trash of a media item
	 *
	 * @param    integer $post_id    post id of the item.
	 *
	 * @return   integer
	 */
	private function bx_trash_restore_media($post_id)
	{
		$post = get_post($post_id);

		if (!$post) {
			return -1;
		}

		$new_post_status = $post->post_status === 'trash' ? 'publish' : 'trash';

		$args = array(
			'ID' => $post_id,
			'post_status' => $new_post_status,
		);
		$post_id = wp_update_post($args);

		if (!$post_id) {
			return -1;
		}

		return $post_id;
	}
}
