<?php
// exit if accessed directly
if ( ! defined( 'ABSPATH' ) )
	exit;

/**
 * Responsive Lightbox Remote Library class.
 *
 * @class Responsive_Lightbox_Remote_Library
 */
class Responsive_Lightbox_Remote_Library {

	public $providers = [];
	private $image_formats = [
		'bmp'	=> 'image/bmp',
		'gif'	=> 'image/gif',
		'jpe'	=> 'image/jpeg',
		'jpeg'	=> 'image/jpeg',
		'jpg'	=> 'image/jpeg',
		'png'	=> 'image/png',
		'tif'	=> 'image/tiff',
		'tiff'	=> 'image/tiff',
		'webp'	=> 'image/webp'
	];

	/**
	 * Class constructor.
	 *
	 * @return void
	 */
	public function __construct() {
		// is remote library active?
		if ( ! Responsive_Lightbox()->options['remote_library']['active'] )
			return;

		// actions
		add_action( 'wp_ajax_rl_remote_library_query', [ $this, 'ajax_query_media' ] );
		add_action( 'wp_ajax_rl_upload_image', [ $this, 'ajax_upload_image' ] );
		add_action( 'admin_enqueue_scripts', [ $this, 'remote_library_scripts' ] );

		// add filter to send new data to editor
		add_filter( 'image_send_to_editor', [ $this, 'send_image_to_editor' ], 21, 8 );
	}

	/**
	 * Hidden field with response data for gallery preview.
	 *
	 * @param array $args Field arguments
	 * @return string
	 */
	public function remote_library_response_data( $args ) {
		// get main instance
		$rl = Responsive_Lightbox();

		// get active providers
		$providers = $this->get_active_providers();

		// normalize provider value
		if ( is_array( $args['media_provider'] ) ) {
			$args['media_provider'] = reset( $args['media_provider'] );
		}
		$args['media_provider'] = sanitize_key( (string) $args['media_provider'] );
		if ( $args['media_provider'] === '' ) {
			$args['media_provider'] = 'all';
		}
		if ( $args['media_provider'] !== 'all' && ! in_array( $args['media_provider'], $providers, true ) ) {
			$args['media_provider'] = 'all';
		}

		$html = '';

		// any providers?
		if ( ! empty( $providers ) ) {
			foreach ( $providers as $provider ) {
				// get provider
				$provider = $rl->providers[$provider];

				// add response data arguments if needed
				if ( ! empty( $provider['response_args'] ) ) {
					$response = $provider['instance']->get_response_data();

					foreach ( $provider['response_args'] as $arg ) {
						if ( array_key_exists( $arg, $response ) ) {
							$html .= '<span id="' . esc_attr( 'rl_' . $args['tab_id'] . '_' . $args['menu_item'] . '_' . $args['field'] . '_' . $provider['slug'] . '_' . $arg ) . '" class="rl-response-data" data-value="' . esc_attr( base64_encode( wp_json_encode( $response[$arg] ) ) ) . '" data-name="' . esc_attr( $arg ) . '" data-provider="' . esc_attr( $provider['slug'] ) . '"></span>';
						}
					}
				}
			}
		}

		return $html;
	}

	/**
	 * Send updated image data to editor.
	 *
	 * @param string $html The image HTML markup to send
	 * @param int $id The attachment ID
	 * @param string $caption The image caption
	 * @param string $title The image title
	 * @param string $align The image alignment
	 * @param string $url The image source URL
	 * @param string|array $size Size of image
	 * @param string $alt The image alternative text.
	 * @return string
	 */
	function send_image_to_editor( $html, $id, $caption, $title, $align, $url, $size, $alt ) {
		if ( $id === Responsive_Lightbox()->galleries->maybe_generate_thumbnail() && isset( $_POST['attachment'] ) && is_array( $_POST['attachment'] ) ) {
			$attachment = wp_unslash( $_POST['attachment'] );

			if ( isset( $attachment['remote_library_image'], $attachment['width'], $attachment['height'] ) ) {
				$html = preg_replace( '/src=(\'|")(.*?)(\'|")/', 'src="' . ( ! empty( $attachment['rl_url'] ) ? esc_url( $attachment['rl_url'] ) : $url ) . '"', $html );
				$html = preg_replace( '/width=(\'|")(.*?)(\'|")/', 'width="' . (int) $attachment['width'] . '"', $html );
				$html = preg_replace( '/height=(\'|")(.*?)(\'|")/', 'height="' . (int) $attachment['height'] . '"', $html );
				$html = preg_replace( '/(\s)?id="attachment_' . (int) $id . '"/', '', $html );
				$html = preg_replace( '/(\s)?wp-image-' . (int) $id . '/', '', $html );
			}
		}

		return $html;
	}

