<?php
/**
 * REST API Kadence Starter Templates Rest.
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}
/**
 * REST API Starter controller class.
 */
class Kadence_Cloud_Rest_Controller extends WP_REST_Controller {

	/**
	 * Include property name.
	 */
	const PROP_KEY = 'key';

	/**
	 * Include property name.
	 */
	const PROP_READ_ONLY = 'read';

	/**
	 * Include property name.
	 */
	const PROP_SITE = 'site';

	/**
	 * Include property name.
	 */
	const PROP_DATA = 'data';

	/**
	 * Include property name.
	 */
	const PROP_ID = 'id';

	/**
	 * Include property name.
	 */
	const PROP_IDS = 'ids';

	/**
	 * Include property name.
	 */
	const PROP_TYPE = 'type';

	/**
	 * Include property name.
	 */
	const PROP_STYLE = 'style';

	/**
	 * Include property name.
	 */
	const PROP_RETURN_COUNT = 'items';

	/**
	 * Include property name.
	 */
	const PROP_RETURN_PAGE = 'page';

	/**
	 * Include property name.
	 */
	const PROP_BETA = 'beta';

	/**
	 * Include property name.
	 */
	const PROP_RELOAD = 'reload';

	/**
	 * Include property name.
	 */
	const PROP_META = 'meta';
	/**
	 * Rest Namespace.
	 *
	 * @var string
	 */
	public $namespace = '';

	/**
	 * Rest Get Base.
	 *
	 * @var string
	 */
	public $rest_base = '';
	/**
	 * Rest Info Base.
	 *
	 * @var string
	 */
	public $info_base = '';
	/**
	 * Rest Single Base.
	 *
	 * @var string
	 */
	public $single_base = '';

	/**
	 * Rest Single Pattern Base.
	 *
	 * @var string
	 */
	public $single_pattern = '';

	/**
	 * Rest Single Pattern Base.
	 *
	 * @var string
	 */
	public $patterns = '';

	/**
	 * Rest Category Base.
	 *
	 * @var string
	 */
	public $category_base = '';

