<?php
/********************************************************************
 * Copyright (C) 2024 Darko Gjorgjijoski (https://darkog.com/)
 * Copyright (C) 2024 IDEOLOGIX MEDIA Dooel (https://ideologix.com/)
 *
 * This file is property of IDEOLOGIX MEDIA Dooel (https://ideologix.com)
 * This file is part of Vimeify Plugin - https://wordpress.org/plugins/vimeify/
 *
 * Vimeify - Formerly "WP Vimeo Videos" is free software: you can redistribute
 * it and/or modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 2 of the License,
 * or (at your option) any later version.
 *
 * Vimeify - Formerly "WP Vimeo Videos" is distributed in the hope that it
 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this plugin. If not, see <https://www.gnu.org/licenses/>.
 *
 * Code developed by Darko Gjorgjijoski <dg@darkog.com>.
 **********************************************************************/

namespace Vimeify\Core\Backend;

use Vimeify\Core\Abstracts\BaseProvider;
use Vimeify\Core\Abstracts\BaseTool;
use Vimeify\Core\Components\Database;
use Vimeify\Core\Components\Vimeo;
use Vimeify\Core\Utilities\FileSystem;
use Vimeify\Core\Utilities\Formatters\ByteFormatter;
use Vimeify\Core\Utilities\Formatters\VimeoFormatter;
use Vimeify\Core\Utilities\Validators\NetworkValidator;
use Vimeify\Core\Wrappers\ConnectionState;
use Vimeify\Vimeo\Exceptions\VimeoRequestException;

class Ajax extends BaseProvider {

	/**
	 * Registers specific piece of functionality
	 * @return void
	 */
	public function register() {

		add_action( 'wp_ajax_vimeify_connect', [ $this, 'handle_connect' ] );
		add_action( 'wp_ajax_vimeify_disconnect', [ $this, 'handle_disconnect' ] );
		add_action( 'wp_ajax_vimeify_dismiss_instructions', [ $this, 'handle_dismiss_instructions' ] );
		add_action( 'wp_ajax_vimeify_handle_upload', [ $this, 'handle_upload' ] );
		add_action( 'wp_ajax_vimeify_handle_delete', [ $this, 'handle_delete' ] );
		add_action( 'wp_ajax_vimeify_store_upload', [ $this, 'store_upload' ] );
		add_action( 'wp_ajax_vimeify_handle_basic_edit', [ $this, 'handle_basic_edit' ] );
		add_action( 'wp_ajax_vimeify_handle_embed_privacy', [ $this, 'handle_embed_privacy' ] );
		add_action( 'wp_ajax_vimeify_delete_embed_privacy_domain', [ $this, 'handle_embed_privacy_whitelist_remove' ] );
		add_action( 'wp_ajax_vimeify_handle_video_embed_preset_set', [ $this, 'handle_video_embed_preset_set' ] );
		add_action( 'wp_ajax_vimeify_handle_video_folder_set', [ $this, 'handle_video_folder_set' ] );
		add_action( 'wp_ajax_vimeify_get_uploads', [ $this, 'get_uploads' ] );
		add_action( 'wp_ajax_vimeify_attachment2vimeo', [ $this, 'handle_attachment2vimeo' ] );
		add_action( 'wp_ajax_vimeify_attachment2vimeo_delete', [ $this, 'handle_attachment2vimeo_delete' ] );
		add_action( 'wp_ajax_vimeify_user_search', [ $this, 'handle_user_search' ] );
		add_action( 'wp_ajax_vimeify_folder_search', [ $this, 'handle_folder_search' ] );
		add_action( 'wp_ajax_vimeify_upload_profile_search', [ $this, 'handle_upload_profile_search' ] );
		add_action( 'wp_ajax_vimeify_embed_preset_search', [ $this, 'handle_embed_preset_search' ] );
		add_action( 'wp_ajax_vimeify_generate_stats', [ $this, 'handle_generate_stats' ] );
		add_action( 'wp_ajax_vimeify_handle_tool_process', array( $this, 'handle_tool_process' ), 50 );
		add_action( 'wp_ajax_vimeify_load_more_gallery_videos', [ $this, 'handle_load_more_gallery_videos' ] );
		add_action( 'wp_ajax_nopriv_vimeify_load_more_gallery_videos', [ $this, 'handle_load_more_gallery_videos' ] );
		add_action( 'wp_ajax_vimeify_save_settings', [ $this, 'handle_save_settings' ] );
	}