	/**
	 * Get all available providers.
	 *
	 * @return array
	 */
	public function get_providers() {
		return (array) apply_filters( 'rl_get_providers', Responsive_Lightbox()->providers );
	}

	/**
	 * Get all active providers.
	 *
	 * @return array
	 */
	public function get_active_providers() {
		$providers = $this->get_providers();
		$active_providers = [];

		foreach ( $providers as $provider => $data ) {
			if ( $this->is_provider_enabled( $provider ) )
				$active_providers[] = $provider;
		}

		return (array) apply_filters( 'rl_get_active_providers', $active_providers );
	}

	/**
	 * Check whether provider is active.
	 *
	 * @param string $provider Media provider
	 * @return bool
	 */
	public function is_active_provider( $provider ) {
		$is_active = $this->is_provider_enabled( $provider );

		return (bool) apply_filters( 'rl_is_active_provider', $is_active, $provider );
	}

	/**
	 * Check whether a provider is enabled in settings.
	 *
	 * Supports both legacy key: [provider][active]
	 * and migrated key pattern: [provider][provider_active].
	 *
	 * @param string $provider Provider slug.
	 * @return bool
	 */
	private function is_provider_enabled( $provider ) {
		$rl = Responsive_Lightbox();
		$remote_settings = isset( $rl->options['remote_library'] ) && is_array( $rl->options['remote_library'] )
			? $rl->options['remote_library']
			: [];
		$alt_key = $provider . '_active';

		if ( array_key_exists( $provider, $remote_settings ) ) {
			$provider_settings = $remote_settings[$provider];

			// Migrated key ({provider}_active) takes priority over legacy key (active)
			// to prevent false negatives when both exist with conflicting values.
			if ( is_array( $provider_settings ) ) {
				if ( isset( $provider_settings[$alt_key] ) ) {
					return $this->normalize_bool_value( $provider_settings[$alt_key] );
				}

				if ( isset( $provider_settings['active'] ) ) {
					return $this->normalize_bool_value( $provider_settings['active'] );
				}
			} else {
				// Fallback for scalar provider values.
				return $this->normalize_bool_value( $provider_settings );
			}
		}

		// Fallback shape: remote_library[provider_active]
		if ( isset( $remote_settings[$alt_key] ) ) {
			return $this->normalize_bool_value( $remote_settings[$alt_key] );
		}

		// Fallback to plugin defaults when option payload does not carry provider keys.
		if ( isset( $rl->defaults['remote_library'][$provider] ) && is_array( $rl->defaults['remote_library'][$provider] ) ) {
			$default_provider = $rl->defaults['remote_library'][$provider];
			if ( isset( $default_provider['active'] ) ) {
				return $this->normalize_bool_value( $default_provider['active'] );
			}
			if ( isset( $default_provider[$alt_key] ) ) {
				return $this->normalize_bool_value( $default_provider[$alt_key] );
			}
		}

		// Fallback to provider registration defaults.
		if ( isset( $rl->providers[$provider]['defaults'] ) && is_array( $rl->providers[$provider]['defaults'] ) ) {
			$provider_defaults = $rl->providers[$provider]['defaults'];
			if ( isset( $provider_defaults['active'] ) ) {
				return $this->normalize_bool_value( $provider_defaults['active'] );
			}
			if ( isset( $provider_defaults[$alt_key] ) ) {
				return $this->normalize_bool_value( $provider_defaults[$alt_key] );
			}
		}

		return false;
	}

	/**
	 * Normalize mixed bool-like values from options.
	 *
	 * @param mixed $value Value to normalize.
	 * @return bool
	 */
	private function normalize_bool_value( $value ) {
		if ( is_bool( $value ) ) {
			return $value;
		}

		if ( is_string( $value ) ) {
			$value = strtolower( trim( $value ) );
			if ( $value === '' || $value === 'false' || $value === '0' ) {
				return false;
			}
			if ( $value === 'true' || $value === '1' ) {
				return true;
			}
		}

		return ! empty( $value );
	}

