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

if ( ! class_exists( 'WPSC_Attachment' ) ) :

	final class WPSC_Attachment {

		/**
		 * Object data in key => val pair.
		 *
		 * @var array
		 */
		private $data = array();

		/**
		 * Set whether or not current object properties modified
		 *
		 * @var boolean
		 */
		private $is_modified = false;

		/**
		 * Schema for this model
		 *
		 * @var array
		 */
		public static $schema = array();

		/**
		 * Prevent fields to modify
		 *
		 * @var array
		 */
		public static $prevent_modify = array();

		/**
		 * Catche for search results for ticket list of attachments.
		 *
		 * @var array
		 */
		private static $tl_search_items;

		/**
		 * Initialize this class
		 *
		 * @return void
		 */
		public static function init() {

			// Apply schema for this model.
			add_action( 'init', array( __CLASS__, 'apply_schema' ), 2 );

			// Get object of this class.
			add_filter( 'wpsc_load_ref_classes', array( __CLASS__, 'load_ref_class' ) );

			// File upload.
			add_action( 'wp_ajax_wpsc_file_upload', array( __CLASS__, 'file_upload' ) );
			add_action( 'wp_ajax_nopriv_wpsc_file_upload', array( __CLASS__, 'file_upload' ) );

			// Custom image in tinymce.
			add_action( 'wp_ajax_wpsc_add_custom_image_tinymce', array( __CLASS__, 'add_custom_image_tinymce' ) );
			add_action( 'wp_ajax_nopriv_wpsc_add_custom_image_tinymce', array( __CLASS__, 'add_custom_image_tinymce' ) );
			add_action( 'wp_ajax_wpsc_edit_custom_image_tinymce', array( __CLASS__, 'edit_custom_image_tinymce' ) );
			add_action( 'wp_ajax_nopriv_wpsc_edit_custom_image_tinymce', array( __CLASS__, 'edit_custom_image_tinymce' ) );

			// Check download file.
			add_action( 'init', array( __CLASS__, 'check_download_file' ), 100 );

			// Upload tinymce image.
			add_action( 'wp_ajax_wpsc_tinymce_upload_file', array( __CLASS__, 'tinymce_upload_file' ) );
			add_action( 'wp_ajax_nopriv_wpsc_tinymce_upload_file', array( __CLASS__, 'tinymce_upload_file' ) );

			// Attachment garbage collector.
			add_action( 'wpsc_attach_garbage_collector', array( __CLASS__, 'garbage_collector' ) );
		}

		/**
		 * Change file upload path
		 *
		 * @param array $param - file upload array.
		 * @return array
		 */
		public static function wpsc_upload_dir( $param ) {

			$today = new DateTime();
			$file_path = $param['basedir'] . '/wpsc/' . $today->format( 'Y' ) . '/' . $today->format( 'm' );
			if ( ! file_exists( $file_path ) ) {
				mkdir( $file_path, 0755, true );
			}

			$param['path'] = $file_path;

			return $param;
		}

		/**
		 * Custom upload mime types
		 *
		 * @param array $existing_mimes - file upload array.
		 * @return array
		 */
		public static function wpsc_custom_upload_mimes( $existing_mimes ) {

			$settings = get_option( 'wpsc-gs-file-attachments', array() );

			// Merge allowed-file-ext-mimes first.
			if ( isset( $settings['allowed-file-ext-mimes'] ) ) {
				$existing_mimes = array_merge( $existing_mimes, $settings['allowed-file-ext-mimes'] );
			}

			// Then apply mime-exceptions to give them priority.
			if ( isset( $settings['mime-exceptions'] ) && is_array( $settings['mime-exceptions'] ) ) {
				foreach ( $settings['mime-exceptions'] as $type ) {
					$type_array = explode( ':', $type );
					if ( count( $type_array ) === 2 ) {
						$existing_mimes[ $type_array[0] ] = $type_array[1];
					}
				}
			}

			return $existing_mimes;
		}

		/**
		 * Apply schema for this model
		 *
		 * @return void
		 */
		public static function apply_schema() {

			$schema       = array(
				'id'           => array(
					'has_ref'          => false,
					'ref_class'        => '',
					'has_multiple_val' => false,
				),
				'name'         => array(
					'has_ref'          => false,
					'ref_class'        => '',
					'has_multiple_val' => false,
				),
				'file_path'    => array(
					'has_ref'          => false,
					'ref_class'        => '',
					'has_multiple_val' => false,
				),
				'is_image'     => array(
					'has_ref'          => false,
					'ref_class'        => '',
					'has_multiple_val' => false,
				),
				'is_active'    => array(
					'has_ref'          => false,
					'ref_class'        => '',
					'has_multiple_val' => false,
				),
				'is_uploaded'  => array(
					'has_ref'          => false,
					'ref_class'        => '',
					'has_multiple_val' => false,
				),
				'date_created' => array(
					'has_ref'          => true,
					'ref_class'        => 'datetime',
					'has_multiple_val' => false,
				),
				'source'       => array(
					'has_ref'          => false,
					'ref_class'        => '',
					'has_multiple_val' => false,
				),
				'source_id'    => array(
					'has_ref'          => false,
					'ref_class'        => '',
					'has_multiple_val' => false,
				),
				'ticket_id'    => array(
					'has_ref'          => false,
					'ref_class'        => '',
					'has_multiple_val' => false,
				),
				'customer_id'  => array(
					'has_ref'          => false,
					'ref_class'        => '',
					'has_multiple_val' => false,
				),
			);
			self::$schema = apply_filters( 'wpsc_attachment_schema', $schema );

			// Prevent modify.
			$prevent_modify       = array( 'id' );
			self::$prevent_modify = apply_filters( 'wpsc_attachment_prevent_modify', $prevent_modify );
		}

		/**
		 * Model constructor
		 *
		 * @param int $id - Optional. Data record id to retrive object for.
		 */
		public function __construct( $id = 0 ) {

			global $wpdb;

			$id = intval( $id );

			if ( $id > 0 ) {

				$attachment = $wpdb->get_row( "SELECT * FROM {$wpdb->prefix}psmsc_attachments WHERE id = " . $id, ARRAY_A );
				if ( ! is_array( $attachment ) ) {
					return;
				}

				foreach ( $attachment as $key => $val ) {
					$this->data[ $key ] = $val !== null ? $val : '';
				}
			}
		}

		/**
		 * Convert object into an array
		 *
		 * @return array
		 */
		public function to_array() {

			return $this->data;
		}

		/**
		 * Magic get function to use with object arrow function
		 *
		 * @param string $var_name - variable name.
		 * @return mixed
		 */
		public function __get( $var_name ) {

			if ( ! isset( $this->data[ $var_name ] ) ||
				$this->data[ $var_name ] == null ||
				$this->data[ $var_name ] == ''
			) {
				return self::$schema[ $var_name ]['has_multiple_val'] ? array() : '';
			}

			if ( self::$schema[ $var_name ]['has_multiple_val'] ) {

				$response = array();
				$values   = $this->data[ $var_name ] ? explode( '|', $this->data[ $var_name ] ) : array();
				foreach ( $values as $val ) {
					$response[] = self::$schema[ $var_name ]['has_ref'] ?
									WPSC_Functions::get_object( self::$schema[ $var_name ]['ref_class'], $val ) :
									$val;
				}
				return $response;

			} else {

				return self::$schema[ $var_name ]['has_ref'] && $this->data[ $var_name ] ?
					WPSC_Functions::get_object( self::$schema[ $var_name ]['ref_class'], $this->data[ $var_name ] ) :
					$this->data[ $var_name ];
			}
		}

		/**
		 * Magic function to use setting object field with arrow function
		 *
		 * @param string $var_name - (Required) property slug.
		 * @param mixed  $value - (Required) value to set for a property.
		 * @return void
		 */
		public function __set( $var_name, $value ) {

			if (
				! isset( $this->data[ $var_name ] ) ||
				in_array( $var_name, self::$prevent_modify )
			) {
				return;
			}

			$data_val = '';
			if ( self::$schema[ $var_name ]['has_multiple_val'] ) {

				$data_vals = array_map(
					fn( $val ) => is_object( $val ) ? WPSC_Functions::set_object( self::$schema[ $var_name ]['ref_class'], $val ) : $val,
					$value
				);

				$data_val = $data_vals ? implode( '|', $data_vals ) : '';

			} else {

				$data_val = is_object( $value ) ? WPSC_Functions::set_object( self::$schema[ $var_name ]['ref_class'], $value ) : $value;
			}

			if ( $this->data[ $var_name ] == $data_val ) {
				return;
			}

			$this->data[ $var_name ] = $data_val;
			$this->is_modified       = true;
		}

		/**
		 * Save changes made
		 *
		 * @return boolean
		 */
		public function save() {

			global $wpdb;

			if ( ! $this->is_modified ) {
				return true;
			}

			$data    = $this->data;
			$success = true;

			if ( ! isset( $data['id'] ) ) {

				$at = self::insert( $data );
				if ( $at ) {
					$this->data = $at->data;
					$success    = true;
				} else {
					$success = false;
				}
			} else {

				unset( $data['id'] );
				$success = $wpdb->update(
					$wpdb->prefix . 'psmsc_attachments',
					$data,
					array( 'id' => $this->data['id'] )
				);
			}
			$this->is_modified = false;
			return $success ? true : false;
		}

		/**
		 * Insert new attachment
		 *
		 * @param array $data - insert data.
		 * @return WPSC_Attachment
		 */
		public static function insert( $data ) {

			global $wpdb;

			$data['is_active'] = isset( $data['is_active'] ) ? $data['is_active'] : 0;

			$success = $wpdb->insert(
				$wpdb->prefix . 'psmsc_attachments',
				$data
			);

			if ( ! $success ) {
				return false;
			}

			$attachment = new WPSC_Attachment( $wpdb->insert_id );
			return $attachment;
		}

		/**
		 * Make it inactive so that garbage collector will delete files associated in
		 * background and then delete the record. This will improve its performance.
		 *
		 * @param WPSC_Attachment $attachment - attachment object.
		 * @return boolean
		 */
		public static function destroy( $attachment ) {

			$attachment->is_active = 0;
			$attachment->save();
			return true;
		}

		/**
		 * Set data to create new object using direct data. Used in find method
		 *
		 * @param array $data - data to set for object.
		 * @return void
		 */
		private function set_data( $data ) {

			foreach ( $data as $var_name => $val ) {
				$this->data[ $var_name ] = $val !== null ? $val : '';
			}
		}

		/**
		 * Find records based on given filters
		 *
		 * @param array   $filter - array containing array items like search, where, orderby, order, page_no, items_per_page, etc.
		 * @param boolean $is_object - return data as array or object. Default object.
		 * @return mixed
		 */
		public static function find( $filter = array(), $is_object = true ) {

			global $wpdb;

			$sql   = 'SELECT * FROM ' . $wpdb->prefix . 'psmsc_attachments ';
			$where = self::get_where( $filter );

			$filter['items_per_page'] = isset( $filter['items_per_page'] ) ? $filter['items_per_page'] : 5;
			$filter['page_no']        = isset( $filter['page_no'] ) ? $filter['page_no'] : 1;
			$filter['orderby']        = isset( $filter['orderby'] ) ? $filter['orderby'] : 'id';
			$filter['order']          = isset( $filter['order'] ) ? $filter['order'] : 'ASC';

			$order = WPSC_Functions::parse_order( $filter );
			$limit = WPSC_Functions::parse_limit( $filter );

			$sql = $sql . $where . $order . $limit;
			$results = $wpdb->get_results( $sql, ARRAY_A );

			// total results.
			$sql   = 'SELECT count(id) FROM ' . $wpdb->prefix . 'psmsc_attachments ';
			$total_items = $wpdb->get_var( $sql . $where );

			$response = WPSC_Functions::parse_response( $results, $total_items, $filter );

			// Return array.
			if ( ! $is_object ) {
				return $response;
			}

			// create and return array of objects.
			$temp_results = array();
			foreach ( $response['results'] as $attachment ) {

				$ob   = new WPSC_Attachment();
				$data = array();
				foreach ( $attachment as $key => $val ) {
					$data[ $key ] = $val;
				}
				$ob->set_data( $data );
				$temp_results[] = $ob;
			}
			$response['results'] = $temp_results;

			return $response;
		}

		/**
		 * Get where for find method
		 *
		 * @param array $filter - user filter.
		 * @return array
		 */
		private static function get_where( $filter ) {

			$where = array( '1=1' );

			// Set user defined filters.
			$meta_query = isset( $filter['meta_query'] ) ? $filter['meta_query'] : array();
			if ( $meta_query ) {
				$where[] = WPSC_Functions::parse_user_filters( __CLASS__, $meta_query );
			}

			// Search.
			$search = WPSC_Functions::get_filter_search_str( $filter );
			if ( $search ) {
				$search_query = array(
					'CONVERT(name USING utf8) LIKE \'%' . $search . '%\'',
				);
				$search_query = apply_filters( 'wpsc_attachment_search_query', $search_query, $filter );
				$where[]      = '( ' . implode( ' OR ', $search_query ) . ' )';
			}

			return 'WHERE ' . implode( ' AND ', $where ) . ' ';
		}

		/**
		 * Load current class to reference classes
		 *
		 * @param array $classes - Associative array of class names indexed by its slug.
		 * @return array
		 */
		public static function load_ref_class( $classes ) {

			$classes['wpsc_attachment'] = array(
				'class'    => __CLASS__,
				'save-key' => 'id',
			);
			return $classes;
		}

		/**
		 * Clear records and actual files for attachments that are not active for more than 24 hrs.
		 * This uses cron-job for background running to improve performace of load-time while deleting attachments.
		 *
		 * @return void
		 */
		public static function garbage_collector() {

			global $wpdb;
			$d = ( new DateTime() )->sub( new DateInterval( 'P1D' ) );

			$attachments = self::find(
				array(
					'items_per_page' => 20,
					'meta_query'     => array(
						'relation' => 'AND',
						array(
							'slug'    => 'date_created',
							'compare' => '<',
							'val'     => $d->format( 'Y-m-d H:i:s' ),
						),
						array(
							'relation' => 'OR',
							array(
								'slug'    => 'is_active',
								'compare' => '=',
								'val'     => '0',
							),
							array(
								'slug'    => 'source',
								'compare' => '=',
								'val'     => 'img_editor_tmp',
							),
						),
					),
				)
			)['results'];
			foreach ( $attachments as $attachment ) {

				$upload_dir = wp_upload_dir();
				$file_path = $upload_dir['basedir'] . $attachment->file_path;
				if ( file_exists( $file_path ) ) {
					wp_delete_file( $file_path );
				}
				$wpdb->query( "DELETE FROM {$wpdb->prefix}psmsc_attachments WHERE id=" . $attachment->id );
			}
		}

		/**
		 * Ajax callback for attachment file upload
		 *
		 * @return void
		 */
		public static function file_upload() {

			if ( check_ajax_referer( 'wpsc_file_upload', '_ajax_nonce', false ) != 1 ) {
				wp_send_json_error( 'Unauthorized request!', 401 );
			}

			if ( ! WPSC_Text_Editor::is_allow_attachments() ) {
				wp_send_json_error( new WP_Error( '001', 'Unauthorized' ), 401 );
			}

			$recaptcha = get_option( 'wpsc-recaptcha-settings' );
			if ( $recaptcha['allow-recaptcha'] === 1 && $recaptcha['recaptcha-version'] == 3 && $recaptcha['recaptcha-site-key'] && $recaptcha['recaptcha-secret-key'] ) {
				WPSC_MS_Recaptcha::validate( 'file_upload' );
			}

			$file = isset( $_FILES['wpscFileAttachment'] ) ? $_FILES['wpscFileAttachment'] : false; // phpcs:ignore
			if ( ! $file ) {
				wp_send_json_error( 'File not found!', 400 );
			}

			$file_settings = get_option( 'wpsc-gs-file-attachments' );
			$original_name = sanitize_file_name( $file['name'] );
			$filename      = time() . '_' . sanitize_file_name( $file['name'] );
			$extension     = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) );
			$today         = new DateTime();
			$upload_dir    = wp_upload_dir();

			// Allowed file extension.
			$allowed_file_extensions = explode( ',', $file_settings['allowed-file-extensions'] );
			$allowed_file_extensions = array_map( 'trim', $allowed_file_extensions );
			$allowed_file_extensions = array_map( 'strtolower', $allowed_file_extensions );
			if ( ! ( in_array( $extension, $allowed_file_extensions ) ) ) {
				wp_send_json_error( 'File extension not allowed!', 400 );
			}

			// Allowed file size.
			$allowed_file_size = intval( $file_settings['attachments-max-filesize'] ) * 1000000;
			if ( ! ( isset( $file['size'] ) && $file['size'] <= $allowed_file_size ) ) {
				wp_send_json_error( 'File size exceeds allowed limit!', 400 );
			}

			// Init attachment data.
			$data = array(
				'name'         => $original_name,
				'date_created' => $today->format( 'Y-m-d H:i:s' ),
			);

			// Check for image type. Add a ".txt" extension to non-image file to prevent executing uploaded files on server.
			$img_extensions = array( 'png', 'jpeg', 'jpg', 'bmp', 'pdf', 'gif' );
			if ( ! in_array( $extension, $img_extensions ) ) {
				$data['is_image'] = 0;
			} else {
				$data['is_image'] = 1;
			}

			$ext = pathinfo( $filename, PATHINFO_EXTENSION );
			$file_name = substr( $filename, 0, -( strlen( $ext ) + 1 ) );
			$filename = $file_name . '.' . strtolower( $ext );
			$filepath_short = '/wpsc/' . $today->format( 'Y' ) . '/' . $today->format( 'm' ) . '/' . $filename;
			$data['file_path'] = $filepath_short;

			// Set up upload overrides.
			$upload_overrides = array(
				'test_form' => false,
			);
			$file['name'] = $filename;
			add_filter( 'upload_dir', array( __CLASS__, 'wpsc_upload_dir' ) );
			add_filter( 'upload_mimes', array( __CLASS__, 'wpsc_custom_upload_mimes' ) );
			$uploaded_file = wp_handle_upload( $file, $upload_overrides );
			// Insert record in database.
			if ( $uploaded_file && empty( $uploaded_file['error'] ) ) {

				$pos = strpos( $uploaded_file['file'], 'wpsc/' );
				if ( $pos === false ) {
					wp_send_json_error( 'Something went wrong!', 500 );
				}
				$new_path = substr( $uploaded_file['file'], $pos );
				if ( $new_path[0] !== '/' ) {
					$new_path = '/' . $new_path;
				}
				$data['file_path'] = $new_path;
				$attachment = self::insert( $data );
				if ( ! $attachment->id ) {
					wp_send_json_error( 'Something went wrong!', 500 );
				}

				wp_send_json( array( 'id' => $attachment->id ) );

			} else {

				$error_message = isset( $uploaded_file['error'] ) ? $uploaded_file['error'] : 'Something went wrong!';
				wp_send_json_error( $error_message, 500 );
			}
		}

		/**
		 * Returns attachment ids to be used for search in ticket list
		 *
		 * @param string $search - search string.
		 * @return array
		 */
		public static function get_tl_search_string( $search ) {

			$search_items = array();
			if ( is_array( self::$tl_search_items ) ) {
				$search_items = self::$tl_search_items;
			} else {
				$attachments = self::find(
					array(
						'search'         => $search,
						'items_per_page' => 0,
					)
				)['results'];
				if ( $attachments ) {
					foreach ( $attachments as $attachment ) {
						$search_items[] = $attachment->id;
					}
				}
				self::$tl_search_items = $search_items;
			}

			return $search_items;
		}

		/**
		 * Custom tinymce image popup
		 *
		 * @return void
		 */
		public static function add_custom_image_tinymce() {

			if ( check_ajax_referer( 'wpsc_add_custom_image_tinymce', '_ajax_nonce', false ) != 1 ) {
				wp_send_json_error( 'Unauthorized request!', 401 );
			}

			$title = esc_attr__( 'Insert/edit image', 'supportcandy' );

			$editor_id = isset( $_POST['editor_id'] ) ? sanitize_text_field( wp_unslash( $_POST['editor_id'] ) ) : '';
			if ( ! $editor_id ) {
				wp_send_json_error( 'Something went wrong!', 400 );
			}

			ob_start();?>
			<form action="#" onsubmit="return false;" class="wpsc-insert-edit-image">
				<div class="wpsc-input-group">
					<div class="label-container">
						<label for=""><?php esc_attr_e( 'Source', 'supportcandy' ); ?></label>
						<span class="required-char">*</span>
					</div>
					<div style="display:flex;">
						<input type="text" accept="image/*" id="wpsc-tinymce-image-url" class="wpsc-tinymce-image-url" style="flex-grow: 1;" autocomplete="off">
						<button id="wpsc-tinymce-get-file-url" class="wpsc-button small secondary" style="margin-left: 2px; width:100px;" onclick="wpsc_tinymce_image_picker();"><?php esc_attr_e( 'Upload', 'supportcandy' ); ?></button>
						<input type='file' accept="image/*" name='fileupload' id='wpsc-fileupload' style='display: none;'>
					</div>
				</div>
				<div class="wpsc-input-group">
					<div class="label-container">
						<label for=""><?php esc_attr_e( 'Dimentions', 'supportcandy' ); ?></label>
						<span class="required-char">*</span>
					</div>
					<div style="display: flex; align-items:center;">
						<input type="text" id="wpsc-tinymce-image-width" aria-label="width" class="wpsc-image-dimention" style="width:70px; margin-right:5px;" autocomplete="off">
						<span class="wpsc-image-dimention-x-sign" style="margin-right:5px;">x</span>
						<input type="text" id="wpsc-tinymce-image-height" aria-label="height" class="wpsc-image-dimention" style="width:70px; margin-right:10px;" autocomplete="off">
						<div class="checkbox-container">
							<?php $unique_id = uniqid( 'wpsc_' ); ?>
							<input id="<?php echo esc_attr( $unique_id ); ?>" type="checkbox" checked value="1"/>
							<label for="<?php echo esc_attr( $unique_id ); ?>"><?php esc_attr_e( 'Constraint properties', 'supportcandy' ); ?></label>
						</div>
					</div>
				</div>
				<script>
					jQuery(".wpsc-tinymce-image-url").change(function(){

						var img = new Image();
						img.src = jQuery(this).val();
						img.onload = function() {
							jQuery('#wpsc-tinymce-image-height').val(this.height);
							jQuery('#wpsc-tinymce-image-width').val(this.width);
							var aspectRatio = this.width/this.height; 
							//Get new height:
							jQuery("#wpsc-tinymce-image-width").on("change", function(){

								if(jQuery("#<?php echo esc_attr( $unique_id ); ?>").prop('checked') == true){
									newWidth  = jQuery(this).val();
									newHeight = Math.round(newWidth/aspectRatio);
									jQuery("#wpsc-tinymce-image-height").val(newHeight);
								}
							});
							//Get new width:
							jQuery("#wpsc-tinymce-image-height").on("change", function(){

								if(jQuery("#<?php echo esc_attr( $unique_id ); ?>").prop('checked') == true){
									newHeight = jQuery(this).val();
									newWidth  = Math.round(newHeight*aspectRatio);
									jQuery("#wpsc-tinymce-image-width").val(newWidth);
								}
							});
						}
					});

					/**
					 * Tinymce image picker
					 */
					function wpsc_tinymce_image_picker() {

						jQuery("#wpsc-fileupload").trigger("click");
						jQuery("#wpsc-fileupload").unbind('change');

						jQuery("#wpsc-fileupload").on("change", function() {
							var file = this.files[0];
							var dataform = new FormData();
							dataform.append('file', file);
							dataform.append('file_name', file.name);
							dataform.append('action', 'wpsc_tinymce_upload_file');
							dataform.append('_ajax_nonce', '<?php echo esc_attr( wp_create_nonce( 'wpsc_tinymce_upload_file' ) ); ?>');

							jQuery.ajax({
								url: supportcandy.ajax_url,
								type: 'POST',
								data: dataform,
								processData: false,
								contentType: false
							}).done(function (res) {

								jQuery('.wpsc-tinymce-image-url').val(res.imgURL);
								var img = new Image();
								img.src = res.imgURL;
								img.onload = function() {

									jQuery('#wpsc-tinymce-image-height').val(this.height);
									jQuery('#wpsc-tinymce-image-width').val(this.width);
									var aspectRatio = this.width/this.height; 

									//Get new height:
									jQuery("#wpsc-tinymce-image-width").on("change", function() {

										if(jQuery("#<?php echo esc_attr( $unique_id ); ?>").prop('checked') == true){
											newWidth  = jQuery(this).val();
											newHeight = Math.round(newWidth/aspectRatio);
											jQuery("#wpsc-tinymce-image-height").val(newHeight);
										}
									});

									//Get new width:
									jQuery("#wpsc-tinymce-image-height").on("change", function() {

										if(jQuery("#<?php echo esc_attr( $unique_id ); ?>").prop('checked') == true){
											newHeight = jQuery(this).val();
											newWidth  = Math.round(newHeight*aspectRatio);
											jQuery("#wpsc-tinymce-image-width").val(newWidth);
										}
									});
								};
							});    
						});
					}
				</script>
			</form>
			<?php
			$body = ob_get_clean();

			ob_start();
			?>
			<button class="wpsc-button small primary" onclick="wpsc_insert_editor_img('<?php echo esc_attr( $editor_id ); ?>');">
				<?php esc_attr_e( 'Submit', 'supportcandy' ); ?>
			</button>
			<button class="wpsc-button small secondary" onclick="wpsc_close_modal();">
				<?php esc_attr_e( 'Cancel', 'supportcandy' ); ?>
			</button>
			<?php
			$footer = ob_get_clean();

			$response = array(
				'title'  => $title,
				'body'   => $body,
				'footer' => $footer,
			);
			wp_send_json( $response );
		}

		/**
		 * Custom tinymce image popup
		 *
		 * @return void
		 */
		public static function edit_custom_image_tinymce() {

			if ( check_ajax_referer( 'wpsc_edit_custom_image_tinymce', '_ajax_nonce', false ) != 1 ) {
				wp_send_json_error( 'Unauthorized request!', 401 );
			}

			$title = esc_attr__( 'Edit image', 'supportcandy' );

			$editor_id = isset( $_POST['editor_id'] ) ? sanitize_text_field( wp_unslash( $_POST['editor_id'] ) ) : '';
			if ( ! $editor_id ) {
				wp_send_json_error( 'Something went wrong!', 400 );
			}

			$height = isset( $_POST['height'] ) ? intval( $_POST['height'] ) : '';
			if ( ! $height ) {
				wp_send_json_error( 'Something went wrong!', 400 );
			}

			$width = isset( $_POST['width'] ) ? intval( $_POST['width'] ) : '';
			if ( ! $width ) {
				wp_send_json_error( 'Something went wrong!', 400 );
			}

			$src = isset( $_POST['src'] ) ? esc_url_raw( wp_unslash( $_POST['src'] ) ) : '';
			if ( ! $src ) {
				wp_send_json_error( 'Something went wrong!', 400 );
			}

			ob_start();
			?>
			<form action="#" onsubmit="return false;" class="wpsc-insert-edit-image">
				<div class="wpsc-input-group">
					<div class="label-container">
						<label for=""><?php esc_attr_e( 'Source', 'supportcandy' ); ?></label>
						<span class="required-char">*</span>
					</div>
					<div style="display:flex;">
						<input type="text" id="wpsc-tinymce-image-url" class="wpsc-tinymce-image-url" style="flex-grow: 1;" autocomplete="off" value="<?php echo esc_attr( $src ); ?>">
						<button id="wpsc-tinymce-get-file-url" class="wpsc-button small secondary" style="margin-left: 2px; width:100px" onclick="wpsc_tinymce_image_picker();"><?php esc_attr_e( 'Upload', 'supportcandy' ); ?></button>
						<input type='file' accept="image/*" name='fileupload' id='wpsc-fileupload' style='display: none;'>
					</div>
				</div>
				<div class="wpsc-input-group">
					<div class="label-container">
						<label for=""><?php esc_attr_e( 'Dimentions', 'supportcandy' ); ?></label>
						<span class="required-char">*</span>
					</div>
					<div style="display: flex; align-items:center;">
						<input type="text" id="wpsc-tinymce-image-width" aria-label="width" class="wpsc-image-dimention" style="width:70px; margin-right:5px;" autocomplete="off" value="<?php echo esc_attr( $width ); ?>">
						<span class="wpsc-image-dimention-x-sign" style="margin-right:5px;">x</span>
						<input type="text" id="wpsc-tinymce-image-height" aria-label="height" class="wpsc-image-dimention" style="width:70px; margin-right:10px;" autocomplete="off" value="<?php echo esc_attr( $height ); ?>" >
						<div class="checkbox-container">
							<?php $unique_id = uniqid( 'wpsc_' ); ?>
							<input id="<?php echo esc_attr( $unique_id ); ?>" type="checkbox" checked value="1"/>
							<label for="<?php echo esc_attr( $unique_id ); ?>"><?php esc_attr_e( 'Constraint properties', 'supportcandy' ); ?></label>
						</div>
					</div>
				</div>
				<script>
					jQuery(".wpsc-tinymce-image-url").change(function(){

						var img = new Image();
						img.src = jQuery(this).val();
						img.onload = function() {
							jQuery('#wpsc-tinymce-image-height').val(this.height);
							jQuery('#wpsc-tinymce-image-width').val(this.width);
							var aspectRatio = this.width/this.height; 
							//Get new height:
							jQuery("#wpsc-tinymce-image-width").on("change", function(){

								if(jQuery("#<?php echo esc_attr( $unique_id ); ?>").prop('checked') == true){
									newWidth  = jQuery(this).val();
									newHeight = Math.round(newWidth/aspectRatio);
									jQuery("#wpsc-tinymce-image-height").val(newHeight);
								}
							});
							//Get new width:
							jQuery("#wpsc-tinymce-image-height").on("change", function(){

								if(jQuery("#<?php echo esc_attr( $unique_id ); ?>").prop('checked') == true){
									newHeight = jQuery(this).val();
									newWidth  = Math.round(newHeight*aspectRatio);
									jQuery("#wpsc-tinymce-image-width").val(newWidth);
								}
							});
						}
					});
					var height = <?php echo esc_attr( $height ); ?>;
					var width  = <?php echo esc_attr( $width ); ?>;
					var aspectRatio = width/height; 
					jQuery("#wpsc-tinymce-image-width").on("change", function(){

						if(jQuery("#<?php echo esc_attr( $unique_id ); ?>").prop('checked') == true){
							newWidth  = jQuery(this).val();
							newHeight = Math.round(newWidth/aspectRatio);
							jQuery("#wpsc-tinymce-image-height").val(newHeight);
						}
					});
					//Get new width:
					jQuery("#wpsc-tinymce-image-height").on("change", function(){

						if(jQuery("#<?php echo esc_attr( $unique_id ); ?>").prop('checked') == true){
							newHeight = jQuery(this).val();
							newWidth  = Math.round(newHeight*aspectRatio);
							jQuery("#wpsc-tinymce-image-width").val(newWidth);
						}
					});

					/**
					 * Tinymce image picker
					 */
					function wpsc_tinymce_image_picker() {

						jQuery("#wpsc-fileupload").trigger("click");
						jQuery("#wpsc-fileupload").unbind('change');

						jQuery("#wpsc-fileupload").on("change", function() {
							var file = this.files[0];
							var dataform = new FormData();
							dataform.append('file', file);
							dataform.append('file_name', file.name);
							dataform.append('action', 'wpsc_tinymce_upload_file');
							dataform.append('_ajax_nonce', '<?php echo esc_attr( wp_create_nonce( 'wpsc_tinymce_upload_file' ) ); ?>');

							jQuery.ajax({
								url: supportcandy.ajax_url,
								type: 'POST',
								data: dataform,
								processData: false,
								contentType: false
							}).done(function (res) {

								jQuery('.wpsc-tinymce-image-url').val(res.imgURL);
								var img = new Image();
								img.src = res.imgURL;
								img.onload = function() {

									jQuery('#wpsc-tinymce-image-height').val(this.height);
									jQuery('#wpsc-tinymce-image-width').val(this.width);
									var aspectRatio = this.width/this.height; 

									//Get new height:
									jQuery("#wpsc-tinymce-image-width").on("change", function() {

										if(jQuery("#<?php echo esc_attr( $unique_id ); ?>").prop('checked') == true){
											newWidth  = jQuery(this).val();
											newHeight = Math.round(newWidth/aspectRatio);
											jQuery("#wpsc-tinymce-image-height").val(newHeight);
										}
									});

									//Get new width:
									jQuery("#wpsc-tinymce-image-height").on("change", function() {

										if(jQuery("#<?php echo esc_attr( $unique_id ); ?>").prop('checked') == true){
											newHeight = jQuery(this).val();
											newWidth  = Math.round(newHeight*aspectRatio);
											jQuery("#wpsc-tinymce-image-width").val(newWidth);
										}
									});
								};
							});    
						});
					}
				</script>
			</form>
			<?php
			$body = ob_get_clean();

			ob_start();
			?>
			<button class="wpsc-button small primary" onclick="wpsc_insert_editor_img('<?php echo esc_attr( $editor_id ); ?>');">
				<?php esc_attr_e( 'Submit', 'supportcandy' ); ?>
			</button>
			<button class="wpsc-button small secondary" onclick="wpsc_close_modal();">
				<?php esc_attr_e( 'Cancel', 'supportcandy' ); ?>
			</button>
			<?php
			$footer = ob_get_clean();

			$response = array(
				'title'  => $title,
				'body'   => $body,
				'footer' => $footer,
			);
			wp_send_json( $response );
		}

		/**
		 * Check download file
		 *
		 * @return void
		 */
		public static function check_download_file() {

			if ( isset( $_REQUEST['wpsc_attachment'] ) ) { // phpcs:ignore

				if ( WPSC_Functions::is_site_admin() && ! extension_loaded( 'fileinfo' ) ) {
					echo 'The fileinfo extension is not available. Please enable it in your PHP configuration.';
					exit;
				}

				$current_user = WPSC_Current_User::$current_user;
				$has_auth = true;
				if ( isset( $_REQUEST['user'] ) && ! $current_user ) { // phpcs:ignore

					$customer = new WPSC_Customer( intval( $_REQUEST['user'] ) ); // phpcs:ignore
					if ( ! $customer->id ) {
						wp_send_json_error( __( 'Bad request!', 'supportcandy' ), 400 );
					}

					$current_user = WPSC_Current_User::change_current_user( $customer->email );
					$has_auth = false;
				}

				$attachment_id = intval( $_REQUEST['wpsc_attachment'] ); // phpcs:ignore
				$attachment = new WPSC_Attachment( $attachment_id );
				if ( ! $attachment->id ) {
					return;
				}
				$auth_code = isset($_REQUEST['auth_code']) ? sanitize_text_field( $_REQUEST['auth_code'] ) : ''; // phpcs:ignore
				$advanced = get_option( 'wpsc-ms-advanced-settings' );
				switch ( $attachment->source ) {

					case 'cf':
						$cf = new WPSC_Custom_Field( $attachment->source_id );
						if ( ! $cf->id ) {
							wp_send_json_error( __( 'Bad request!', 'supportcandy' ), 400 );
						}

						if ( in_array( $cf->field, array( 'ticket', 'agentonly' ) ) ) { // ticket field.

							$ticket = new WPSC_Ticket( $attachment->ticket_id );
							if ( ! $ticket->id ) {
								$ticket = new WPSC_Archive_Ticket( $attachment->ticket_id );
								if ( ! $ticket->id ) {
									wp_send_json_error( __( 'Bad request!', 'supportcandy' ), 400 );
								}
							}

							if ( ! $has_auth ) {
								$auth_code = isset( $_REQUEST['auth_code'] ) ? sanitize_text_field( $_REQUEST['auth_code'] ) : ''; // phpcs:ignore
								if ( ! $auth_code || $auth_code != $ticket->auth_code ) {
									wp_send_json_error( 'Unauthorized!', 401 );
								}
							}

							if ( is_a( $ticket, 'WPSC_Ticket' ) || is_a( $ticket, 'WPSC_Archive_Ticket' ) ) {
								// Determine correct class based on ticket type.
								$ticket_class = is_a( $ticket, 'WPSC_Ticket' )
									? 'WPSC_Individual_Ticket'
									: 'WPSC_Individual_Archive_Ticket';

								// Assign current ticket to class static property.
								$ticket_class::$ticket = $ticket;
								$has_access = (
									( $current_user->is_agent && $ticket_class::has_ticket_cap( 'view' ) ) ||
									$ticket_class::is_customer() ||
									( ! $advanced['ticket-url-auth'] && $ticket->auth_code === $auth_code )
								);

								if ( ! $has_access ) {
									wp_send_json_error( __( 'Unauthorized!', 'supportcandy' ), 401 );
								}
							}
							self::file_download( $attachment );

						} else { // customer field.

							$customer       = new WPSC_Customer( intval( $attachment->customer_id ) );
							$ticket_widgets = get_option( 'wpsc-ticket-widget', array() );
							$raised_by      = $ticket_widgets['raised-by'];

							if (
								$current_user->customer->id == $customer->id ||
								(
									$current_user->is_agent &&
									in_array( $current_user->agent->role, $raised_by['allowed-agent-roles'] )
								)
							) {
								self::file_download( $attachment );
							}
						}
						break;

					case 'reply':
					case 'report':
						$ticket = new WPSC_Ticket( $attachment->ticket_id );
						if ( ! $ticket->id ) {
							$ticket = new WPSC_Archive_Ticket( $attachment->ticket_id );
							if ( ! $ticket->id ) {
								wp_send_json_error( __( 'Bad request!', 'supportcandy' ), 400 );
							}
						}

						if ( ! $has_auth ) {
							$auth_code = isset( $_REQUEST['auth_code'] ) ? sanitize_text_field( $_REQUEST['auth_code'] ) : ''; // phpcs:ignore
							if ( ! $auth_code || $auth_code != $ticket->auth_code ) {
								wp_send_json_error( 'Unauthorized!', 401 );
							}
						}

						if ( is_a( $ticket, 'WPSC_Ticket' ) || is_a( $ticket, 'WPSC_Archive_Ticket' ) ) {
							// Determine correct class based on ticket type.
							$ticket_class = is_a( $ticket, 'WPSC_Ticket' )
								? 'WPSC_Individual_Ticket'
								: 'WPSC_Individual_Archive_Ticket';

							// Assign current ticket to class static property.
							$ticket_class::$ticket = $ticket;
							$has_access = (
								( $current_user->is_agent && $ticket_class::has_ticket_cap( 'view' ) ) ||
								$ticket_class::is_customer() ||
								( ! $advanced['ticket-url-auth'] && $ticket->auth_code === $auth_code )
							);

							if ( ! $has_access ) {
								wp_send_json_error( __( 'Unauthorized!', 'supportcandy' ), 401 );
							}
						}
						self::file_download( $attachment );
						break;

					case 'note':
						$ticket = new WPSC_Ticket( $attachment->ticket_id );
						if ( ! $ticket->id ) {
							$ticket = new WPSC_Archive_Ticket( $attachment->ticket_id );
							if ( ! $ticket->id ) {
								wp_send_json_error( __( 'Bad request!', 'supportcandy' ), 400 );
							}
						}

						if ( ! $has_auth ) {
							$auth_code = isset( $_REQUEST['auth_code'] ) ? sanitize_text_field( $_REQUEST['auth_code'] ) : ''; // phpcs:ignore
							if ( ! $auth_code || $auth_code != $ticket->auth_code ) {
								wp_send_json_error( 'Unauthorized!', 401 );
							}
						}

						if ( is_a( $ticket, 'WPSC_Ticket' ) || is_a( $ticket, 'WPSC_Archive_Ticket' ) ) {

							// Determine correct handler class dynamically.
							$ticket_class = is_a( $ticket, 'WPSC_Ticket' )
								? 'WPSC_Individual_Ticket'
								: 'WPSC_Individual_Archive_Ticket';

							// Assign the ticket.
							$ticket_class::$ticket = $ticket;
							if ( ! ( $current_user->is_agent && $ticket_class::has_ticket_cap( 'pn' ) ) ) {
								wp_send_json_error( __( 'Unauthorized!', 'supportcandy' ), 401 );
							}
						}

						self::file_download( $attachment );
						break;

					case 'img_editor':
						$ticket = new WPSC_Ticket( $attachment->ticket_id );
						if ( ! $ticket->id ) {
							$ticket = new WPSC_Archive_Ticket( $attachment->ticket_id );
							if ( ! $ticket->id ) {
								wp_send_json_error( __( 'Bad request!', 'supportcandy' ), 400 );
							}
						}

						if ( is_a( $ticket, 'WPSC_Ticket' ) || is_a( $ticket, 'WPSC_Archive_Ticket' ) ) {

							// Determine class dynamically.
							$ticket_class = is_a( $ticket, 'WPSC_Ticket' )
								? 'WPSC_Individual_Ticket'
								: 'WPSC_Individual_Archive_Ticket';

							$ticket_class::$ticket = $ticket;
							if (
								! (
									( $current_user->is_agent && $ticket_class::has_ticket_cap( 'view' ) ) ||
									$ticket_class::is_customer() ||
									( $ticket->auth_code == $auth_code )
								)
							) {
								wp_send_json_error( 'Unauthorized!', 401 );
							}
							self::file_download( $attachment );
						} else {
							wp_send_json_error( __( 'Invalid ticket type.', 'supportcandy' ), 400 );
						}
						break;
					case 'img_editor_tmp':
						self::file_download( $attachment );
				}
			}
		}

		/**
		 * Download attachment
		 *
		 * @param WPSC_Attachment $attachment - attachment object.
		 * @return void
		 */
		public static function file_download( $attachment ) {

			// Turn off all output buffering.
			while ( ob_get_level() ) {
				ob_end_clean();
			}

			$upload_dir = wp_upload_dir();
			$file_path = $upload_dir['basedir'] . $attachment->file_path;
			if ( ! file_exists( $file_path ) ) {
				echo 'File does not exists!';
				exit;
			}

			// Check whether attachment is of image type.
			$attach_settings = get_option( 'wpsc-gs-file-attachments' );
			if ( $attachment->is_image && $attach_settings['image-download-behaviour'] == 'open-browser' ) {
				header( 'Content-Type: ' . mime_content_type( $file_path ) );
				readfile( $file_path ); // phpcs:ignore
				exit( 0 );
			}

			header( 'Content-Description: File Transfer' );
			header( 'Cache-Control: public' );
			header( 'Content-Type: application/force-download' );
			header( 'Content-Disposition: attachment;filename="' . $attachment->name . '"' );
			header( 'Content-Length: ' . filesize( $file_path ) );
			flush();
			readfile( $file_path ); // phpcs:ignore
			exit( 0 );
		}

		/**
		 * Upload tinymce image file to database
		 *
		 * @return void
		 */
		public static function tinymce_upload_file() {

			if ( check_ajax_referer( 'wpsc_tinymce_upload_file', '_ajax_nonce', false ) != 1 ) {
				wp_send_json_error( 'Unauthorized request!', 401 );
			}

			$file = isset( $_FILES['file'] ) ? $_FILES['file'] : false; // phpcs:ignore
			if ( ! $file ) {
				wp_send_json_error( 'File not found!', 400 );
			}

			$file_settings = get_option( 'wpsc-gs-file-attachments' );
			$original_name = sanitize_file_name( $file['name'] );
			$filename      = time() . '_' . sanitize_file_name( $file['name'] );
			$extension     = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) );
			$today         = new DateTime();
			$upload_dir    = wp_upload_dir();

			// Check file extension.
			$img_extensions = array( 'jpg', 'jpeg', 'png', 'gif' );
			if ( ! ( in_array( $extension, $img_extensions ) ) ) {
				wp_send_json_error( 'Invalid file extension!', 400 );
			}

			// Allowed file size.
			$allowed_file_size = intval( $file_settings['attachments-max-filesize'] ) * 1000000;
			if ( ! ( isset( $file['size'] ) && $file['size'] <= $allowed_file_size ) ) {
				wp_send_json_error( 'File size exceeds allowed limit!', 400 );
			}

			$filepath_short = '/wpsc/' . $today->format( 'Y' ) . '/' . $today->format( 'm' ) . '/' . $filename;

			// Init attachment data.
			$data = array(
				'name'         => $original_name,
				'file_path'    => $filepath_short,
				'is_image'     => 1,
				'date_created' => $today->format( 'Y-m-d H:i:s' ),
				'is_active'    => 0,
				'source'       => 'img_editor_tmp',
			);

			// Set up upload overrides.
			$upload_overrides = array(
				'test_form' => false,
			);
			$file['name']  = $filename;
			add_filter( 'upload_dir', array( __CLASS__, 'wpsc_upload_dir' ) );
			$uploaded_file = wp_handle_upload( $file, $upload_overrides );
			// Insert record in database.
			if ( $uploaded_file && empty( $uploaded_file['error'] ) ) {

				$pos = strpos( $uploaded_file['file'], '/wpsc/' );
				if ( $pos === false ) {
					wp_send_json_error( 'Something went wrong!', 500 );
				}
				$new_path = substr( $uploaded_file['file'], $pos );
				$data['file_path'] = $new_path;
				$attachment = self::insert( $data );
				if ( ! $attachment->id ) {
					wp_send_json_error( 'Something went wrong!', 500 );
				}

				wp_send_json( array( 'imgURL' => home_url( '/' ) . '?wpsc_attachment=' . $attachment->id ) );

			} else {

				$error_message = isset( $uploaded_file['error'] ) ? $uploaded_file['error'] : 'Something went wrong!';
				wp_send_json_error( $error_message, 500 );
			}
		}
	}
endif;

WPSC_Attachment::init();