	/**
	 * Handles the Vimeify connect
	 * @return void
	 */
	public function handle_connect() {
		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'message' => esc_html__( 'Security Check Failed.', 'vimeify' ),
			) );
		}

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array(
				'message' => esc_html__( 'Permission denied.', 'vimeify' ),
			) );
		}

		$connection_type = isset( $_POST['connection_type'] ) ? sanitize_text_field( wp_unslash( $_POST['connection_type'] ) ) : '';

		switch ( $connection_type ) {
			case Vimeo::CONNECTION_TYPE_CUSTOM_ACCESS_TOKEN:
				$access_token = isset( $_POST['access_token'] ) ? sanitize_text_field( wp_unslash( $_POST['access_token'] ) ) : '';
				break;
			case Vimeo::CONNECTION_TYPE_VIMEO_PLUGIN:
				$access_token = get_option( '_vimeo_access_token' );
				break;
			default:
				$access_token = '';
				break;
		}

		if ( empty( $access_token ) ) {
			wp_send_json_error( array(
				'message' => __( 'The access token is empty. Please enter valid access token!', 'vimeify' ),
			) );
		}

		$vimeo = $this->plugin->system()->vimeo();

		try {
			$conn      = Vimeo::create_connection( $access_token );
			$data      = Vimeo::verify_connection( $conn );
			$con_state = new ConnectionState( $data );
			if ( $con_state->is_connected ) {
				if ( $con_state->is_authenticated_connection ) {
					$success = 1;
					$message = __( 'Vimeo is connected successfully! Redirecting to the plugin settings now...', 'vimeify' );
					if ( $connection_type !== Vimeo::CONNECTION_TYPE_VIMEO_PLUGIN ) {
						$vimeo->set_access_token( $access_token ); // We store tokens if the connection type is not set to Vimeo plugin.
					}
					$vimeo->set_connection_type( $connection_type );
					// Flush state cache to ensure new account type is detected for cron intervals
					$vimeo->flush_cache();
				} else {
					$success = 0;
					$message = __( 'Vimeo is connected but the connection type is not authenticated. Please re-create the token as authenticated type.', 'vimeify' );
				}
			} else {
				$success = 0;
				$message = sprintf( __( 'Unable to connect to Vimeo (Error: %s)', 'vimeify' ), ! empty( $con_state->error ) ? $con_state->error : __( 'Unknown error', 'vimeify' ) );
			}
		} catch ( \Exception $e ) {
			$success = 0;
			$message = $e->getMessage();
		}

		if ( $success ) {
			wp_send_json_success( [ 'message' => $message ] );
		} else {
			wp_send_json_error( [ 'message' => $message ] );
		}

	}

	/**
	 * Handle the Vimeify disconnect
	 * @return void
	 */
	public function handle_disconnect() {
		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'message' => esc_html__( 'Security Check Failed.', 'vimeify' ),
			) );
		}

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array(
				'message' => esc_html__( 'Permission denied.', 'vimeify' ),
			) );
		}

		// Prevent disconnect if access token is loaded from wp-config.php
		if ( $this->plugin->system()->vimeo()->is_access_token_from_config() ) {
			wp_send_json_error( array(
				'message' => esc_html__( 'Cannot disconnect. The access token is loaded from wp-config.php constant (VIMEIFY_ACCESS_TOKEN). Please remove the constant from wp-config.php to disconnect.', 'vimeify' ),
			) );
		}

		delete_option( 'vimeify_auth' );

		wp_send_json_success( [ 'message' => esc_html__( 'Successfully disconnected from Vimeo.com API. Reloading page...' ) ] );

		exit;

	}

	/**
	 * Dismiss instructions
	 * @return void
	 */
	public function handle_dismiss_instructions() {
		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'message' => __( 'Security Check Failed.', 'vimeify' ),
			) );
		}
		if ( isset( $_GET['dismiss'] ) && 'yes' === $_GET['dismiss'] ) {
			update_option( 'vimeify_welcome', 1 );
		}
		exit;
	}

	/**
	 * Store upload on the server
	 */
	public function store_upload() {

		$logtag = 'VIMEIFY-STORE-UPLOAD';

		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'message' => __( 'Security Check Failed.', 'vimeify' ),
			) );
		}

		if ( ! current_user_can( 'upload_files' ) ) {
			wp_send_json_error( array(
				'message' => __( 'Unauthorized action. User must be Author, Editor or Administrator to edit.', 'vimeify' )
			) );
			exit;
		}

		if ( ! $this->plugin->system()->requests()->is_http_post() ) {
			wp_send_json_error( array(
				'message' => __( 'Invalid request.', 'vimeify' ),
			) );
		}

		$title        = ! empty( $_POST['title'] ) ? sanitize_text_field( wp_unslash( $_POST['title'] ) ) : __( 'Untitled', 'vimeify' );
		$description  = ! empty( $_POST['description'] ) ? sanitize_text_field( wp_unslash( $_POST['description'] ) ) : '';
		$meta         = ! empty( $_POST['meta'] ) && is_array( $_POST['meta'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['meta'] ) ) : [];
		$size         = isset( $_POST['size'] ) ? intval( $_POST['size'] ) : false;
		$source       = ! empty( $_REQUEST['source'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['source'] ) ) : null;
		$view_privacy = ! empty( $_POST['view_privacy'] ) ? sanitize_text_field( wp_unslash( $_POST['view_privacy'] ) ) : null;
		$folder_uri   = ! empty( $_POST['folder_uri'] ) ? sanitize_text_field( wp_unslash( $_POST['folder_uri'] ) ) : null;
		$uri          = ! empty( $_POST['uri'] ) ? sanitize_text_field( wp_unslash( $_POST['uri'] ) ) : '';

		// Extract categories from meta
		$categories = [];
		if ( is_string( $meta ) ) {
			$meta = @json_decode( $meta, true );
		}
		if ( ! empty( $meta['categories'] ) && is_array( $meta['categories'] ) ) {
			$categories = array_map( 'intval', $meta['categories'] );
		}

		if ( empty( $uri ) ) {
			wp_send_json_error( array(
				'message' => __( 'Missing URI.', 'vimeify' ),
			) );
		}

		$vimeo_formatter = new VimeoFormatter();
		$video_id        = $vimeo_formatter->uri_to_id( $uri );

		if ( is_string( $meta ) ) {
			$meta = @json_decode( $meta, true );
		}

		/**
		 * Upload success hook
		 */
		$hook_data = array(
			'vimeo_title'       => $title,
			'vimeo_description' => $description,
			'vimeo_id'          => $video_id,
			'vimeo_size'        => $size,
			'vimeo_meta'        => $meta,
			'overrides'         => [
				'view_privacy' => $view_privacy,
				'folder_uri'   => $folder_uri
			],
			'categories'        => $categories,
			'source'            => array(
				'software' => $source,
			),
		);

		$this->plugin->system()->logger()->log( sprintf( 'Triggered vimeify_upload_complete (%s)', wp_json_encode( $hook_data ) ), $logtag );
		do_action( 'vimeify_upload_complete', $hook_data );

		// Assign categories to the video post
		if ( ! empty( $categories ) ) {
			$post_id = $this->plugin->system()->database()->get_post_id( $uri );
			if ( $post_id ) {
				wp_set_object_terms( $post_id, $categories, Database::TAXONOMY_CATEGORY );
			}
		}

		wp_send_json_success( array(
			'message' => __( 'Video uploaded successfully.', 'vimeify' ),
		) );

		exit;

	}

	/**
	 * Handles video deletion
	 */
	public function handle_delete() {

		$logtag = 'VIMEIFY-ADMIN-EDIT';

		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'title'   => __( 'Permission denied', 'vimeify' ),
				'message' => __( 'Security check failed. Please contact administrator.', 'vimeify' )
			) );
		}

		$required_cap = apply_filters( 'vimeify_vimeo_cap_delete', 'delete_posts' );

		if ( ! current_user_can( $required_cap ) ) {
			wp_send_json_error( array(
				'title'   => __( 'Permission denied', 'vimeify' ),
				'message' => __( 'Your WordPress account doesn\'t have the required permissions do delete video.', 'vimeify' )
			) );
			exit;
		}
		if ( ! $this->plugin->system()->vimeo()->can_delete() ) {
			wp_send_json_error( array(
				'title'   => __( 'Permission denied', 'vimeify' ),
				'message' => __( 'Your vimeo account doesn\'t have the delete scope to perform this action.', 'vimeify' )
			) );
			exit;
		}

		if ( ! isset( $_POST['vimeo_uri'] ) || empty( $_POST['vimeo_uri'] ) ) {

			// If vimeo_uri is missing, try to delete the local post only
			if ( isset( $_POST['post_id'] ) && is_numeric( $_POST['post_id'] ) ) {

				$post_id = intval( $_POST['post_id'] );

				// Check if post exists
				$post = get_post( $post_id );
				if ( ! $post ) {
					// Post doesn't exist, consider it already deleted
					wp_send_json_success( array(
						'title'        => __( 'Success', 'vimeify' ),
						'message'      => __( 'Local video entry was already deleted.', 'vimeify' ),
						'local_delete' => 1
					) );
					exit;
				}

				$deleted = wp_delete_post( $post_id, true );
				if ( $deleted ) {
					wp_send_json_success( array(
						'title'        => __( 'Success', 'vimeify' ),
						'message'      => __( 'Local video entry deleted successfully. The Vimeo video could not be found.', 'vimeify' ),
						'local_delete' => 1
					) );
					exit;
				} else {
					wp_send_json_error( array(
						'title'   => __( 'Error', 'vimeify' ),
						'message' => __( 'Failed to delete local video entry.', 'vimeify' )
					) );
					exit;
				}
			}

			// No vimeo_uri and no post_id
			wp_send_json_error( array(
				'title'   => __( 'Missing video', 'vimeify' ),
				'message' => __( 'Select valid vimeo video to delete.', 'vimeify' )
			) );

			exit;
		}


		$vimeo_uri         = $this->plugin->system()->vimeo()->formatter()->id_to_uri( sanitize_text_field( wp_unslash( $_POST['vimeo_uri'] ) ) );
		$vimeo_post_id     = $this->plugin->system()->database()->get_post_id( $vimeo_uri );
		$vimeo_post_exists = ( false !== get_post_status( $vimeo_post_id ) );

		try {
			$response = $this->plugin->system()->vimeo()->delete( $vimeo_uri );

			if ( $response['status'] !== 204 && ( isset( $response['body']['error'] ) && ! empty( $response['body']['error'] ) ) ) {
				$local_delete = false;
				if ( $vimeo_post_exists ) {
					$local_delete = true;
					wp_delete_post( $vimeo_post_id, true );
					$this->plugin->system()->logger()->log( sprintf( 'Local video %s deleted', $vimeo_post_id ), $logtag );
				}
				$this->plugin->system()->logger()->log( sprintf( 'Unable to delete remote video %s', $vimeo_uri ), $logtag );
				wp_send_json_error( array(
					'title'        => __( 'Sorry!', 'vimeify' ),
					'message'      => $response['body']['error'],
					'local_delete' => $local_delete,
				) );
			} elseif ( $response['status'] === 204 ) {
				$local_delete = false;
				if ( $vimeo_post_exists ) {
					$local_delete = true;
					wp_delete_post( $vimeo_post_id, true );
					$this->plugin->system()->logger()->log( sprintf( 'Local video %s deleted', $vimeo_post_id ), $logtag );
				}
				$this->plugin->system()->logger()->log( sprintf( 'Remote video %s deleted', $vimeo_uri ), $logtag );
				wp_send_json_success( array(
					'title'        => __( 'Success', 'vimeify' ),
					'message'      => __( 'Video deleted successfully!', 'vimeify' ),
					'local_delete' => $local_delete
				) );
			} else {
				$this->plugin->system()->logger()->log( sprintf( 'Unable to delete remote video %s', $vimeo_uri ), $logtag );
				wp_send_json_error( array(
					'title'   => __( 'Sorry!', 'vimeify' ),
					'message' => __( 'The video was not deleted.', 'vimeify' )
				) );
			}

		} catch ( \Exception $e ) {
			$this->plugin->system()->logger()->log( sprintf( 'Unable to delete remote video %s. Local %s video deleted. Error: %s', $vimeo_uri, $vimeo_post_id, $e->getMessage() ), $logtag );
			wp_delete_post( $vimeo_post_id, true );
			wp_send_json_success( array(
				'title'        => __( 'Success', 'vimeify' ),
				'message'      => __( 'Video deleted successfully. However we had a trouble deleting the vimeo from vimeo.com. It may be deleted or belongs to different account.', 'vimeify' ),
				'local_delete' => true,
			) );
			exit;
		}
	}

	/**
	 * Handle basic edit vimeo
	 */
	public function handle_basic_edit() {

		$logtag = 'VIMEIFY-ADMIN-EDIT';

		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'message' => __( 'Security Check Failed.', 'vimeify' ),
			) );
			exit;
		}

		if ( ! $this->plugin->system()->requests()->is_http_post() ) {
			wp_send_json_error( array(
				'message' => __( 'Invalid request', 'vimeify' )
			) );
			exit;
		}

		if ( ! current_user_can( 'upload_files' ) ) {
			wp_send_json_error( array(
				'message' => __( 'Unauthorized action', 'vimeify' )
			) );
			exit;
		}

		$uri          = isset( $_POST['uri'] ) ? sanitize_text_field( wp_unslash( $_POST['uri'] ) ) : '';
		$name         = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
		$description  = isset( $_POST['description'] ) ? sanitize_text_field( wp_unslash( $_POST['description'] ) ) : '';
		$view_privacy = isset( $_POST['view_privacy'] ) ? sanitize_text_field( wp_unslash( $_POST['view_privacy'] ) ) : '';

		if ( empty( $name ) ) {
			wp_send_json_error( array(
				'message' => __( 'Please enter valid name', 'vimeify' )
			) );
			exit;
		}

		try {

			/**
			 * Basic details
			 */
			$params = array(
				'name'        => $name,
				'description' => $description,
			);

			/**
			 * Set privacy
			 */
			$privacy = $this->get_view_privacy( $view_privacy );
			if ( ! empty( $privacy ) ) {
				if ( 'default' !== $privacy ) {
					$params['privacy'] = array( 'view' => $privacy );
				}
			}

			/**
			 * Update video
			 */
			$response = $this->plugin->system()->vimeo()->edit( $uri, $params );
			$post_ID  = $this->plugin->system()->database()->get_post_id( $uri );

			/**
			 * Respond back
			 */
			if ( isset( $response['status'] ) ) {
				if ( $response['status'] === 200 ) {
					wp_update_post( array(
						'ID'         => $post_ID,
						'post_title' => $name,
					) );
					$this->plugin->system()->logger()->log( sprintf( 'Video "%s" saved', $uri ), $logtag );
					wp_send_json_success( array(
						'message' => __( 'Video saved successfully', 'vimeify' )
					) );
				} else {
					$this->plugin->system()->logger()->log( sprintf( 'Unable to save video %s', $uri ), $logtag );
					wp_send_json_error( array(
						'message' => __( 'Error saving the video', 'vimeify' )
					) );
				}
			} else {
				$this->plugin->system()->logger()->log( sprintf( 'Unable to save video %s', $uri ), $logtag );
				wp_send_json_error( array(
					'message' => __( 'Unknown error.', 'vimeify' ),
				) );
			}

		} catch ( VimeoRequestException $e ) {

			$this->plugin->system()->logger()->log( sprintf( 'Unable to save video  %s', $uri ), $logtag );

			wp_send_json_error( array(
				'message' => $e->getMessage(),
			) );

		}

		exit;

	}

	/**
	 * Handle embed privacy
	 */
	public function handle_embed_privacy() {

		$logtag = 'VIMEIFY-ADMIN-EDIT';

		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'message' => __( 'Security Check Failed.', 'vimeify' ),
			) );
			exit;
		}

		if ( ! $this->plugin->system()->requests()->is_http_post() ) {
			wp_send_json_error( array(
				'message' => __( 'Invalid request', 'vimeify' )
			) );
			exit;
		}

		if ( ! current_user_can( 'upload_files' ) ) {
			wp_send_json_error( array(
				'message' => __( 'Unauthorized action', 'vimeify' )
			) );
			exit;
		}

		$uri = isset( $_POST['uri'] ) ? sanitize_text_field( wp_unslash( $_POST['uri'] ) ) : '';

		$privacy_embed = isset( $_POST['privacy_embed'] ) ? sanitize_text_field( wp_unslash( $_POST['privacy_embed'] ) ) : '';

		if ( empty( $privacy_embed ) || ! in_array( $privacy_embed, array( 'public', 'whitelist' ) ) ) {
			wp_send_json_error( array(
				'message' => __( 'Invalid embed privacy mehtod.', 'vimeify' ),
			) );
			exit;
		}

		$whitelist_domain = isset( $_POST['privacy_embed_domain'] ) ? sanitize_text_field( wp_unslash( $_POST['privacy_embed_domain'] ) ) : '';

		try {
			$network_validator = new NetworkValidator();
			$this->plugin->system()->vimeo()->set_embed_privacy( $uri, $privacy_embed );
			if ( 'whitelist' === $privacy_embed && ! empty( $whitelist_domain ) ) {
				if ( ! $network_validator->validate_domain_name( $whitelist_domain ) ) {
					$this->plugin->system()->logger()->log( sprintf( 'Unable to whitelist domain %s for %s. Error: Invalid format', $whitelist_domain, $uri ), $logtag );
					wp_send_json_error( array(
						'message' => __( 'Invalid domain. Please enter valid domain name.', 'vimeify' ),
					) );
					exit;
				} else {
					$whitelist_response = $this->plugin->system()->vimeo()->whitelist_domain_add( $uri, $whitelist_domain );
					$this->plugin->system()->logger()->log( sprintf( 'Domain %s added to whitelist for %s', $whitelist_domain, $uri ), $logtag );
					if ( isset( $whitelist_response['status'] ) ) {
						if ( $whitelist_response['status'] >= 200 && $whitelist_response['status'] < 300 ) {
							wp_send_json_success( array(
								'message'      => __( 'Domain added to embed whitelist.', 'vimeify' ),
								'domain_added' => $whitelist_domain,
								'uri'          => $uri,
							) );
							exit;
						} else {
							wp_send_json_error( array(
								'message' => __( 'Failed to add domain to the embed whitelist.', 'vimeify' ),
							) );
							exit;
						}
					} else {
						wp_send_json_error( array(
							'message' => __( 'Invalid response received from vimeo', 'vimeify' )
						) );
						exit;
					}
				}
			} else {
				wp_send_json_success( array(
					'message' => __( 'Embed privacy changed successfully.', 'vimeify' ),
				) );
				exit;
			}
		} catch ( \Exception $e ) {
			$message = 'whitelist' === $privacy_embed ?
				sprintf( 'Unable to whitelist domain %s for %s. Error: %s', $whitelist_domain, $uri, $e->getMessage() ) :
				sprintf( 'Unable to change embed privacy for %s. Error: %s', $uri, $e->getMessage() );
			wp_send_json_error( array(
				'message' => $message,
			) );
			exit;
		}
	}

	/**
	 * Handles whitelist removal
	 */
	public function handle_embed_privacy_whitelist_remove() {

		$logtag = 'VIMEIFY-ADMIN-EDIT';

		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'message' => __( 'Security Check Failed.', 'vimeify' ),
			) );
			exit;
		}

		if ( ! $this->plugin->system()->requests()->is_http_post() ) {
			wp_send_json_error( array(
				'message' => __( 'Invalid request', 'vimeify' )
			) );
			exit;
		}

		if ( ! current_user_can( 'upload_files' ) ) {
			wp_send_json_error( array(
				'message' => __( 'Unauthorized action', 'vimeify' )
			) );
			exit;
		}

		$uri = isset( $_POST['uri'] ) ? sanitize_text_field( wp_unslash( $_POST['uri'] ) ) : '';

		if ( empty( $uri ) ) {
			wp_send_json_error( array(
				'message' => __( 'Invalid video URI specified', 'vimeify' )
			) );
			exit;
		}

		$whitelist_domain = isset( $_POST['domain'] ) ? sanitize_text_field( wp_unslash( $_POST['domain'] ) ) : '';

		try {
			$whitelist_response = $this->plugin->system()->vimeo()->whitelist_domain_remove( $uri, $whitelist_domain );
			$this->plugin->system()->logger()->log( sprintf( 'Domain %s whitelisted for %s', $whitelist_domain, $uri ), $logtag );
		} catch ( VimeoRequestException $e ) {
			$this->plugin->system()->logger()->log( sprintf( 'Error removing domain from whitelist (%s)', $e->getMessage() ), $logtag );
			wp_send_json_success( array(
				'message' => __( 'Error removing domain from whitelist.', 'vimeify' ),
			) );
			exit;
		}
		if ( isset( $whitelist_response['status'] ) ) {
			if ( $whitelist_response['status'] === 204 ) {
				wp_send_json_success( array(
					'message' => __( 'Domain removed successfully', 'vimeify' ),
				) );
			} else {
				wp_send_json_error( array(
					'message' => isset( $whitelist_response['body']['error'] ) ? $whitelist_response['body']['error'] : __( 'Failed to remove domain from embed privacy whitelist.', 'vimeify' )
				) );
				exit;
			}
		} else {
			wp_send_json_error( array(
				'message' => __( 'Invalid response received from vimeo', 'vimeify' )
			) );
			exit;
		}
	}

	/**
	 * Handles Video folder update. Remove or add video to folder.
	 */
	public function handle_video_folder_set() {

		$logtag = 'VIMEIFY-ADMIN-EDIT';

		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'message' => __( 'Security Check Failed.', 'vimeify' ),
			) );
			exit;
		}

		if ( ! $this->plugin->system()->requests()->is_http_post() ) {
			wp_send_json_error( array(
				'message' => __( 'Invalid request.', 'vimeify' ),
			) );
		}

		if ( ! current_user_can( 'upload_files' ) ) {
			wp_send_json_error( array(
				'message' => __( 'Unauthorized action. User must be Author, Editor or Administrator to edit.', 'vimeify' )
			) );
			exit;
		}

		$video_uri       = isset( $_POST['video_uri'] ) ? sanitize_text_field( wp_unslash( $_POST['video_uri'] ) ) : null;
		$folder_uri      = isset( $_POST['folder_uri'] ) ? sanitize_text_field( wp_unslash( $_POST['folder_uri'] ) ) : null;
		$vimeo_formatter = new VimeoFormatter();
		$video_id        = $vimeo_formatter->uri_to_id( $video_uri );

		if ( empty( $video_uri ) ) {
			wp_send_json_error( array(
				'message' => __( 'Video missing.', 'vimeify' ),
			) );
			exit;
		}
		if ( empty( $folder_uri ) ) {
			wp_send_json_error( array(
				'message' => __( 'Folder missing.', 'vimeify' ),
			) );
			exit;
		}

		// Handle default
		if ( $folder_uri === 'default' ) {
			$video      = $this->plugin->system()->vimeo()->get( '/videos/' . $video_id . '?fields=parent_folder' );
			$folder_uri = isset( $video['body']['parent_folder']['uri'] ) && ! empty( $video['body']['parent_folder']['uri'] ) ? $video['body']['parent_folder']['uri'] : null;
			if ( $folder_uri ) {
				try {
					$response = $this->plugin->system()->vimeo()->remove_video_folder( $video_uri, $folder_uri );
					$this->plugin->system()->logger()->log( sprintf( 'Folder changed to %s for %s', 'default', $video_uri ), $logtag );
					if ( isset( $response['status'] ) ) {
						if ( in_array( $response['status'], array( 200, 204 ) ) ) {
							$this->plugin->system()->logger()->log( sprintf( 'Folder changed to %s for %s', 'default', $video_uri ), $logtag );
							wp_send_json_success( array(
								'message' => __( 'Video removed from folder successfully.', 'vimeify' ),
							) );
						} else {
							$error = '';
							if ( isset( $response['body']['error'] ) ) {
								$error = sprintf( 'Error: %s', $response['body']['error'] );
							}
							$this->plugin->system()->logger()->log( sprintf( 'Unable to change folder to %s for %s. %s', 'default', $video_uri, $error ), $logtag );
							wp_send_json_success( array(
								'message' => sprintf( __( 'Unable to change folder. %s', 'vimeify' ), $error ),
							) );
						}
					} else {
						$this->plugin->system()->logger()->log( sprintf( 'Unable to set folder %s for %s. Error: Unreadable response', 'default', $video_uri ), $logtag );
						wp_send_json_error( array(
							'message' => __( 'Invalid response received from vimeo', 'vimeify' ),
						) );
					}
				} catch ( VimeoRequestException $e ) {
					$this->plugin->system()->logger()->log( sprintf( 'Unable to remove folder %s for %s. Error: %s', $folder_uri, $video_uri, $e->getMessage() ), $logtag );
					wp_send_json_error( array(
						'message' => $e->getMessage(),
					) );
				}
			} else {
				$this->plugin->system()->logger()->log( sprintf( 'Unable to set folder %s for %s. Error: Invalid folder', $folder_uri, $video_uri ), $logtag );
				wp_send_json_error( array(
					'message' => __( 'Unexpected error! Sorry.', 'vimeify' ),
				) );
			}
		} else { // Handle folder
			try {

				$response = $this->plugin->system()->vimeo()->set_video_folder( $video_uri, $folder_uri );
				if ( isset( $response['status'] ) ) {
					if ( in_array( $response['status'], array( 200, 204 ) ) ) {
						$this->plugin->system()->logger()->log( sprintf( 'Folder changed to %s for %s', $folder_uri, $video_uri ), $logtag );
						wp_send_json_success( array(
							'message' => __( 'Video moved to folder successfully.', 'vimeify' ),
						) );
					} else {
						$error = '';
						if ( isset( $response['body']['error'] ) ) {
							$error = sprintf( 'Error: %s', $response['body']['error'] );
						}
						$this->plugin->system()->logger()->log( sprintf( 'Unable to change folder to %s for %s. %s', $folder_uri, $video_uri, $error ), $logtag );
						wp_send_json_success( array(
							'message' => sprintf( __( 'Unable to change folder. %s', 'vimeify' ), $error ),
						) );
					}
				} else {
					$this->plugin->system()->logger()->log( sprintf( 'Unable to set folder %s for %s. Error: Unreadable response', $folder_uri, $video_uri ), $logtag );
					wp_send_json_error( array(
						'message' => __( 'Invalid response received from vimeo', 'vimeify' ),
					) );
				}

			} catch ( VimeoRequestException $e ) {
				$this->plugin->system()->logger()->log( sprintf( 'Unable to set folder %s for %s. Error: %s', $folder_uri, $video_uri, $e->getMessage() ), $logtag );
				wp_send_json_error( array(
					'message' => $e->getMessage(),
				) );
			}
		}
		exit;

	}

	/**
	 * Handles Video embed preset update. Remove or add embed preset to video.
	 */
	public function handle_video_embed_preset_set() {

		$logtag = 'VIMEIFY-ADMIN-EDIT';

		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'message' => __( 'Security Check Failed.', 'vimeify' ),
			) );
			exit;
		}

		if ( ! $this->plugin->system()->requests()->is_http_post() ) {
			wp_send_json_error( array(
				'message' => __( 'Invalid request.', 'vimeify' ),
			) );
		}

		if ( ! current_user_can( 'upload_files' ) ) {
			wp_send_json_error( array(
				'message' => __( 'Unauthorized action. User must be Author, Editor or Administrator to edit.', 'vimeify' )
			) );
			exit;
		}

		$video_uri       = isset( $_POST['video_uri'] ) ? sanitize_text_field( wp_unslash( $_POST['video_uri'] ) ) : null;
		$preset_uri      = isset( $_POST['embed_preset_uri'] ) ? sanitize_text_field( wp_unslash( $_POST['embed_preset_uri'] ) ) : null;
		$vimeo_formatter = new VimeoFormatter();
		$video_id        = $vimeo_formatter->uri_to_id( $video_uri );

		if ( empty( $video_uri ) ) {
			wp_send_json_error( array(
				'message' => __( 'Video missing.', 'vimeify' ),
			) );
			exit;
		}
		if ( empty( $preset_uri ) ) {
			wp_send_json_error( array(
				'message' => __( 'Preset missing.', 'vimeify' ),
			) );
			exit;
		}

		// Handle default
		if ( $preset_uri === 'default' ) {
			$video      = $this->plugin->system()->vimeo()->get( '/videos/' . $video_id . '?fields=embed' );
			$preset_uri = isset( $video['body']['embed']['uri'] ) && ! empty( $video['body']['embed']['uri'] ) ? $video['body']['embed']['uri'] : null;
			if ( $preset_uri ) {
				try {
					$response = $this->plugin->system()->vimeo()->remove_video_embed_preset( $video_uri, $preset_uri );
					if ( isset( $response['status'] ) ) {
						if ( in_array( $response['status'], array( 200, 204 ) ) ) {
							$this->plugin->system()->logger()->log( sprintf( 'Embed preset changed to %s for %s', 'default', $video_uri ), $logtag );
							wp_send_json_success( array(
								'message' => __( 'Embed preset removed from video successfully', 'vimeify' ),
							) );
						} else {
							$error = '';
							if ( isset( $response['body']['error'] ) ) {
								$error = sprintf( 'Error: %s', $response['body']['error'] );
							}
							$this->plugin->system()->logger()->log( sprintf( 'Unable to change embed preset to %s for %s. %s', 'default', $video_uri, $error ), $logtag );
							wp_send_json_success( array(
								'message' => sprintf( __( 'Unable to change embed preset. %s', 'vimeify' ), $error ),
							) );
						}
					} else {
						$this->plugin->system()->logger()->log( sprintf( 'Unable to set embed preset %s for %s. Error: Unreadable response.', 'default', $video_uri ), $logtag );
						wp_send_json_error( array(
							'message' => __( 'Invalid response received from vimeo', 'vimeify' ),
						) );
					}
				} catch ( VimeoRequestException $e ) {
					$this->plugin->system()->logger()->log( sprintf( 'Unable to remove embed preset %s for %s. Error: %s', $preset_uri, $video_uri, $e->getMessage() ), $logtag );
					wp_send_json_error( array(
						'message' => $e->getMessage(),
					) );
				}
			} else {
				$this->plugin->system()->logger()->log( sprintf( 'Unable to set embed preset %s for %s. Error: Invalid preset.', $preset_uri, $video_uri ), $logtag );
				wp_send_json_error( array(
					'message' => __( 'Unexpected error! Sorry.', 'vimeify' ),
				) );
			}
		} else { // Handle Preset
			try {

				$response = $this->plugin->system()->vimeo()->set_video_embed_preset( $video_uri, $preset_uri );
				if ( isset( $response['status'] ) ) {
					if ( in_array( $response['status'], array( 200, 204 ) ) ) {
						$this->plugin->system()->logger()->log( sprintf( 'Embed preset changed to %s for %s', $preset_uri, $video_uri ), $logtag );
						wp_send_json_success( array(
							'message' => __( 'Embed preset added to Video successfully.', 'vimeify' ),
						) );
					} else {
						$error = '';
						if ( isset( $response['body']['error'] ) ) {
							$error = sprintf( 'Error: %s', $response['body']['error'] );
						}
						$this->plugin->system()->logger()->log( sprintf( 'Unable to change embed preset to %s for %s. %s', $preset_uri, $video_uri, $error ), $logtag );
						wp_send_json_success( array(
							'message' => sprintf( __( 'Unable to change embed preset. %s', 'vimeify' ), $error ),
						) );
					}
				} else {
					$this->plugin->system()->logger()->log( sprintf( 'Unable to set embed preset %s for %s. Error: Unraedable response', $preset_uri, $video_uri ), $logtag );
					wp_send_json_error( array(
						'message' => __( 'Invalid response received from vimeo', 'vimeify' ),
					) );
				}

			} catch ( VimeoRequestException $e ) {
				$this->plugin->system()->logger()->log( sprintf( 'Unable to set embed preset %s for %s. Error: %s', $preset_uri, $video_uri, $e->getMessage() ), $logtag );
				wp_send_json_error( array(
					'message' => $e->getMessage(),
				) );
			}
		}
		exit;

	}

	/**
	 * Handles attachment upload
	 */
	public function handle_attachment2vimeo() {

		$logtag = 'VIMEIFY-ADMIN-A2V';

		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array( 'message' => __( 'Security Check Failed.', 'vimeify' ) ) );
			exit;
		}

		if ( ! $this->plugin->system()->requests()->is_http_post() ) {
			wp_send_json_error( array( 'message' => __( 'Invalid request', 'vimeify' ) ) );
			exit;
		}

		if ( ! current_user_can( 'upload_files' ) ) {
			wp_send_json_error( array(
				'message' => __( 'Unauthorized action. User must be Author, Editor or Administrator to edit.', 'vimeify' )
			) );
			exit;
		}

		$ID           = isset( $_POST['attachment_id'] ) ? intval( $_POST['attachment_id'] ) : null;
		$title        = ! empty( $_POST['vimeo_title'] ) ? sanitize_text_field( wp_unslash( $_POST['vimeo_title'] ) ) : null;
		$desc         = ! empty( $_POST['vimeo_description'] ) ? sanitize_text_field( wp_unslash( $_POST['vimeo_description'] ) ) : null;
		$view_privacy = ! empty( $_POST['vimeo_view_privacy'] ) ? sanitize_text_field( wp_unslash( $_POST['vimeo_view_privacy'] ) ) : null;
		$folder_uri   = ! empty( $_POST['vimeo_folder_uri'] ) ? sanitize_text_field( wp_unslash( $_POST['vimeo_folder_uri'] ) ) : null;


		if ( is_null( $ID ) ) {
			wp_send_json_error( array( 'message' => __( 'Invalid attachment', 'vimeify' ) ) );
			exit;
		}

		if ( is_null( $title ) ) {
			wp_send_json_error( array( 'message' => __( 'Please enter valid video title.', 'vimeify' ) ) );
			exit;
		}

		$file_path = get_attached_file( $ID );
		$file_size = FileSystem::file_exists( $file_path ) ? FileSystem::file_size( $file_path ) : false;

		try {

			$params = array(
				'name' => $title,
			);

			if ( ! is_null( $desc ) ) {
				$params['description'] = $desc;
			}

			/**
			 * Perform the upload
			 */
			$result          = $this->plugin->system()->vimeo()->upload( $file_path, $params );
			$vimeo_uri       = $result['response'];
			$vimeo_formatter = new VimeoFormatter();
			$vimeo_id        = $vimeo_formatter->uri_to_id( $vimeo_uri );
			$hook_data       = array(
				'vimeo_title'       => $title,
				'vimeo_description' => $desc,
				'vimeo_id'          => $vimeo_id,
				'vimeo_size'        => $file_size,
				'overrides'         => array(
					'view_privacy' => $view_privacy,
					'folder_uri'   => $folder_uri,
				),
				'source'            => array(
					'software' => 'Backend.Form.Attachment',
					'media_id' => $ID,
				)
			);

			$this->plugin->system()->logger()->log( sprintf( 'Triggered vimeify_upload_complete (%s)', wp_json_encode( $hook_data ) ), $logtag );

			/**
			 * Upload success hook
			 */
			do_action( 'vimeify_upload_complete', $hook_data );

			$this->plugin->system()->logger()->log( sprintf( 'Media library video #%s uploaded to Vimeo', $ID ), $logtag );

			wp_send_json_success( array(
				'vimeo_id'          => $vimeo_id,
				'info_metabox_html' => wp_kses(
					$this->plugin->system()->views()->get_view( 'admin/partials/media-buttons', [
						'id'     => $ID,
						'plugin' => $this->plugin,
					] ),
					wp_kses_allowed_html( 'vimeify' )
				)
			) );


		} catch ( \Exception $e ) {
			$this->plugin->system()->logger()->log( sprintf( 'Failed to uplaod from media library screen (%s)', $e->getMessage() ), $logtag );
			wp_send_json_error( sprintf( __( 'Upload failed: %s', 'vimeify' ), $e->getMessage() ) );
		}
		exit;
	}

	/**
	 * Handles attachment delete
	 */
	public function handle_attachment2vimeo_delete() {

		$logtag = 'VIMEIFY-ADMIN-A2V';

		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array( 'message' => __( 'Security Check Failed.', 'vimeify' ) ) );
			exit;
		}

		if ( ! $this->plugin->system()->requests()->is_http_post() ) {
			wp_send_json_error( array( 'message' => __( 'Invalid request', 'vimeify' ) ) );
			exit;
		}

		if ( ! current_user_can( 'delete_files' ) ) {
			wp_send_json_error( array(
				'message' => __( 'Unauthorized action. User must be Author, Editor or Administrator to edit.', 'vimeify' )
			) );
			exit;
		}

		$ID = isset( $_POST['attachment_id'] ) ? intval( $_POST['attachment_id'] ) : null;

		if ( is_null( $ID ) ) {
			wp_send_json_error( array( 'message' => __( 'Invalid attachment', 'vimeify' ) ) );
			exit;
		}

		$meta = get_post_meta( $ID, 'vimeify', true );

		if ( ! isset( $meta['vimeo_id'] ) ) {
			wp_send_json_error( array( 'message' => __( 'Invalid attachment', 'vimeify' ) ) );
			exit;
		} else {
			try {
				$uri      = '/videos/' . $meta['vimeo_id'];
				$response = $this->plugin->system()->vimeo()->delete( $uri );
				$local_id = isset( $meta['local_id'] ) ? (int) $meta['local_id'] : 0;
				if ( $local_id > 0 ) {
					wp_delete_post( $local_id, true );
				}
				delete_post_meta( $ID, 'vimeify' );
				$this->plugin->system()->logger()->log( sprintf( 'Media library video %s deleted from Vimeo', $uri ), $logtag );
				wp_send_json_success( array(
					'message'           => __( 'Video deleted from vimeo successfully', 'vimeify' ),
					'info_metabox_html' => wp_kses(
						$this->plugin->system()->views()->get_view( 'admin/partials/media-buttons', [
							'id'     => $ID,
							'plugin' => $this->plugin,
						] ),
						wp_kses_allowed_html( 'vimeify' )
					),
					'data'              => wp_json_encode( $response )
				) );

			} catch ( \Exception $e ) {
				$this->plugin->system()->logger()->log( sprintf( 'Unable to delete from media library screen (%s)', $e->getMessage() ), $logtag );
				wp_send_json_error( array( 'message' => __( 'Invalid attachment', 'vimeify' ) ) );
				exit;
			}
		}

	}

	/**
	 * Return the uploaded videos
	 */
	public function get_uploads() {

		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'message' => __( 'Security Check Failed.', 'vimeify' ),
			) );
			exit;
		}

		if ( ! $this->plugin->system()->requests()->is_http_get() ) {
			wp_send_json_error( array(
				'message' => __( 'Invalid request', 'vimeify' )
			) );
			exit;
		}

		if ( ! current_user_can( 'upload_files' ) ) {
			wp_send_json_error( array(
				'message' => __( 'Unauthorized action', 'vimeify' )
			) );
			exit;
		}

		$current_user_uploads = ! current_user_can( 'administrator' ) && (int) $this->plugin->system()->settings()->global()->get( 'admin.tinymce.show_author_uploads_only' );

		// Get pagination and search parameters
		$page     = isset( $_GET['page'] ) ? max( 1, (int) $_GET['page'] ) : 1;
		$per_page = isset( $_GET['per_page'] ) ? max( 1, (int) $_GET['per_page'] ) : 20;
		$search   = isset( $_GET['search'] ) ? sanitize_text_field( wp_unslash( $_GET['search'] ) ) : '';

		$result = $this->plugin->system()->database()->get_uploaded_videos( $current_user_uploads, $page, $per_page, $search );

		wp_send_json_success( array(
			'uploads' => $result['uploads'],
			'total'   => $result['total'],
		) );
		exit;

	}

	/**
	 * Handles user search
	 */
	public function handle_user_search() {

		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'message' => __( 'Security Check Failed.', 'vimeify' ),
			) );
			exit;
		}

		$items    = array();
		$phrase   = isset( $_REQUEST['search_str'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['search_str'] ) ) : '';
		$page_num = isset( $_REQUEST['page_number'] ) ? intval( $_REQUEST['page_number'] ) : 1;
		$per_page = 25;

		$params = array(
			'number' => $per_page,
			'paged'  => $page_num,
		);
		if ( ! empty( $phrase ) ) {
			$params['search']         = '*' . esc_attr( $phrase ) . '*';
			$params['search_columns'] = array(
				'user_login',
				'user_nicename',
				'user_email',
				'user_url',
			);
		}
		$query = new \WP_User_Query( $params );
		foreach ( $query->get_results() as $user ) {
			$items[] = array(
				'id'   => $user->ID,
				'text' => sprintf( '%s (#%d - %s)', $user->user_nicename, $user->ID, $user->user_email )
			);
		}

		wp_send_json_success( [
			'results'    => $items,
			'pagination' => [
				'more' => ! empty( $items )
			]
		] );

	}

	/**
	 * Handles folder search
	 * @return void
	 */
	public function handle_folder_search() {
		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'message' => __( 'Security Check Failed.', 'vimeify' ),
			) );
			exit;
		}

		$phrase   = isset( $_REQUEST['search_str'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['search_str'] ) ) : '';
		$page_num = isset( $_REQUEST['page_number'] ) ? intval( $_REQUEST['page_number'] ) : 1;
		$per_page = 25;

		$request = [
			'per_page' => $per_page,
			'page'     => $page_num,
			'query'    => $phrase
		];

		try {
			$folders_response = $this->plugin->system()->vimeo()->get_folders_query( $request );
		} catch ( \Exception $e ) {
		}

		$data  = ! empty( $folders_response['results'] ) ? $folders_response['results'] : [];
		$items = [
			[
				'id'   => 'default',
				'text' => __( 'Default (no folder)', 'vimeify' )
			]
		];
		foreach ( $data as $entry ) {
			$items[] = [
				'id'   => $entry['uri'],
				'text' => $entry['name'],
			];
		}

		wp_send_json_success( [
			'results'    => $items,
			'pagination' => [
				'more' => isset( $folders_response['paging']['has_more'] ) ? $folders_response['paging']['has_more'] : false,
			]
		] );
	}

	/**
	 * Handles the profile searcdarkog wodarh
	 * @return void
	 */
	public function handle_upload_profile_search() {

		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'message' => __( 'Security Check Failed.', 'vimeify' ),
			) );
			exit;
		}

		$phrase   = isset( $_REQUEST['search_str'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['search_str'] ) ) : '';
		$page_num = isset( $_REQUEST['page_number'] ) ? intval( $_REQUEST['page_number'] ) : 1;
		$per_page = 25;

		$params = [
			'post_type'      => Database::POST_TYPE_UPLOAD_PROFILE,
			'post_status'    => 'publish',
			'posts_per_page' => $per_page,
			'paged'          => $page_num,
			's'              => $phrase,
		];

		$items = [];
		$query = new \WP_Query( $params );

		foreach ( $query->posts as $entry ) {
			$items[] = [
				'id'   => $entry->ID,
				'text' => $entry->post_title,
			];
		}

		wp_send_json_success( [
			'results'    => $items,
			'pagination' => [
				'more' => $page_num < $query->max_num_pages,
			]
		] );

	}

	/**
	 * Handles the embed preset search
	 * @return void
	 * @since 2.0.0
	 */
	public function handle_embed_preset_search() {
		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'message' => __( 'Security Check Failed.', 'vimeify' ),
			) );
			exit;
		}

		$phrase   = isset( $_REQUEST['search_str'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['search_str'] ) ) : '';
		$page_num = isset( $_REQUEST['page_number'] ) ? intval( $_REQUEST['page_number'] ) : 1;
		$per_page = 25;

		$request = [
			'per_page' => $per_page,
			'page'     => $page_num,
			'query'    => $phrase
		];

		try {
			$presets_response = $this->plugin->system()->vimeo()->get_embed_presets_query( $request );
		} catch ( \Exception $e ) {
		}

		$data  = ! empty( $presets_response['results'] ) ? $presets_response['results'] : [];
		$items = [
			[
				'id'   => 'default',
				'text' => __( 'Default (no preset)', 'vimeify' )
			]
		];
		foreach ( $data as $entry ) {
			$items[] = [
				'id'   => $entry['uri'],
				'text' => $entry['name'],
			];
		}

		wp_send_json_success( [
			'results'    => $items,
			'pagination' => [
				'more' => isset( $presets_response['paging']['has_more'] ) ? $presets_response['paging']['has_more'] : false,
			]
		] );
	}

	/**
	 * Handles the statistics
	 * @return void
	 */
	public function handle_generate_stats() {
		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'message' => __( 'Security Check Failed.', 'vimeify' ),
			) );
			exit;
		}

		$params = [];

		$author_uplaods_only = (int) $this->plugin->system()->settings()->global()->get( 'admin.videos_list_table.show_author_uploads_only' );

		if ( $author_uplaods_only ) {
			$params['author'] = get_current_user_id();
		}

		$total_uploaded = $this->plugin->system()->database()->get_total_uploaded_size( $params );

		$byte_formatter = new ByteFormatter();

		wp_send_json_success( [
			'html' => wp_kses(
				$this->plugin->system()->views()->get_view( 'admin/partials/library-stats', array(
					'plugin'         => $this->plugin,
					'total_uploaded' => $byte_formatter->format( $total_uploaded ),
				) ),
				wp_kses_allowed_html( 'vimeify' )
			)
		] );

	}

	public function handle_tool_process() {
		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) || ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( [
				'message' => __( 'Security Check Failed.', 'vimeify' ),
			] );
			exit;
		}

		$tool_slug = isset( $_POST['tool'] ) ? sanitize_text_field( wp_unslash( $_POST['tool'] ) ) : null;
		$tool_id   = isset( $_POST['id'] ) ? sanitize_text_field( wp_unslash( $_POST['id'] ) ) : null;
		$tools     = $this->plugin()->system()->tools();

		if ( is_null( $tool_slug ) || ! isset( $tools[ $tool_slug ] ) ) {
			wp_send_json_error( [ 'message' => __( 'Unknown tool selected.', 'vimeify' ) ] );
			exit;
		}


		$step = isset( $_POST['step'] ) ? intval( $_POST['step'] ) : null;
		$page = isset( $_POST['page'] ) ? intval( $_POST['page'] ) : null;

		/* @var BaseTool $tool */
		$tool = $tools[ $tool_slug ];
		$tool->set_id( $tool_id );

		$init = isset( $_POST['init'] ) ? (int) $_POST['init'] : 0;
		if ( $init ) {

			$process = $tool->init_process();
			if ( ! is_wp_error( $process ) ) {
				wp_send_json_success();
			} else {
				if ( 'data_warn' === $process->get_error_code() ) {
					wp_send_json_success( [ 'warning' => $process->get_error_message() ] );
				} else {
					wp_send_json_error( [ 'message' => $process->get_error_message() ] );
				}
			}

		} else {

			$next = $tool->get_next_step( $step, $page );

			if ( is_wp_error( $next ) ) {
				wp_send_json_error( [ 'message' => $next->get_error_message() ] );
				exit;
			} else {
				$result = $tool->do_step( $step, $page );
				if ( $next['next_step'] !== - 1 ) {
					$next['message'] = is_wp_error( $result ) ? $result->get_error_message() : $next['message'];
				} else {
					$tool->mark_as_complete();
				}
				wp_send_json_success( $next );

				exit;
			}
		}


	}

	/**
	 * Obtian the privacy option based on input and plan support.
	 *
	 * @param $input
	 *
	 * @return mixed|string|null
	 *
	 * @since 1.7.0
	 */
	private function get_view_privacy( $input ) {

		$default = $this->plugin->system()->settings()->profile()->get( 'Backend.Form.Other', 'view_privacy' );

		$privacy = $input === 'default' ? 'default' : ( empty( $input ) ? $default : $input );
		if ( $this->plugin->system()->vimeo()->supports_view_privacy_option( $privacy ) ) {
			return $privacy;
		} else {
			return 'default';
		}
	}

	/**
	 * Handle load more gallery videos
	 * @return void
	 */
	public function handle_load_more_gallery_videos() {
		// Verify nonce
		if ( ! check_ajax_referer( 'vimeify_gallery_nonce', 'nonce', false ) ) {
			wp_send_json_error( [ 'message' => __( 'Invalid security token.', 'vimeify' ) ] );
		}

		$gallery_id = isset( $_POST['gallery_id'] ) ? intval( $_POST['gallery_id'] ) : 0;
		$offset = isset( $_POST['offset'] ) ? intval( $_POST['offset'] ) : 0;

		if ( empty( $gallery_id ) ) {
			wp_send_json_error( [ 'message' => __( 'Invalid gallery ID.', 'vimeify' ) ] );
		}

		// Load gallery view with offset
		$view = new \Vimeify\Core\Frontend\Views\GalleryView( $this->plugin );
		$output = $view->output( [
			'id'     => $gallery_id,
			'offset' => $offset,
		] );

		// Extract thumbnails or playlist items HTML
		$dom = new \DOMDocument();
		@$dom->loadHTML( '<?xml encoding="UTF-8">' . $output );
		$xpath = new \DOMXPath( $dom );

		// Try slider thumbnails first
		$items = $xpath->query( '//div[@class="vimeify-gallery-thumbnails"]/div' );
		$container = $xpath->query( '//div[@class="vimeify-gallery-thumbnails"]' )->item( 0 );

		// If not found, try playlist items
		if ( $items->length === 0 ) {
			$items = $xpath->query( '//div[contains(@class, "vimeify-playlist-items")]/div[contains(@class, "vimeify-playlist-item")]' );
			$container = $xpath->query( '//div[contains(@class, "vimeify-playlist-items")]' )->item( 0 );
		}

		$html = '';
		foreach ( $items as $item ) {
			$html .= $dom->saveHTML( $item );
		}

		// Get data attributes from container
		$per_page = $container ? intval( $container->getAttribute( 'data-per-page' ) ) : 10;
		$current_offset = $offset;

		// Check if there's a load more button or loading indicator (indicates more content)
		$has_more_button = $xpath->query( '//button[@class="vimeify-gallery-load-more"]' )->length > 0
			|| $xpath->query( '//div[@class="vimeify-playlist-loading"]' )->length > 0;

		wp_send_json_success( [
			'html'        => $html,
			'offset'      => $current_offset,
			'next_offset' => $current_offset + $per_page,
			'has_more'    => $has_more_button,
		] );
	}

	/**
	 * Handle settings save via AJAX
	 *
	 * @return void
	 */
	public function handle_save_settings() {
		if ( ! check_ajax_referer( 'vimeify_nonce', '_wpnonce', false ) ) {
			wp_send_json_error( array(
				'message' => esc_html__( 'Security Check Failed.', 'vimeify' ),
			) );
		}

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array(
				'message' => esc_html__( 'Permission denied.', 'vimeify' ),
			) );
		}

		// Get current settings
		$settings = $this->plugin->system()->settings()->global();

		// Process submitted data
		// The form sends data in format: section[field][choice] = value
		$sections_to_process = array( 'overview', 'admin', 'frontend', 'sync', 'upload_profiles' );

		foreach ( $sections_to_process as $section ) {
			if ( isset( $_POST[ $section ] ) && is_array( $_POST[ $section ] ) ) {
				foreach ( $_POST[ $section ] as $field_id => $field_value ) {
					$sanitized_key = $section . '.' . sanitize_key( $field_id );

					if ( is_array( $field_value ) ) {
						// Checkbox group - each choice is a separate key
						$sanitized_value = array();
						foreach ( $field_value as $choice_key => $choice_value ) {
							$sanitized_choice_key = sanitize_key( $choice_key );
							$sanitized_value[ $sanitized_choice_key ] = $this->sanitize_setting_value( $choice_value );
						}
						$settings->set( $sanitized_key, $sanitized_value );
					} else {
						// Single value (text, select, radio)
						$settings->set( $sanitized_key, $this->sanitize_setting_value( $field_value ) );
					}
				}
			}
		}

		// Handle checkbox fields that were unchecked (not submitted)
		// We need to clear values that existed before but weren't submitted
		$this->handle_unchecked_checkboxes( $settings, $sections_to_process );

		// Save settings
		$settings->save();

		wp_send_json_success( array(
			'message' => esc_html__( 'Settings saved successfully.', 'vimeify' ),
			'values'  => $settings->all(),
		) );
	}

	/**
	 * Handle unchecked checkboxes by clearing their values
	 *
	 * @param object $settings
	 * @param array  $sections
	 *
	 * @return void
	 */
	protected function handle_unchecked_checkboxes( $settings, $sections ) {
		// Get all current values
		$all_values = $settings->all();

		foreach ( $sections as $section ) {
			if ( ! isset( $all_values[ $section ] ) || ! is_array( $all_values[ $section ] ) ) {
				continue;
			}

			foreach ( $all_values[ $section ] as $field_id => $field_value ) {
				// If this field is a checkbox group (array with numeric-like values)
				if ( is_array( $field_value ) ) {
					$new_value = array();

					foreach ( $field_value as $choice_key => $choice_value ) {
						// Check if this checkbox was submitted
						// phpcs:ignore WordPress.Security.NonceVerification.Missing
						if ( isset( $_POST[ $section ][ $field_id ][ $choice_key ] ) ) {
							$new_value[ $choice_key ] = $this->sanitize_setting_value(
								// phpcs:ignore WordPress.Security.NonceVerification.Missing
								$_POST[ $section ][ $field_id ][ $choice_key ]
							);
						}
						// If not submitted, it means unchecked - don't include it
					}

					$settings->set( $section . '.' . $field_id, $new_value );
				}
			}
		}
	}

	/**
	 * Sanitize a setting value based on its type
	 *
	 * @param mixed $value
	 *
	 * @return mixed
	 */
	protected function sanitize_setting_value( $value ) {
		if ( is_array( $value ) ) {
			return array_map( array( $this, 'sanitize_setting_value' ), $value );
		}

		// Check if it's a numeric value
		if ( is_numeric( $value ) ) {
			return intval( $value );
		}

		// Default to sanitize_text_field
		return sanitize_text_field( wp_unslash( $value ) );
	}
}