	/**
	 * Scripts and styles for media frame.
	 *
	 * @global string $wp_version
	 * @global string $pagenow
	 *
	 * @return void
	 */
	public function remote_library_scripts() {
		global $wp_version;
		global $pagenow;

		// display only for post edit pages
		if ( ! ( ( $pagenow === 'post.php' || $pagenow === 'post-new.php' ) || ( version_compare( $wp_version, '5.8', '>=' ) && ( $pagenow === 'widgets.php' || $pagenow === 'customize.php' ) ) ) )
			return;

		// get main instance
		$rl = Responsive_Lightbox();

		wp_enqueue_script( 'responsive-lightbox-remote-library-media', RESPONSIVE_LIGHTBOX_URL . '/js/admin-media.js', [ 'jquery', 'media-models', 'underscore' ], $rl->defaults['version'] );

		// prepare script data
		$script_data = [
			'thumbnailID'			=> $rl->galleries->maybe_generate_thumbnail(),
			'postID'				=> get_the_ID(),
			'providers'				=> $this->get_providers(),
			'providersActive'		=> $this->get_active_providers(),
			'allProviders'			=> esc_html__( 'All providers', 'responsive-lightbox' ),
			'uploadAndInsert'		=> esc_html__( 'Upload and Insert', 'responsive-lightbox' ),
			'uploadAndSelect'		=> esc_html__( 'Upload and Select', 'responsive-lightbox' ),
			'filterByremoteLibrary'	=> esc_html__( 'Filter by remote library', 'responsive-lightbox' ),
			'getUploadNonce'		=> wp_create_nonce( 'rl-remote-library-upload-image' ),
			'queryNonce'			=> wp_create_nonce( 'rl-remote-library-query' )
		];

		wp_add_inline_script( 'responsive-lightbox-remote-library-media', 'var rlRemoteLibraryMedia = ' . wp_json_encode( $script_data ) . ";\n", 'before' );

		// enqueue gallery
		$rl->galleries->enqueue_gallery_scripts_styles();
	}

	/**
	 * AJAX media query action.
	 *
	 * @return void
	 */
	public function ajax_query_media() {
		$data = stripslashes_deep( $_POST );

		// check rate limiting (30 queries per minute to prevent API abuse)
		if ( ! Responsive_Lightbox()->check_rate_limit( 'rl_ajax_query_media', 30, 60 ) ) {
			wp_send_json_error( esc_html__( 'Rate limit exceeded. Please try again later.', 'responsive-lightbox' ) );
		}

		// check user capabilities
		if ( ! current_user_can( 'upload_files' ) )
			wp_send_json_error( esc_html__( 'Insufficient permissions.', 'responsive-lightbox' ) );

		// verify nonce
		if ( ! isset( $data['nonce'] ) || ! wp_verify_nonce( $data['nonce'], 'rl-remote-library-query' ) )
			wp_send_json_error( esc_html__( 'Invalid nonce.', 'responsive-lightbox' ) );

		$results = [
			'last'		=> false,
			'images'	=> [],
			'data'		=> []
		];

		if ( isset( $data['media_provider'], $data['media_search'], $data['media_page'] ) ) {
			$data['media_provider'] = sanitize_key( $data['media_provider'] );

			if ( $data['media_provider'] === 'all' || $this->is_active_provider( $data['media_provider'] ) ) {
				$data['preview_page'] = (int) $data['media_page'];
				$data['preview_per_page'] = 20;

				// get images
				$results['images'] = $this->get_remote_library_images( $data );

				// get main instance
				$rl = Responsive_Lightbox();

				// single provider?
				if ( $data['media_provider'] !== 'all' ) {
					// get provider
					$provider = $rl->providers[$data['media_provider']];

					// add response data arguments if needed
					if ( ! empty( $provider['response_args'] ) ) {
						$response = $provider['instance']->get_response_data();

						foreach ( $provider['response_args'] as $arg ) {
							if ( array_key_exists( $arg, $response ) )
								$results['data'][$provider['slug']][$arg] = base64_encode( wp_json_encode( $response[$arg] ) );
						}
					}
				} else {
					// get active providers
					$providers = $this->get_active_providers();

					if ( ! empty( $providers ) ) {
						foreach ( $providers as $provider ) {
							// get provider
							$provider = $rl->providers[$provider];

							// add response data arguments if needed
							if ( ! empty( $provider['response_args'] ) ) {
								$response = $provider['instance']->get_response_data();

								foreach ( $provider['response_args'] as $arg ) {
									if ( array_key_exists( $arg, $response ) )
										$results['data'][$provider['slug']][$arg] = base64_encode( wp_json_encode( $response[$arg] ) );
								}
							}
						}
					}
				}

				if ( ! empty( $results['images'] ) ) {
					// create WP compatible attachments
					$results['images'] = $this->create_wp_remote_attachments( $results['images'], $data );

					// handle last page if needed
					$results['last'] = apply_filters( 'rl_remote_library_query_last_page', false, $results, $data );
				} else
					$results['last'] = true;
			}
		}

		// send data
		wp_send_json( $results );
	}