	/**
	 * Rest Category Base.
	 *
	 * @var string
	 */
	public $search_base = '';
	/**
	 * Posts to process
	 *
	 * @var array
	 */
	private $processed_posts = [];
	/**
	 * Flag blocks that are CPTs.
	 *
	 * @var array
	 */
	protected $is_cpt_block = [
		'kadence/navigation',
		'kadence/header',
		'kadence/advanced-form',
		'kadence/query',
		'kadence/query-card',
	];



	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->namespace      = 'kadence-cloud/v1';
		$this->rest_base      = 'get';
		$this->info_base      = 'info';
		$this->single_base    = 'single';
		$this->single_pattern = 'single_pattern';
		$this->patterns       = 'patterns';
		$this->category_base  = 'categories';
		$this->search_base    = 'search';
	}

	/**
	 * Registers the routes for the objects of the controller.
	 *
	 * @see register_rest_route()
	 */
	public function register_routes() {
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base,
			[
				[
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => [ $this, 'get_items' ],
					'permission_callback' => '__return_true',
					'args'                => $this->get_collection_params(),
				],
			]
		);
		register_rest_route(
			$this->namespace,
			'/' . $this->info_base,
			[
				[
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => [ $this, 'get_info' ],
					'permission_callback' => '__return_true',
					'args'                => $this->get_collection_params(),
				],
			]
		);
		register_rest_route(
			$this->namespace,
			'/' . $this->category_base,
			[
				[
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => [ $this, 'get_categories' ],
					'permission_callback' => '__return_true',
					'args'                => $this->get_collection_params(),
				],
			]
		);
		register_rest_route(
			$this->namespace,
			'/' . $this->single_base,
			[
				[
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => [ $this, 'get_single' ],
					'permission_callback' => '__return_true',
					'args'                => $this->get_analytics_params(),
				],
			]
		);
		register_rest_route(
			$this->namespace,
			'/' . $this->single_pattern,
			[
				[
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => [ $this, 'get_single_pattern' ],
					'permission_callback' => '__return_true',
					'args'                => $this->get_analytics_params(),
				],
			]
		);
		register_rest_route(
			$this->namespace,
			'/' . $this->patterns,
			[
				[
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => [ $this, 'get_patterns' ],
					'permission_callback' => '__return_true',
					'args'                => $this->get_collection_params(),
				],
			]
		);
		register_rest_route(
			$this->namespace,
			'/' . $this->search_base,
			[
				[
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => [ $this, 'get_search' ],
					'permission_callback' => '__return_true',
					'args'                => $this->get_collection_params(),
				],
			]
		);
	}
	/**
	 * Disable the emoji's
	 */
	public function disable_emojis() {
		remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
		remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
		remove_action( 'wp_print_styles', 'print_emoji_styles' );
		remove_action( 'admin_print_styles', 'print_emoji_styles' ); 
		remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
		remove_filter( 'comment_text_rss', 'wp_staticize_emoji' ); 
		remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
	}
	/**
	 * Retrieves a collection of objects.
	 *
	 * @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.
	 */
	public function get_single( $request ) {
		if ( $this->check_access( $request ) ) {
			$settings          = json_decode( get_option( 'kadence_cloud' ), true );
			$pattern_id        = $request->get_param( self::PROP_ID );
			$pattern_read_only = $request->get_param( self::PROP_READ_ONLY );
			$pattern_data      = $request->get_param( self::PROP_DATA );
			$pattern_is_read   = ( ! empty( $pattern_read_only ) && 'true' === $pattern_read_only ? true : false );
			$pattern_is_data   = ( $pattern_data === true ? true : false );
			if ( empty( $pattern_id ) ) {
				return rest_ensure_response( new WP_Error( 'invalid_request', __( 'Invalid Request, Missing Pattern ID', 'kadence-cloud' ), [ 'status' => 400 ] ) );
			}
			if ( ! $pattern_is_read && isset( $settings['enable_analytics'] ) && $settings['enable_analytics'] ) {
				$pattern_type  = $request->get_param( self::PROP_TYPE );
				$pattern_style = $request->get_param( self::PROP_STYLE );
				$key           = $request->get_param( self::PROP_KEY );
				if ( empty( $pattern_id ) || empty( $pattern_type ) ) {
					return rest_ensure_response( new WP_Error( 'invalid_request', __( 'Invalid Request, Incorrect Pattern Data', 'kadence-cloud' ), [ 'status' => 400 ] ) );
				}
				$data = [
					'type'    => $pattern_type,
					'post_id' => absint( $pattern_id ),
					'style'   => ! empty( $pattern_style ) ? $pattern_style : 'light',
				];
				KadenceWP\KadenceCloud\Analytics_Dashboard_Util::record_event( $data );
			}
			$pattern = $this->get_pattern( $pattern_id, $pattern_is_read, $pattern_is_data );
			if ( ! $pattern ) {
				return rest_ensure_response( new WP_Error( 'invalid_request', __( 'Failed Request, Missing Pattern', 'kadence-cloud' ), [ 'status' => 400 ] ) );
			}
			return rest_ensure_response( $pattern );

		} else {
			return rest_ensure_response( new WP_Error( 'invalid_access', __( 'Invalid Request, Incorrect Access Key', 'kadence-cloud' ), [ 'status' => 401 ] ) );
		}
	}
	/**
	 * Retrieves a collection of objects.
	 *
	 * @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.
	 */
	public function get_single_pattern( $request ) {
		if ( $this->check_access( $request ) ) {
			$settings   = json_decode( get_option( 'kadence_cloud' ), true );
			$pattern_id = $request->get_param( self::PROP_ID );
			if ( empty( $pattern_id ) ) {
				return rest_ensure_response( new WP_Error( 'invalid_request', __( 'Invalid Request, Missing Pattern ID', 'kadence-cloud' ), [ 'status' => 400 ] ) );
			}
			$request_extras = apply_filters( 'kadence_cloud_rest_request_extras', [], $request );
			$args           = [
				'post_type'   => 'kadence_cloud',
				'post_status' => 'publish',
				'p'           => $pattern_id,
			];
			$templates      = new WP_Query( $args );
			$pattern        = $this->get_template_array( $templates, $request_extras );
			if ( ! $pattern ) {
				return rest_ensure_response( new WP_Error( 'invalid_request', __( 'Failed Request, Missing Pattern', 'kadence-cloud' ), [ 'status' => 400 ] ) );
			}
			return rest_ensure_response( $pattern );

		} else {
			return rest_ensure_response( new WP_Error( 'invalid_request', __( 'Invalid Request, Incorrect Access Key', 'kadence-cloud' ), [ 'status' => 400 ] ) );
		}
	}

	/**
	 * Retrieves a collection of objects.
	 *
	 * @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.
	 */
	public function get_search( $request ) {
		if ( $this->check_access( $request ) ) {
			$settings     = json_decode( get_option( 'kadence_cloud' ), true );
			$search_term  = $request->get_param( self::PROP_TYPE );
			if ( empty( $search_term ) ) {
				return rest_ensure_response( new WP_Error( 'invalid_request', __( 'Invalid Request, Missing Search Term', 'kadence-cloud' ), [ 'status' => 400 ] ) );
			}
			$request_extras = apply_filters( 'kadence_cloud_rest_request_extras', [], $request );
			$args           = [
				'post_type'   => 'kadence_cloud',
				'post_status' => 'publish',
				's'           => $search_term,
			];
			$templates      = new WP_Query( $args );
			$patterns       = $this->get_template_array( $templates, $request_extras );
			return rest_ensure_response( $patterns );

		} else {
			return rest_ensure_response( new WP_Error( 'invalid_request', __( 'Invalid Request, Incorrect Access Key', 'kadence-cloud' ), [ 'status' => 400 ] ) );
		}
	}
	/**
	 * Retrieves a collection of objects.
	 *
	 * @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.
	 */
	public function get_patterns( $request ) {
		if ( $this->check_access( $request ) ) {
			$settings    = json_decode( get_option( 'kadence_cloud' ), true );
			$pattern_ids = $request->get_param( self::PROP_IDS );
			$ids         = array_map( 'intval', explode( ',', $pattern_ids ) );
			if ( empty( $pattern_ids ) ) {
				return rest_ensure_response( new WP_Error( 'invalid_request', __( 'Invalid Request, Missing Pattern IDs', 'kadence-cloud' ), [ 'status' => 400 ] ) );
			}
			$request_extras = apply_filters( 'kadence_cloud_rest_request_extras', [], $request );
			$args           = [
				'post_type'   => 'kadence_cloud',
				'post_status' => 'publish',
				'post__in'    => $ids,
			];
			$templates      = new WP_Query( $args );
			$pattern        = $this->get_template_array( $templates, $request_extras );
			if ( ! $pattern ) {
				return rest_ensure_response( new WP_Error( 'invalid_request', __( 'Failed Request, Missing Pattern', 'kadence-cloud' ), [ 'status' => 400 ] ) );
			}
			return rest_ensure_response( $pattern );

		} else {
			return rest_ensure_response( new WP_Error( 'invalid_request', __( 'Invalid Request, Incorrect Access Key', 'kadence-cloud' ), [ 'status' => 400 ] ) );
		}
	}
	/**
	 * Prepare post for export
	 */
	private function prepare_post_for_export( $post_id ) {
		if ( empty( $post_id ) ) {
			return '';
		}
		$post = get_post( $post_id );
		if ( ! $post ) {
			return '';
		}
		
		$post_data = [
			'ID'           => $post->ID,
			'post_content' => $post->post_content,
			'post_title'   => $post->post_title,
			'post_excerpt' => $post->post_excerpt,
			'post_type'    => $post->post_type,
			'meta'         => [],
			'inner_posts'  => [],
		];
		$post_data['inner_posts'] = $this->get_inner_posts_recursive( $post->post_content );
		$meta = get_post_custom( $post->ID );
		foreach ( $meta as $key => $values ) {
			// Check if key starts with _kad.
			if ( strpos( $key, '_kad' ) === 0 ) {
				$post_data['meta'][ $key ] = array_map( 'maybe_unserialize', $values );
			}
		}

		return $post_data;
	}
	/**
	 * Recursively get related posts
	 */
	private function get_inner_posts_recursive( $content, $inner_posts = [] ) {
		$blocks = parse_blocks($content);

		foreach ($blocks as $block) {
			if( in_array( $block['blockName'], $this->is_cpt_block ) && !empty( $block['attrs']['id'] ) ) {
				$nested_id = $block['attrs']['id'];

				if ( ! in_array( $nested_id, $this->processed_posts ) ) {
					$nested_post = get_post( $nested_id );
					if ( $nested_post ) {
						$inner_posts[] = $this->prepare_post_for_export( $nested_post );
						$this->processed_posts[] = $nested_id;
						$inner_posts = $this->get_inner_posts_recursive($nested_post->post_content, $inner_posts);
					}
				}
			}

			if (!empty($block['innerBlocks'])) {
				foreach ($block['innerBlocks'] as $inner_block) {
					$inner_posts = $this->get_inner_posts_recursive(serialize_blocks(array($inner_block)), $inner_posts);
				}
			}
		}
		return $inner_posts;
	}
	/**
	 * Retrieves a collection of objects.
	 */
	public function get_cpt_blocks( $content ) {
		$cpt_blocks = [];
		$blocks     = parse_blocks( $content );
		foreach ( $blocks as $block ) {
			if ( in_array( $block['blockName'], $this->is_cpt_block, true ) ) {
				$cpt_blocks = $this->process_content_for_export( $block, $cpt_blocks );
			}
			if ( ! empty( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) {
				$cpt_blocks = $this->blocks_cycle_through( $block['innerBlocks'], $cpt_blocks );
			}
		}
		return $cpt_blocks;
	}
	/**
	 * Retrieves a collection of objects.
	 */
	public function blocks_cycle_through( $blocks, $cpt_blocks ) {
		foreach ( $blocks as $block ) {
			if ( in_array( $block['blockName'], $this->is_cpt_block, true ) ) {
				$cpt_blocks = $this->process_content_for_export( $block, $cpt_blocks );
			}
			if ( ! empty( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) {
				$cpt_blocks = $this->blocks_cycle_through( $block['innerBlocks'], $cpt_blocks );
			}
		}
		return $cpt_blocks;
	}
	/**
	 * Process content for export
	 */
	private function process_content_for_export( $block, $cpt_blocks ) {
		switch ( $block['blockName'] ) {
			case 'kadence/advanced-form':
				if ( ! empty( $block['attrs']['id'] ) && ! in_array( $block['attrs']['id'], $this->processed_posts ) ) {
					$this->processed_posts[] = $block['attrs']['id'];
					$content = $this->prepare_post_for_export( $block['attrs']['id'] );
					if ( ! empty( $content ) ) {
						$cpt_blocks['kadence/advanced-form'][] = $content;
					}
				}
				break;
			case 'kadence/header':
				if ( ! empty( $block['attrs']['id'] ) && ! in_array( $block['attrs']['id'], $this->processed_posts ) ) {
					$this->processed_posts[] = $block['attrs']['id'];
					$content = $this->prepare_post_for_export( $block['attrs']['id'] );
					if ( ! empty( $content ) ) {
						$cpt_blocks['kadence/header'][] = $content;
					}
				}
				break;
			case 'kadence/navigation':
				if ( ! empty( $block['attrs']['id'] ) && ! in_array( $block['attrs']['id'], $this->processed_posts ) ) {
					$this->processed_posts[] = $block['attrs']['id'];
					$content = $this->prepare_post_for_export( $block['attrs']['id'] );
					if ( ! empty( $content ) ) {
						$cpt_blocks['kadence/navigation'][] = $content;
					}
				}
				break;
			case 'kadence/query':
				if ( ! empty( $block['attrs']['id'] ) && ! in_array( $block['attrs']['id'], $this->processed_posts ) ) {
					$this->processed_posts[] = $block['attrs']['id'];
					$content = $this->prepare_post_for_export( $block['attrs']['id'] );
					if ( ! empty( $content ) ) {
						$cpt_blocks['kadence/query'][] = $content;
					}
				}
				break;	
			case 'kadence/query-card':
				if ( ! empty( $block['attrs']['id'] ) && ! in_array( $block['attrs']['id'], $this->processed_posts ) ) {
					$this->processed_posts[] = $block['attrs']['id'];
					$content = $this->prepare_post_for_export( $block['attrs']['id'] );
					if ( ! empty( $content ) ) {
						$cpt_blocks['kadence/query-card'][] = $content;
					}
				}
				break;
		}
		return $cpt_blocks;
	}

	/**
	 * Get Pattern content from pattern id.
	 * 
	 * @param string $pattern_id the pattern id.
	 * @param bool   $pattern_is_read is the pattern is read mode.
	 * @param bool   $pattern_is_data is the pattern is data mode.
	 */
	public function get_pattern( $pattern_id, $pattern_is_read, $pattern_is_data ) {
		$pattern = get_post( $pattern_id );
		if ( ! empty( $pattern ) ) {
			if ( $pattern_is_read ) {
				$pattern_content = $this->get_pattern_html_content( $pattern );
			} elseif ( $pattern_is_data ) {
				$pattern_content = [
					'content' => $pattern->post_content,
					'cpt_blocks'     => $this->get_cpt_blocks( $pattern->post_content ),
					'id'      => $pattern_id,
				];
			} else {
				$pattern_content = $pattern->post_content;
			}
			if ( ! empty( $pattern_content ) ) {
				return $pattern_content;
			}
		}
		return false;
	}

	/**
	 * Get Pattern html content from pattern object.
	 * 
	 * @param object $post the pattern post object.
	 */
	public function get_pattern_html_content( $post ) {
		global $wp_styles, $wp_scripts;
		$this->disable_emojis();
		$wp_styles  = new WP_Styles(); // phpcs:ignore
		$wp_scripts = new WP_Scripts(); // phpcs:ignore
		if ( class_exists( 'Kadence_Blocks_CSS' ) ) {
			$kadence_blocks_css = \Kadence_Blocks_CSS::get_instance();
			if ( method_exists( $kadence_blocks_css, 'frontend_block_css' ) ) {
				$kadence_blocks_css::$styles        = [];
				$kadence_blocks_css::$head_styles   = [];
				$kadence_blocks_css::$custom_styles = [];
			}
		}
		if ( class_exists( 'Kadence_Blocks_Frontend' ) ) {
			$kadence_blocks = \Kadence_Blocks_Frontend::get_instance();
			if ( method_exists( $kadence_blocks, 'frontend_build_css' ) ) {
				$kadence_blocks->frontend_build_css( $post );
			}
			if ( class_exists( 'Kadence_Blocks_Pro_Frontend' ) ) {
				$kadence_blocks_pro = \Kadence_Blocks_Pro_Frontend::get_instance();
				if ( method_exists( $kadence_blocks_pro, 'frontend_build_css' ) ) {
					$kadence_blocks_pro->frontend_build_css( $post );
				}
			}
		}
		if ( class_exists( 'Kadence_Blocks_CSS' ) ) {
			$kadence_blocks_css = \Kadence_Blocks_CSS::get_instance();
			if ( method_exists( $kadence_blocks_css, 'frontend_block_css' ) ) {
				$kadence_blocks_css->frontend_block_css();
			}
		}
		do_action( 'kadence_cloud_before_post_html_content', $post );
		$block_content = apply_filters( 'kadence_cloud_post_html_content_before_do_blocks', $post->post_content, $post, $request_extras );
		ob_start();
		wp_print_styles();
		echo do_blocks( $block_content );  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		$wp_scripts->print_scripts();
		return ob_get_clean();
	}
	
	/**
	 * Retrieves a collection of objects.
	 *
	 * @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.
	 */
	public function get_categories( $request ) {
		if ( $this->check_access( $request ) ) {
			$terms       = get_terms(
				[
					'taxonomy'   => 'kadence-cloud-categories',
					'hide_empty' => false,
					'orderby'    => 'menu_order',
					'order'      => 'ASC',
				]
			);
			$terms_array = [];
			if ( ! empty( $terms ) ) {
				foreach ( $terms as $key => $value ) {
					$terms_array[ $value->slug ] = $value->name;
				}
			}
			return rest_ensure_response( $terms_array );
		} else {
			return wp_send_json( __( 'Invalid Request, Incorrect Access Key', 'kadence-cloud' ) );
		}
	}
	/**
	 * Retrieves a collection of objects.
	 *
	 * @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.
	 */
	public function get_info( $request ) {
		if ( $this->check_access( $request ) ) {
			$settings = json_decode( get_option( 'kadence_cloud' ), true );
			if ( isset( $settings['expires'] ) && ! empty( $settings['expires'] ) ) {
				if ( 'day' === $settings['expires'] ) {
					$expires = DAY_IN_SECONDS;
				} elseif ( 'week' === $settings['expires'] ) {
					$expires = WEEK_IN_SECONDS;
				} else {
					$expires = MONTH_IN_SECONDS;
				}
			} else {
				$expires = MONTH_IN_SECONDS;
			}
			$key  = $request->get_param( self::PROP_KEY );
			$name = ( isset( $settings['cloud_name'] ) && ! empty( $settings['cloud_name'] ) ? wp_kses_post( $settings['cloud_name'] ) : wp_kses_post( get_bloginfo( 'name' ) ) );

			// Find if collection has been set as pattern hub name.
			if ( isset( $settings['access_keys'] ) ) {
				foreach ( $settings['access_keys'] as $access_key ) {
					if ( ! isset( $access_key['useAsPatternHubName'] ) ) {
						continue;
					}

					if ( ! isset( $access_key['key'] ) ) {
						continue;
					}

					if ( ! isset( $access_key['collections'] ) && empty( $access_key['collections'] ) ) {
						continue;
					}

					if ( $access_key['key'] === $key && $access_key['useAsPatternHubName'] === true ) {
						$collection = get_term_by( 'id', $access_key['collections'], 'kadence-cloud-collections' );
						if ( isset( $collection->name ) && ! empty( $collection->name ) ) {
							$name = $collection->name;
						}
					}
				}
			}

			$info = [
				'name'    => $name,
				'slug'    => sanitize_title( md5( get_bloginfo( 'url' ) . $key ) ),
				'refresh' => ( isset( $settings['expires'] ) && ! empty( $settings['expires'] ) ? wp_kses_post( $settings['expires'] ) : 'month' ),
				'expires' => gmdate( 'Y-m-d H:i:s', strtotime( current_time( 'mysql' ) ) + $expires ),
			];
			$info = apply_filters( 'kadence_cloud_library_info_args', $info, $request );
			return wp_send_json( $info );

		} else {
			return wp_send_json( __( 'Invalid Request, Incorrect Access Key', 'kadence-cloud' ) );
		}
	}

	/**
	 * Retrieves a collection of objects.
	 *
	 * @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.
	 */
	public function get_items( $request ) {
		$key         = $request->get_param( self::PROP_KEY );
		$per_page    = $request->get_param( self::PROP_RETURN_COUNT );
		$page_number = $request->get_param( self::PROP_RETURN_PAGE );
		$meta        = $request->get_param( self::PROP_META );
		$request_extras = [];
		if ( ! empty( $meta ) && 'info' === $meta ) {
			$request_extras['meta_only'] = true;
		}
		if ( ! empty( $meta ) && 'html' === $meta ) {
			$request_extras['content_only'] = true;
		}

		if ( $this->check_access( $request ) ) {
			$request_extras = apply_filters( 'kadence_cloud_rest_request_extras', $request_extras, $request );
			$reload         = $request->get_param( self::PROP_RELOAD );
			if ( ! empty( $reload ) ) {
				$reload = true;
			} else {
				$reload = false;
			}
			if ( ! empty( $per_page ) ) {
				$per_page = absint( $per_page );
			} else {
				$per_page = -1;
			}
			if ( ! empty( $page_number ) ) {
				$page_number = absint( $page_number );
			} else {
				$page_number = 1;
			}
			add_filter( 'kadence_blocks_css_output_media_queries', '__return_false' );
			add_filter( 'kadence-blocks-countup-static', '__return_true' );
			add_filter( 'kadence-blocks-progress-bar-static', '__return_true' );
			add_filter( 'kadence_blocks_post_block_style_force_output', '__return_true' );
			return $this->get_templates( $per_page, $page_number, $key, $reload, $request_extras );

		} else {
			return wp_send_json( __( 'Invalid Request, Incorrect Access Key', 'kadence-cloud' ) );
		}
	}
	/**
	 * Check if the request should get access to the files.
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @return Boolean true or false based on if access should be granted.
	 */
	public function check_access( $request ) {
		$access   = false;
		$key      = $request->get_param( self::PROP_KEY );
		$keys     = [];
		$settings = json_decode( get_option( 'kadence_cloud' ), true );
		if ( isset( $settings['access_keys'] ) && ! empty( $settings['access_keys'] ) && is_array( $settings['access_keys'] ) ) {
			$access_keys = [];
			if ( isset( $settings['access_keys'][0] ) && is_array( $settings['access_keys'][0] ) ) {
				foreach ( $settings['access_keys'] as $the_key => $the_keys ) {
					if ( $the_keys['key'] === $key ) {
						$access = true;
						break;
					}
				}
			} else {
				$access_keys = $settings['access_keys'];
				if ( is_array( $access_keys ) && ! empty( $access_keys ) && in_array( $key, $access_keys ) ) {
					$access = true;
				}
			}
		}
		return apply_filters( 'kadence_cloud_rest_request_access', $access, $key, $request );
	}
	/**
	 * Retrieves a collection of objects.
	 */
	public function get_template_array( $template_query, $request_extras ) {
		$library             = [];
		$fallback_image_args = apply_filters(
			'kadence_cloud_post_fallback_image_args',
			[
				'w' => intval( 1280 ),
				'h' => intval( 800 ),
			]
		);
		$just_meta = $request_extras['meta_only'] ?? false;
		$content_only = $request_extras['content_only'] ?? false;
		if ( $template_query->have_posts() ) :
			while ( $template_query->have_posts() ) :
				$template_query->the_post();
				global $post;
				$slug = 'ptn-' . get_the_ID();
				if ( $slug ) {
					$library[ $slug ]         = [];
					if ( ! $just_meta ) {
						if ( apply_filters( 'kadence_cloud_post_send_content', true, $post, $request_extras ) ) {
							$library[ $slug ]['content'] = $post->post_content;
							$cpt_blocks = $this->get_cpt_blocks( $post->post_content );
							$library[ $slug ]['cpt_blocks'] = $cpt_blocks;
						}
						if ( apply_filters( 'kadence_cloud_post_send_html_content', false, $post, $request_extras ) ) {
							global $wp_styles, $wp_scripts;
							$this->disable_emojis();
							$wp_styles  = new WP_Styles();
							$wp_scripts = new WP_Scripts();
							if ( class_exists( 'Kadence_Blocks_CSS' ) ) {
								$kadence_blocks_css = \Kadence_Blocks_CSS::get_instance();
								if ( method_exists( $kadence_blocks_css, 'frontend_block_css' ) ) {
									$kadence_blocks_css::$styles        = [];
									$kadence_blocks_css::$head_styles   = [];
									$kadence_blocks_css::$custom_styles = [];
								}
							}
							if ( class_exists( 'Kadence_Blocks_Frontend' ) ) {
								$kadence_blocks = \Kadence_Blocks_Frontend::get_instance();
								if ( method_exists( $kadence_blocks, 'frontend_build_css' ) ) {
									$kadence_blocks->frontend_build_css( $post );
								}
								if ( class_exists( 'Kadence_Blocks_Pro_Frontend' ) ) {
									$kadence_blocks_pro = \Kadence_Blocks_Pro_Frontend::get_instance();
									if ( method_exists( $kadence_blocks_pro, 'frontend_build_css' ) ) {
										$kadence_blocks_pro->frontend_build_css( $post );
									}
								}
							}
							if ( class_exists( 'Kadence_Blocks_CSS' ) ) {
								$kadence_blocks_css = \Kadence_Blocks_CSS::get_instance();
								if ( method_exists( $kadence_blocks_css, 'frontend_block_css' ) ) {
									$kadence_blocks_css->frontend_block_css();
								}
							}
							do_action( 'kadence_cloud_before_post_html_content', $post );
							$block_content = apply_filters( 'kadence_cloud_post_html_content_before_do_blocks', $post->post_content, $post, $request_extras );
							ob_start();
							wp_print_styles();
							echo do_blocks( $block_content );  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
							$wp_scripts->print_scripts();
							$library[ $slug ]['html'] = ob_get_clean();
						}
					}
					if ( $content_only ) {
						continue;
					}
					$library[ $slug ]['slug'] = $slug;
					$library[ $slug ]['id']   = get_the_ID();
					$library[ $slug ]['name'] = the_title_attribute( 'echo=0' );
					$terms_array              = [];
					if ( get_the_terms( $post, 'kadence-cloud-categories' ) ) {
						$terms = get_the_terms( $post, 'kadence-cloud-categories' );
						if ( is_array( $terms ) ) {
							foreach ( $terms as $key => $value ) {
								$terms_array[ $value->slug ] = $value->name;
							}
						}
					}
					$library[ $slug ]['categories'] = $terms_array;
					$keywords_array                 = [];
					if ( get_the_terms( $post, 'kadence-cloud-keywords' ) ) {
						$terms = get_the_terms( $post, 'kadence-cloud-keywords' );
						if ( is_array( $terms ) ) {
							foreach ( $terms as $key => $value ) {
								$keywords_array[] = $value->name;
							}
						}
					}
					$library[ $slug ]['keywords'] = $keywords_array;
					if ( ! empty( $request_extras ) && is_array( $request_extras ) ) {
						foreach ( $request_extras as $key => $data ) {
							$library[ $slug ][ $key ] = $data;
						}
					}
					$post_extras = apply_filters( 'kadence_cloud_post_extra_args', [], $post, $request_extras );
					if ( ! empty( $post_extras ) && is_array( $post_extras ) ) {
						foreach ( $post_extras as $key => $data ) {
							$library[ $slug ][ $key ] = $data;
						}
					}
					$library[ $slug ]['pro']    = apply_filters( 'kadence_cloud_post_is_pro', false, $post, $request_extras );
					$library[ $slug ]['locked'] = apply_filters( 'kadence_cloud_post_is_locked', false, $post, $request_extras );
					$library[ $slug ]['description'] = apply_filters( 'kadence_cloud_post_description', $post->post_excerpt, $post, $request_extras );
					$image_meta                      = get_post_meta( get_the_ID(), '_kc_preview_image', true );
					if ( has_post_thumbnail() ) {
						$image                      = wp_get_attachment_image_src( get_post_thumbnail_id( $post ), 'full' );
						$library[ $slug ]['image']  = $image[0];
						$library[ $slug ]['imageW'] = $image[1];
						$library[ $slug ]['imageH'] = $image[2];
					} elseif ( ! empty( $image_meta['url'] ) ) {
						$library[ $slug ]['image']  = $image_meta['url'];
						$library[ $slug ]['imageW'] = $image_meta['width'];
						$library[ $slug ]['imageH'] = $image_meta['height'];
					} else {
						$library[ $slug ]['image']  = add_query_arg( $fallback_image_args, 'https://s0.wordpress.com/mshots/v1/' . rawurlencode( esc_url( get_permalink() ) ) );
						$library[ $slug ]['imageW'] = $fallback_image_args['w'];
						$library[ $slug ]['imageH'] = $fallback_image_args['h'];
					}
					
					$library[ $slug ] = apply_filters( 'kadence_cloud_individual_library_item_data', $library[ $slug ], $post, $request_extras );
				}
			endwhile;
		endif;
		return $library;
	}
	/**
	 * Get Forms from ids.
	 *
	 * @param array $form_ids array of form ids.
	 */
	public function get_forms( $form_ids ) {
		$forms = [];
		foreach ( $form_ids as $form_id ) {
			$form = get_post( $form_id );
			if ( ! empty( $form ) && 'kadence_form' === $form->post_type ) {
				$forms[ $form_id ] = [
					'id'      => $form_id,
					'name'    => $form->post_title,
					'content' => $form->post_content,
					'meta'    => [],
				];
				$meta              = get_post_meta( $form_id );
				foreach ( $meta as $key => $value ) {
					if ( 0 === strpos( $key, '_kad_form_' ) ) {
						$forms[ $form_id ]['meta'][ $key ] = maybe_unserialize( $value[0] );
					}
				}
			}
		}
		return $forms;
	}
	/**
	 * Check if pattern has Advanced form
	 */
	public function has_form( $content ) {
		$blocks = parse_blocks( $content );
		if ( ! is_array( $blocks ) || empty( $blocks ) ) {
			return [];
		}
		$form_ids = [];
		foreach ( $blocks as $indexkey => $block ) {
			if ( ! is_object( $block ) && is_array( $block ) && isset( $block['blockName'] ) ) {
				if ( 'kadence/advanced-form' === $block['blockName'] ) {
					if ( isset( $block['attrs'] ) && is_array( $block['attrs'] ) ) {
						$blockattr = $block['attrs'];
						if ( isset( $blockattr['id'] ) ) {
							$form_ids[] = $blockattr['id'];
						}
					}
				}
				if ( ! empty( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) {
					$forms_to_add = $this->blocks_form_cycle_through( $block['innerBlocks'] );
					if ( ! empty( $forms_to_add ) ) {
						$form_ids = array_merge( $form_ids, $forms_to_add );
					}
				}
			}
		}
		return $form_ids;
	}
	/**
	 * Builds css for inner blocks
	 *
	 * @param array $inner_blocks array of inner blocks.
	 */
	public function blocks_form_cycle_through( $inner_blocks ) {
		$forms_to_add = [];
		foreach ( $inner_blocks as $in_indexkey => $inner_block ) {
			if ( 'kadence/advanced-form' === $inner_block['blockName'] ) {
				if ( isset( $inner_block['attrs'] ) && is_array( $inner_block['attrs'] ) ) {
					$blockattr = $inner_block['attrs'];
					if ( isset( $blockattr['id'] ) ) {
						$forms_to_add[] = $blockattr['id'];
					}
				}
			}
			if ( ! empty( $inner_block['innerBlocks'] ) && is_array( $inner_block['innerBlocks'] ) ) {
				$other_forms_to_add = $this->blocks_form_cycle_through( $inner_block['innerBlocks'] );
				if ( ! empty( $other_forms_to_add ) ) {
					$forms_to_add = array_merge( $forms_to_add, $other_forms_to_add );
				}
			}
		}
		return $forms_to_add;
	}
	/**
	 * Retrieves a collection of objects.
	 */
	public function get_templates( $post_per_page = -1, $page_number = 1, $key = '', $reload = false, $request_extras = [] ) {
		$args     = [
			'post_type'      => 'kadence_cloud',
			'post_status'    => 'publish',
			'posts_per_page' => $post_per_page,
			'order'          => 'ASC',
			'meta_key'       => apply_filters( 'kadence_cloud_order_by_popular', false ) ? '_kc_import_count' : '',
			'orderby'        => apply_filters( 'kadence_cloud_order_by_popular', false ) ? 'meta_value_num' : 'menu_order',
			'offset'         => ( 1 < $page_number && -1 !== $post_per_page ? ( $page_number * $post_per_page ) : 0 ),
		];
		$settings = json_decode( get_option( 'kadence_cloud' ), true );
		if ( isset( $settings['access_keys'] ) && ! empty( $settings['access_keys'] ) && is_array( $settings['access_keys'] ) ) {
			if ( isset( $settings['access_keys'][0] ) && is_array( $settings['access_keys'][0] ) ) {
				foreach ( $settings['access_keys'] as $the_key => $the_keys ) {
					if ( $the_keys['key'] === $key ) {
						if ( isset( $the_keys['collections'] ) && ! empty( $the_keys['collections'] ) ) {
							$args['tax_query'] = [
								[
									'taxonomy' => 'kadence-cloud-collections',
									'field'    => 'id',
									'terms'    => explode( ',', $the_keys['collections'] ),
								],
							];
						}
						break;
					}
				}
			}
		}
		$args = apply_filters( 'kadence_cloud_template_query_args', $args, $key, $request_extras );
		if ( apply_filters( 'kadence_cloud_use_local_json_cache', false, $args, $reload, $key, $request_extras ) ) {
			// Do you have the data?
			$library = $this->get_template_data( $args, $reload, $request_extras );
			if ( ! $library ) {
				$templates = new WP_Query( $args );
				$library   = $this->get_template_array( $templates, $request_extras );
			} else {
				$library = json_decode( $library, true );
			}
		} else {
			$templates = new WP_Query( $args );
			$library   = $this->get_template_array( $templates, $request_extras );
		}

		return rest_ensure_response( $library );
	}
	/**
	 * Get the local data file if there, else query the api.
	 *
	 * @access public
	 * @return string
	 */
	public function get_template_data( $args, $reload = false, $request_extras = [] ) {
		$upload_dir = wp_upload_dir();
		$base_path  = trailingslashit( $upload_dir['basedir'] ) . 'kadence_cloud_stored_data';
		$just_meta = $request_extras['meta_only'] ?? false;
		$content_only = $request_extras['content_only'] ?? false;
		$file_name  = md5( $base_path . KADENCE_CLOUD_VERSION . implode( '-', $args ) );
		if ( $just_meta ) {
			$file_name .= '-meta';
		}
		if ( $content_only ) {
			$file_name .= '-content';
		}
		$path       = $base_path . '/' . $file_name . '.json';
		// Check if the local data file exists.
		if ( $reload || ! file_exists( $path ) ) {
			// Attempt to create the file.
			if ( $this->create_template_data_file( $base_path, $path, $args, $request_extras ) ) {
				return $this->get_local_template_data_contents( $path );
			}
		}
		// If the local file exists, return it's data.
		return file_exists( $path )
			? $this->get_local_template_data_contents( $path )
			: false;
	}
	/**
	 * Get local data contents.
	 *
	 * @access public
	 * @return string|false Returns the data contents.
	 */
	public function get_local_template_data_contents( $path ) {

		// Check if the local file is present.
		if ( ! file_exists( $path ) ) {
			return false;
		}

		ob_start();
		include $path;
		return ob_get_clean();
	}
	/**
	 * Write the data to the filesystem.
	 *
	 * @access protected
	 * @return string|false Returns the absolute path of the file on success, or false on fail.
	 */
	protected function create_template_data_file( $base_path, $path, $args, $request_extras ) {
		$filesystem = $this->get_filesystem();

		// If the folder doesn't exist, create it.
		if ( ! file_exists( $base_path ) ) {
			$chmod_dir = ( 0755 & ~ umask() );
			if ( defined( 'FS_CHMOD_DIR' ) ) {
				$chmod_dir = FS_CHMOD_DIR;
			}
			$this->get_filesystem()->mkdir( $base_path, $chmod_dir );
		}

		// If the file doesn't exist, create it. Return false if it can not be created.
		if ( ! $filesystem->exists( $path ) && ! $filesystem->touch( $path ) ) {
			return false;
		}

		// If we got this far, we need to write the file.
		// Get the data.
		$templates = new WP_Query( $args );
		$library   = $this->get_template_array( $templates, $request_extras );
		if ( ! $library ) {
			// No Data.
			return false;
		}
		// Put the contents in the file. Return false if that fails.
		if ( ! $filesystem->put_contents( $path, wp_json_encode( $library ) ) ) {
			return false;
		}

		return $path;
	}
	/**
	 * Get the filesystem.
	 *
	 * @access protected
	 * @return WP_Filesystem
	 */
	protected function get_filesystem() {
		global $wp_filesystem;

		// If the filesystem has not been instantiated yet, do it here.
		if ( ! $wp_filesystem ) {
			if ( ! function_exists( 'WP_Filesystem' ) ) {
				require_once wp_normalize_path( ABSPATH . '/wp-admin/includes/file.php' );
			}
			$credentials = apply_filters( 'kadence_wpfs_credentials', false );
			WP_Filesystem( $credentials );
		}
		return $wp_filesystem;
	}
	/**
	 * Extracts a string between two strings.
	 * 
	 * @param string $string The string to search.
	 * @param string $start The string to start with.
	 * @param string $end The string to end with.
	 * @param string $verify The string to verify.
	 */
	public function get_string_inbetween( $string, $start, $end, $verify ) {
		if ( strpos( $string, $verify ) == 0 ) {
			return '';
		}
		$ini = strpos( $string, $start );
		if ( $ini == 0 ) {
			return '';
		}
		$ini += strlen( $start );
		$len  = strpos( $string, $end, $ini ) - $ini;
		return substr( $string, $ini, $len );
	}
	/**
	 * Extracts a string between two strings when the verify is within.
	 * 
	 * @param string $string The string to search.
	 * @param string $start The string to start with.
	 * @param string $end The string to end with.
	 * @param string $verify The string to verify.
	 * @param string $from The string to start from.
	 */
	public function get_string_inbetween_when( $string, $start, $end, $verify, $from ) {
		if ( strpos( $string, $verify ) == 0 ) {
			return '';
		}
		$ini = strpos( $string, $start, $from );
		if ( $ini == 0 ) {
			return '';
		}
		$ini       += strlen( $start );
		$len        = strpos( $string, $end, $ini ) - $ini;
		$sub_string = substr( $string, $ini, $len );
		if ( strpos( $sub_string, $verify ) == 0 ) {
			return $this->get_string_inbetween_when( $string, $start, $end, $verify, $ini );
		}
		return $sub_string;
	}
	/**
	 * Retrieves the query params for the search results collection.
	 *
	 * @return array Collection parameters.
	 */
	public function get_collection_params() {
		$query_params = parent::get_collection_params();

		$query_params[ self::PROP_KEY ] = [
			'description' => __( 'The request key.', 'kadence-cloud' ),
			'type'        => 'string',
		];

		$query_params[ self::PROP_SITE ] = [
			'description' => __( 'The request website.', 'kadence-cloud' ),
			'type'        => 'string',
		];

		$query_params[ self::PROP_RETURN_COUNT ] = [
			'description' => __( 'Items to return.', 'kadence-cloud' ),
			'type'        => 'string',
		];

		$query_params[ self::PROP_RETURN_PAGE ] = [
			'description' => __( 'The Page to return.', 'kadence-cloud' ),
			'type'        => 'string',
		];
		$query_params[ self::PROP_BETA ]        = [
			'description' => __( 'Use the Beta Library', 'kadence-cloud' ),
			'type'        => 'string',
		];
		$query_params[ self::PROP_RELOAD ]      = [
			'description' => __( 'Force a cache reload', 'kadence-cloud' ),
			'type'        => 'string',
		];
		$query_params[ self::PROP_IDS ]  = [
			'description'       => __( 'The Ids to collect.', 'kadence-cloud' ),
			'validate_callback' => [ $this, 'validate_ids_param' ],
			'sanitize_callback' => [ $this, 'sanitize_ids_param' ],
		];
		$query_params[ self::PROP_META ] = [
			'description' => __( 'Determine the type of data to return.', 'kadence-cloud' ),
			'type'        => 'string',
		];

		return $query_params;
	}
	/**
	 * Validates the 'ids' parameter.
	 *
	 * Checks if the parameter contains only digits and commas.
	 *
	 * @param mixed           $param   The parameter value.
	 * @param WP_REST_Request $request The request object.
	 * @param string          $key     The parameter key.
	 * @return bool True if the parameter is valid, false otherwise.
	 */
	public function validate_ids_param( $param, $request, $key ) {
		return preg_match( '/^[\d,]+$/', $param );
	}
	/**
	 * Sanitizes the 'ids' parameter.
	 *
	 * Removes any non-digit or non-comma characters.
	 *
	 * @param mixed           $param   The parameter value.
	 * @param WP_REST_Request $request The request object.
	 * @param string          $key     The parameter key.
	 * @return string The sanitized parameter value.
	 */
	public function sanitize_ids_param( $param, $request, $key ) {
		return preg_replace( '/[^0-9,]/', '', $param );
	}

	/**
	 * Retrieves the query params for the search results collection.
	 *
	 * @return array Collection parameters.
	 */
	public function get_analytics_params() {
		$query_params = parent::get_collection_params();

		$query_params[ self::PROP_KEY ] = [
			'description' => __( 'The request key.', 'kadence-cloud' ),
			'type'        => 'string',
		];
		$query_params[ self::PROP_ID ]  = [
			'description' => __( 'The item id.', 'kadence-cloud' ),
			'type'        => 'string',
		];
		$query_params[ self::PROP_IDS ]  = [
			'description'       => __( 'The Ids to collect.', 'kadence-cloud' ),
			'validate_callback' => [ $this, 'validate_ids_param' ],
			'sanitize_callback' => [ $this, 'sanitize_ids_param' ],
		];
		$query_params[ self::PROP_TYPE ]      = [
			'description' => __( 'The Item Type.', 'kadence-cloud' ),
			'type'        => 'string',
		];
		$query_params[ self::PROP_READ_ONLY ] = [
			'description' => __( 'Use the if just viewing', 'kadence-cloud' ),
			'type'        => 'string',
		];
		$query_params[ self::PROP_DATA ] = [
			'description' => __( 'Return the item data.', 'kadence-cloud' ),
			'type'        => 'boolean',
			'default'     => false,
		];
		$query_params[ self::PROP_STYLE ]     = [
			'description' => __( 'The item style.', 'kadence-cloud' ),
			'type'        => 'string',
		];

		return $query_params;
	}
}