	/**
	 * AJAX upload image action.
	 *
	 * @return void
	 */
	public function ajax_upload_image() {
		// clear post data
		$data = stripslashes_deep( $_POST );

		// default result
		$result = [
			'id'		=> 0,
			'full'		=> [ '', 0, 0, false ],
			'error'		=> false,
			'message'	=> ''
		];

		// verified upload?
		if ( current_user_can( 'upload_files' ) && isset( $data['rlnonce'], $data['image'], $data['post_id'] ) && wp_verify_nonce( $data['rlnonce'], 'rl-remote-library-upload-image' ) ) {
			// check rate limiting (10 uploads per minute)
			if ( ! Responsive_Lightbox()->check_rate_limit( 'rl_upload_image', 10, 60 ) ) {
				$result['error'] = true;
				$result['message'] = __( 'Rate limit exceeded. Please try again later.', 'responsive-lightbox' );
				wp_send_json( $result );
			}

			// include required files if needed
			if ( ! function_exists( 'media_handle_upload' ) )
				require_once( path_join( ABSPATH, 'wp-admin/includes/media.php' ) );

			if ( ! function_exists( 'wp_handle_upload' ) )
				require_once( path_join( ABSPATH, 'wp-admin/includes/file.php' ) );

			// get media provider
			$media_provider = ! empty( $data['image']['media_provider'] ) ? sanitize_key( $data['image']['media_provider'] ) : '';

			// get active providers
			$providers = $this->get_active_providers();

			if ( in_array( $media_provider, $providers, true ) ) {
				// get image formats
				$image_formats = $this->get_allowed_image_formats( $media_provider );

				if ( ! empty( $data['image']['url'] ) && ! empty( $data['image']['mime'] ) && ! empty( $data['image']['subtype'] ) && array_key_exists( $data['image']['subtype'], $image_formats ) ) {
					// get image url
					$image_url = esc_url_raw( $data['image']['url'] );

					// get allowed hosts
					$hosts = $this->get_allowed_hosts( $media_provider );

					$valid_host = false;

					if ( ! empty( $hosts ) ) {
						// get image host
						$image_host = parse_url( $image_url, PHP_URL_HOST );

						// validate that we got a valid hostname
						if ( ! is_string( $image_host ) || $image_host === '' ) {
							$result['error'] = true;
							$result['message'] = __( 'Invalid image URL.', 'responsive-lightbox' );
						} else {
							// normalize hostname to lowercase for case-insensitive comparison
							$image_host = strtolower( $image_host );

							// check allowed hosts - strict validation to prevent SSRF bypasses
							foreach ( $hosts as $host ) {
								$host = strtolower( $host );

								// Validate exact match or valid subdomain (e.g., upload.wikimedia.org matches wikimedia.org)
								// Prevent substring bypass: evil-wikimedia.org or upload.wikimedia.org.evil.com must NOT match
								if ( $image_host === $host || substr( $image_host, -( strlen( $host ) + 1 ) ) === '.' . $host ) {
									$valid_host = true;

									// no need to check rest of the hosts
									break;
								}
							}
						}
					} else {
						$valid_host = true;
					}

					if ( $valid_host && empty( $result['error'] ) ) {
						// get max image size (ensure at least 1MB)
						$max_size = max( 1, absint( Responsive_Lightbox()->options['remote_library']['max_image_size'] ) ) * 1024 * 1024;

					// check image size via HEAD request - use wp_safe_remote_head for SSRF protection
						$head_response = wp_safe_remote_head( $image_url );
						$skip_size_check = false;

						if ( is_wp_error( $head_response ) ) {
							$skip_size_check = true;
						} else {
							$content_length = wp_remote_retrieve_header( $head_response, 'content-length' );

							if ( $content_length && (int) $content_length > $max_size ) {
								$result['error'] = true;
								$result['message'] = __( 'Image size exceeds maximum allowed size.', 'responsive-lightbox' );
							}
						}

					if ( empty( $result['error'] ) ) {
							// get image as binary data with timeout and size limit to prevent memory exhaustion
							$response = wp_safe_remote_get( $image_url, [
								'timeout' => 30,
								'limit_response_size' => $max_size + 1024, // Add 1KB buffer for headers
							] );

							// no errors?
							if ( ! is_wp_error( $response ) ) {
								// get image binary data
								$image_bits = wp_remote_retrieve_body( $response );

								// check body size as validation
								// Note: limit_response_size prevents memory exhaustion, but we still validate the actual size
								$body_size = strlen( $image_bits );
								if ( $body_size > $max_size ) {
									$result['error'] = true;
									$result['message'] = __( 'Image size exceeds maximum allowed size.', 'responsive-lightbox' );
								}

								if ( empty( $result['error'] ) ) {
									// get sanitized file name
									$file_name = sanitize_file_name( pathinfo( $data['image']['name'], PATHINFO_BASENAME ) );

									// get file extension
									$file_ext = pathinfo( $file_name, PATHINFO_EXTENSION );

									// no extension?
									if ( $file_ext === '' || ! array_key_exists( $file_ext, $image_formats ) ) {
										$file_name .= '.jpg';
										$file_ext = 'jpg';
									}

									// strict file validation using wp_check_filetype with allowed formats whitelist
									$check = wp_check_filetype( $file_name, $image_formats );

									// validate extension is allowed and mime type matches
									if ( $check['ext'] && $check['type'] && array_key_exists( $check['ext'], $image_formats ) && $check['type'] === $data['image']['mime'] ) {
										// upload image
										$uploaded_image = wp_upload_bits( $file_name, null, $image_bits, current_time( 'Y/m' ) );

										if ( isset( $uploaded_image['error'] ) && $uploaded_image['error'] ) {
											$result['error'] = true;
											$result['message'] = $uploaded_image['error'];
										} else {
											// get file name
											$file_name = pathinfo( $uploaded_image['file'], PATHINFO_BASENAME );

											// simulate upload
											$_FILES['rl-remote-image'] = [
												'error'		=> 0,
												'name'		=> $file_name,
												'tmp_name'	=> $uploaded_image['file'],
												'size'		=> filesize( $uploaded_image['file'] )
											];

											// get post id
											$post_id = isset( $data['post_id'] ) ? (int) $data['post_id'] : 0;

											// more reliable mime type checking
											$check = wp_check_filetype_and_ext( $uploaded_image['file'], $file_name );

											// correct mime type and extension?
											if ( strpos( $data['image']['mime'], 'image/' ) === 0 && $check['type'] === $data['image']['mime'] && $check['ext'] !== false && wp_get_image_mime( $uploaded_image['file'] ) === $check['type'] ) {
												// upload image, wp handle sanitization and validation here
												$attachment_id = media_handle_upload(
													'rl-remote-image',
													$post_id,
													[
														'post_title'	=> empty( $data['image']['title'] ) ? $file_name : $data['image']['title'],
														'post_content'	=> empty( $data['image']['description'] ) ? '' : $data['image']['description'],
														'post_excerpt'	=> empty( $data['image']['caption'] ) ? '' : $data['image']['caption']
													],
													[
														'action'	=> 'rl_remote_library_handle_upload',
														'test_form'	=> false
													]
												);

												// upload success?
												if ( ! is_wp_error( $attachment_id ) ) {
													add_post_meta( $attachment_id, '_wp_attachment_image_alt', empty( $data['image']['alt'] ) ? '' : $data['image']['alt'] );

													$result['id'] = $attachment_id;
													$result['full'] = wp_get_attachment_image_src( $attachment_id, 'full' );
												} else {
													$result['error'] = true;
													$result['message'] = $attachment_id->get_error_message();
												}
											// file still exists?
											} elseif ( file_exists( $uploaded_image['file'] ) ) {
												$result['error'] = true;
												$result['message'] = __( 'Invalid MIME type', 'responsive-lightbox' );

												// delete file
												wp_delete_file( $uploaded_image['file'] );
											}
										}
									} else {
										$result['error'] = true;
										$result['message'] = __( 'Invalid image type', 'responsive-lightbox' );
									}
								} else {
									$result['error'] = true;
									$result['message'] = $response->get_error_message();
								}
							}
						}
					} elseif ( empty( $result['error'] ) ) {
						// Only set "Invalid host" if no previous error was set (e.g., "Invalid image URL")
						$result['error'] = true;
						$result['message'] = __( 'Invalid host', 'responsive-lightbox' );
					}
				} else {
					$result['error'] = true;
					$result['message'] = __( 'Missing or invalid image data', 'responsive-lightbox' );
				}
			} else {
				$result['error'] = true;
				$result['message'] = __( 'Invalid media provider', 'responsive-lightbox' );
			}
		} else {
			$result['error'] = true;
			$result['message'] = __( 'Access denied', 'responsive-lightbox' );
		}

		// send data
		wp_send_json( $result );
	}

	/**
	 * Create WP compatible attachments for JavaScript.
	 *
	 * @param array $results Requested images
	 * @param array $args Additional arguments
	 * @return array
	 */
	public function create_wp_remote_attachments( $results, $args ) {
		// get current user
		$user = wp_get_current_user();

		// copy results
		$copy = $results;

		// get current time
		$time = current_time( 'timestamp' );

		// get date format
		$date_format = get_option( 'date_format' );

		// format date
		$date = date_i18n( __( 'F j Y' ), $time );

		// $result is already sanitized by specific provider sanitize_result function
		foreach ( $results as $no => $result ) {
			// make sure those attributes are strings
			$copy[$no]['caption'] = (string) $result['caption'];
			$copy[$no]['description'] = (string) $result['description'];
			$copy[$no]['title'] = (string) $result['title'];
			$copy[$no]['filename'] = $copy[$no]['name'] = (string) $result['filename'];

			// rest of attributes
			$copy[$no]['id'] = 'rl-attachment-' . ( ( $args['preview_page'] - 1 ) * $args['preview_per_page'] + $no ) . '-' . $args['media_provider'];
			$copy[$no]['remote_library_image'] = true;
			$copy[$no]['author'] = $user->ID;
			$copy[$no]['authorName'] = esc_html( $user->user_login );
			$copy[$no]['can'] = [
				'save' => true,
				'remove' => false
			];
			$copy[$no]['compat'] = '';
			$copy[$no]['date'] = $time;
			$copy[$no]['dateFormatted'] = $date;
			$copy[$no]['delete'] = '';
			$copy[$no]['edit'] = '';
			$copy[$no]['update'] = '';
			$copy[$no]['filesizeHumanReadable'] = '';
			$copy[$no]['filesizeInBytes'] = 0;
			$copy[$no]['icon'] = '';
			$copy[$no]['link'] = $result['url'];
			$copy[$no]['menuOrder'] = 0;
			$copy[$no]['meta'] = false;

			// check extension
			$file_ext = pathinfo( $result['url'], PATHINFO_EXTENSION );

			// get image formats
			$image_formats = $this->get_allowed_image_formats( $args['media_provider'] );

			if ( array_key_exists( $file_ext, $image_formats ) ) {
				$copy[$no]['mime'] = $image_formats[$file_ext];
				$copy[$no]['subtype'] = $file_ext;
			} else {
				$copy[$no]['mime'] = 'image/jpeg';
				$copy[$no]['subtype'] = 'jpg';
			}

			$copy[$no]['modified'] = $time;
			$copy[$no]['nonces'] = [
				'delete'	=> '',
				'edit'		=> '',
				'update'	=> ''
			];
			$copy[$no]['orientation'] = $result['orientation'];
			$copy[$no]['status'] = 'inherit';
			$copy[$no]['type'] = 'image';
			$copy[$no]['uploadedTo'] = 0;
			$copy[$no]['uploadedToLink'] = '';
			$copy[$no]['uploadedToTitle'] = '';
			$copy[$no]['sizes'] = [
				'medium' => [
					'height'		=> $result['thumbnail_height'],
					'width'			=> $result['thumbnail_width'],
					'orientation'	=> $result['thumbnail_orientation'],
					'url'			=> $result['thumbnail_url']
				],
				'full' => [
					'height'		=> $result['height'],
					'width'			=> $result['width'],
					'orientation'	=> $result['orientation'],
					'url'			=> $result['url']
				]
			];
		}

		return (array) apply_filters( 'rl_remote_library_wp_attachments', $copy, $args );
	}

	/**
	 * Remote library media query.
	 *
	 * @param array $args
	 * @return array
	 */
	public function get_remote_library_images( $args ) {
		$args = stripslashes_deep( $args );

		// search phrase
		if ( isset( $args['media_search'] ) )
			$args['media_search'] = strtolower( trim( $args['media_search'] ) );
		else
			$args['media_search'] = '';

		// media provider
		if ( isset( $args['media_provider'] ) )
			$args['media_provider'] = trim( $args['media_provider'] );
		else
			$args['media_provider'] = 'all';

		// page number
		if ( isset( $args['preview_page'] ) )
			$args['preview_page'] = (int) $args['preview_page'];
		else
			$args['preview_page'] = 1;

		// number of images per page
		if ( isset( $args['preview_per_page'] ) )
			$args['preview_per_page'] = (int) $args['preview_per_page'];
		else
			$args['preview_per_page'] = 20;

		// get active providers
		$providers = $this->get_active_providers();

		// prepare valid providers
		$valid_providers = [];

		if ( $args['media_provider'] === 'all' )
			$valid_providers = $providers;
		elseif ( in_array( $args['media_provider'], $providers, true ) )
			$valid_providers[] = $args['media_provider'];

		$results = [];

		// any valid providers?
		if ( ! empty( $valid_providers ) ) {
			// get main instance
			$rl = Responsive_Lightbox();

			foreach ( $valid_providers as $provider_name ) {
				if ( ! empty( $args['response_data'][$provider_name] ) ) {
					// get provider
					$provider = $rl->providers[$provider_name];

					if ( ! empty( $provider['response_args'] ) ) {
						foreach ( $provider['response_args'] as $arg ) {
							if ( array_key_exists( $arg, $args['response_data'][$provider_name] ) ) {
								$base64 = base64_decode( $args['response_data'][$provider_name][$arg] );

								if ( ! empty( $base64 ) )
									$args['response_data'][$provider_name][$arg] = json_decode( $base64, true );
							}
						}
					}
				}

				// get results
				$results = apply_filters( 'rl_remote_library_query', $results, $args['media_search'], $provider_name, $args );

				// number of results
				$nor = count( $results );

				// more than requested images?
				if ( $nor > $args['preview_per_page'] ) {
					// get part of images
					$results = array_slice( $results, 0, $args['preview_per_page'], true );

					break;
				// same amount of images?
				} elseif ( $nor === $args['preview_per_page'] )
					break;
			}
		}

		return $results;
	}

	/**
	 * Get allowed hosts.
	 *
	 * @param string $provider
	 * @return array
	 */
	public function get_allowed_hosts( $provider ) {
		// get active providers
		$providers = $this->get_active_providers();

		if ( in_array( $provider, $providers, true ) ) {
			// get available provider host
			$hosts = Responsive_Lightbox()->providers[$provider]['instance']->get_allowed_hosts();
		} else
			$hosts = [];

		return $hosts;
	}

	/**
	 * Get allowed image formats.
	 *
	 * @param string $provider
	 * @return array
	 */
	public function get_allowed_image_formats( $provider = 'all' ) {
		if ( $provider === 'all' ) {
			$image_formats = $this->image_formats;
		} else {
			// get active providers
			$providers = $this->get_active_providers();

			if ( in_array( $provider, $providers, true ) ) {
				// get available provider image formats
				$image_formats = Responsive_Lightbox()->providers[$provider]['instance']->get_allowed_formats();
			} else
				$image_formats = [];
		}

		return $image_formats;
	}
}
