<?php
/**
 * Meta boxes mamager
 */

// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
	die;
}

if ( ! class_exists( 'Jet_Engine_CPT_Meta' ) ) {
	require jet_engine()->plugin_path( 'includes/components/meta-boxes/post.php' );
}

if ( ! class_exists( 'Jet_Engine_CPT_User_Meta' ) ) {

	/**
	 * Define Jet_Engine_CPT_User_Meta class
	 */
	class Jet_Engine_CPT_User_Meta extends Jet_Engine_CPT_Meta {

		private $args;
		private $fields;

		public $builder;

		/**
		 * Constructor for the class
		 */
		public function __construct( $args, $meta_box ) {

			$this->args     = $args;
			$this->meta_box = $meta_box;

			if ( ! empty( $args['hide_field_names'] ) ) {
				$this->hide_field_names = $args['hide_field_names'];
			}

			$fields = $this->prepare_meta_fields( $meta_box );
			$this->fields = $fields;

			if ( ! empty( $this->show_in_rest ) ) {

				if ( ! class_exists( 'Jet_Engine_Rest_User_Meta' ) ) {
					require jet_engine()->meta_boxes->component_path( 'rest-api/fields/user-meta.php' );
				}

				foreach ( $this->show_in_rest as $field ) {
					new Jet_Engine_Rest_User_Meta( $field, null );
				}
			}

			if ( ! is_admin() ) {
				return;
			}

			if ( ! jet_engine()->meta_boxes->conditions->check_conditions( $this->get_box_id(), $args ) ) {
				return;
			}

			add_action( 'current_screen', array( $this, 'init_on_allowed_screens' ) );

		}

		/**
		 * Returns processed user ID
		 * @return [type] [description]
		 */
		public function get_user_id() {

			// phpcs:disable WordPress.Security.NonceVerification.Recommended
			global $current_screen;

			if ( $current_screen && 'user-edit' === $current_screen->base ) {
				$user_id = isset( $_GET['user_id'] ) ? absint( $_GET['user_id'] ) : false;
			} elseif ( $current_screen && 'profile' === $current_screen->base ) {
				$user_id = get_current_user_id();
			} else {
				$user_id = false;
			}
			// phpcs:enable WordPress.Security.NonceVerification.Recommended

			return apply_filters( 'jet-engine/user-meta/current-user-id', $user_id, $this );
		}

		/**
		 * Initialize on allowed screens
		 *
		 * @return [type] [description]
		 */
		public function init_on_allowed_screens( $current_screen ) {

			$allowed_screens = ! empty( $this->args['allowed_user_screens'] ) ? $this->args['allowed_user_screens'] : 'edit';

			switch ( $allowed_screens ) {
				case 'edit':

					if ( 'user-edit' === $current_screen->base ) {
						$this->register_fields();
					}

					break;

				case 'edit-profile':

					if ( in_array( $current_screen->base, array( 'user-edit', 'profile' ) ) ) {
						$this->register_fields( true );
					}

					break;
			}

		}

		/**
		 * Register user meta fields
		 *
		 * @param  boolean $profile [description]
		 * @return [type]           [description]
		 */
		public function register_fields( $profile = false ) {

			add_action( 'edit_user_profile', array( $this, 'render_fields' ), 20 );
			add_action( 'edit_user_profile_update', array( $this, 'edit_user_update' ) );

			if ( $profile ) {
				add_action( 'show_user_profile', array( $this, 'render_fields' ), 20 );
				add_action( 'personal_options_update', array( $this, 'personal_profile_update' ) );
			}

			add_action( 'admin_enqueue_scripts', array( $this, 'init_builder' ), 0 );
			add_action( 'admin_enqueue_scripts', array( $this, 'maybe_enqueue_custom_css' ), 0 );
			add_action( 'admin_enqueue_scripts', array( $this, 'maybe_enqueue_inline_js' ), 20 );

		}

		/**
		 * Initialize builder and register fields
		 *
		 * @return [type] [description]
		 */
		public function init_builder() {

			$this->builder = $this->get_builder_for_meta();

			$zero_allowed = array(
				'stepper',
				'slider',
			);

			$parent = 'user_meta_' . rand( 100, 999 );

			$name = ! empty( $this->args['name'] ) ? esc_attr( $this->args['name'] ) : false;

			if ( $this->edit_link ) {
				$name .= sprintf(
					'<a href="%s" class="jet-engine-edit-box-link" target="_blank"><span class="dashicons dashicons-admin-generic"></span></a>',
					$this->edit_link
				);
			}

			$this->builder->register_section( array(
				$parent => array(
					'type'   => 'section',
					'scroll' => true,
					'title'  => apply_filters( 'jet-engine/compatibility/translate-string', $name ),
				)
			) );

			$this->builder->register_settings(
				array(
					$parent . '_settings' => array(
						'type'   => 'settings',
						'parent' => $parent,
					),
				)
			);

			foreach ( $this->fields as $key => $field ) {

				if ( ! $key ) {
					continue;
				}

				$default = $this->get_arg( $field, 'value', '' );
				$value   = $this->get_meta( $key, $default, $field );

				if ( isset( $field['options_callback'] ) ) {
					$field['options'] = call_user_func( $field['options_callback'] );
				}

				$value = $this->prepare_field_value( $field, $value );

				$element         = $this->get_arg( $field, 'element', 'control' );
				$field['id']     = $this->get_arg( $field, 'id', $key );
				$field['parent'] = $this->get_arg( $field, 'parent', $parent . '_settings' );
				$field['name']   = $this->get_arg( $field, 'name', $key );
				$field['type']   = $this->get_arg( $field, 'type', '' );
				$field['value']  = $value;

				// Fix zero values for stepper and slider
				if ( ! $value && in_array( $field['type'], $zero_allowed ) ) {
					$field['value'] = 0;
				}

				$register_callback = 'register_' . $element;

				if ( method_exists( $this->builder, $register_callback ) ) {
					call_user_func( array( $this->builder, $register_callback ), $field );
				}
			}

		}

		/**
		 * Prepare field value.
		 *
		 * @param $field
		 * @param $value
		 *
		 * @return array
		 */
		public function prepare_field_value( $field, $value ) {

			switch ( $field['type'] ) {
				case 'repeater':

					if ( is_array( $value ) && ! empty( $field['fields'] ) ) {

						$repeater_fields =  $field['fields'];

						foreach ( $value as $item_id => $item_value ) {
							foreach ( $item_value as $repeater_field_id => $repeater_field_value ) {
								$value[ $item_id ][ $repeater_field_id ] = $this->prepare_field_value( $repeater_fields[ $repeater_field_id ], $repeater_field_value );
							}
						}
					}

					break;

				case 'checkbox':

					if ( ! empty( $field['is_array'] ) && ! empty( $field['options'] ) && ! empty( $value ) ) {

						$adjusted = array();

						if ( ! is_array( $value ) ) {
							$value = array( $value );
						}

						foreach ( $value as $val ) {
							$adjusted[ $val ] = 'true';
						}

						foreach ( $field['options'] as $opt_val => $opt_label ) {
							if ( ! in_array( $opt_val, $value ) ) {
								$adjusted[ $opt_val ] = 'false';
							}
						}

						$value = $adjusted;
					}

					break;

				case 'text':

					if ( ! empty( $value ) && $this->to_timestamp( $field ) && is_numeric( $value ) ) {

						switch ( $field['input_type'] ) {
							case 'date':
								$value = $this->get_date( 'Y-m-d', $value );
								break;

							case 'datetime-local':
								$value = $this->get_date( 'Y-m-d\TH:i', $value );
								break;
						}
					}

					break;
			}

			return $value;
		}

		/**
		 * Returns date converted from timestamp
		 *
		 * @return [type] [description]
		 */
		public function get_date( $format, $time ) {
			return apply_filters( 'cx_user_meta/date', date( $format, $time ), $time, $format );
		}

		/**
		 * Safely get attribute from field settings array.
		 *
		 * @since  1.0.0
		 * @param  array            $field   arguments array.
		 * @param  string|int|float $arg     argument key.
		 * @param  mixed            $default default argument value.
		 * @return mixed
		 */
		public function get_arg( $field = array(), $arg = '', $default = '' ) {
			if ( is_array( $field ) && isset( $field[ $arg ] ) ) {
				return $field[ $arg ];
			}
			return $default;
		}

		/**
		 * Retrieve post meta field.
		 *
		 * @since  1.1.0
		 * @since  1.2.0 Process default value.
		 *
		 * @param  object $post    Current post object.
		 * @param  string $key     The meta key to retrieve.
		 * @param  mixed  $default Default value.
		 * @param  array  $field   Meta field apropriate to current key.
		 * @return string
		 */
		public function get_meta( $key = '', $default = false, $field = array() ) {

			$user_id = $this->get_user_id();

			if ( ! $user_id ) {
				return $default;
			}

			$pre_value = apply_filters(
				'jet-engine/user-meta/pre-get-meta/' . $key, false, $user_id, $key, $default, $field
			);

			if ( false !== $pre_value ) {
				return $pre_value;
			}

			$meta = get_user_meta( $user_id, $key, false );

			return ( empty( $meta ) ) ? $default : $meta[0];

		}

		/**
		 * Is date field
		 *
		 * @param  [type]  $input_type [description]
		 * @return boolean             [description]
		 */
		public function to_timestamp( $field ) {

			if ( empty( $field['input_type'] ) ) {
				return false;
			}

			if ( empty( $field['is_timestamp'] ) ) {
				return false;
			}

			if ( ! in_array( $field['input_type'], array( 'date', 'datetime-local' ) ) ) {
				return false;
			}

			return ( true === $field['is_timestamp'] );

		}

		/**
		 * Render fields
		 *
		 * @return [type] [description]
		 */
		public function render_fields() {
			$this->open_meta_wrap();
			$this->builder->render();
			$this->close_meta_wrap();
		}

		/**
		 * Open meta wrap
		 * @return void
		 */
		public function open_meta_wrap() {
			echo '<div class="jet-engine-user-meta-wrap">';
		}

		/**
		 * Get CSS wrapper selector.
		 *
		 * @return string
		 */
		public function get_css_wrapper_selector() {
			return '.jet-engine-user-meta-wrap ';
		}

		/**
		 * Fires on users edited by admin
		 *
		 * @param  [type] $user_id [description]
		 * @return [type]          [description]
		 */
		public function edit_user_update( $user_id ) {

			if ( ! current_user_can( 'edit_users' ) ) {
				return;
			}

			if ( ! current_user_can( 'edit_user', $user_id ) ) {
				return;
			}

			$this->update_meta( $user_id );

		}

		/**
		 * Fires when user editing own profile
		 *
		 * @return [type] [description]
		 */
		public function personal_profile_update( $user_id ) {

			if ( ! current_user_can( 'edit_user', $user_id ) ) {
				return;
			}

			$this->update_meta( $user_id );

		}

		/**
		 * Update user data
		 *
		 * @return [type] [description]
		 */
		public function update_meta( $user_id ) {

			if ( ! current_user_can( 'edit_user', $user_id ) || ! check_admin_referer( "update-user_{$user_id}" ) ) {
				return;
			}

			/**
			 * Hook on before current metabox saving
			 */
			do_action( 'jet-engine/user-meta/before-save/', $user_id, $this );

			foreach ( $this->fields as $key => $field ) {

				if ( isset( $field['element'] ) && 'control' !== $field['element'] ) {
					continue;
				}

				$pre_processed = apply_filters( 'jet-engine/user-meta/preprocess/' . $key, false, $user_id, $this );

				if ( $pre_processed ) {
					continue;
				}

				if ( ! isset( $_POST[ $key ] ) || '' === $_POST[ $key ] ) {

					/**
					 * Fires before specific key will be deleted
					 */
					do_action( 'jet-engine/user-meta/before-delete/' . $key, $user_id, false, $this );

					// Delete all separate fields of repeater.
					if ( 'repeater' === $field['type'] && ! empty( $field['save_separate'] ) ) {
						$this->delete_repeater_separate_fields( $user_id, $key, $field );
					}

					update_user_meta( $user_id, $key, false );

					continue;
				}

				// Sanitized by sanitize_meta()
				$value = $this->sanitize_meta( $field, wp_unslash( $_POST[ $key ] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

				do_action( 'jet-engine/user-meta/before-save/' . $key, $user_id, $value, $key, $this );

				// Save the value of each repeater field as a separate field.
				if ( 'repeater' === $field['type'] && ! empty( $field['save_separate'] ) ) {
					$this->save_repeater_separate_fields( $user_id, $key, $value, $field );
				}

				update_user_meta( $user_id, $key, $value );

			}

			/**
			 * Hook on after current metabox saving
			 */
			do_action( 'jet-engine/user-meta/after-save', $user_id, $this );

		}

		/**
		 * Sanitize passed meta value
		 *
		 * @since  1.1.3
		 * @param  array $field Meta field to sanitize.
		 * @param  mixed $value Meta value.
		 * @return mixed
		 */
		public function sanitize_meta( $field, $value ) {

			if ( 'repeater' === $field['type'] && ! empty( $field['fields'] ) && is_array( $value ) ) {
				$repeater_fields = $field['fields'];

				foreach ( $value as $item_id => $item_value ) {
					foreach ( $item_value as $repeater_field_id => $repeater_field_value ) {
						$value[ $item_id ][ $repeater_field_id ] = $this->sanitize_meta( $repeater_fields[ $repeater_field_id ], $repeater_field_value );
					}
				}
			}

			if ( 'checkbox' === $field['type'] && ! empty( $field['is_array'] ) ) {
				$raw    = ! empty( $value ) ? $value : array();
				$result = array();

				if ( is_array( $raw ) ) {
					foreach ( $raw as $raw_key => $raw_value ) {
						$bool_value = filter_var( $raw_value, FILTER_VALIDATE_BOOLEAN );
						if ( $bool_value ) {
							$result[] = $raw_key;
						}
					}
				}

				return $result;
			}

			if ( $this->to_timestamp( $field ) ) {
				return apply_filters( 'cx_user_meta/strtotime', strtotime( $value ), $value );
			}

			if ( empty( $field['sanitize_callback'] ) ) {
				return $this->sanitize_deafult( $value );
			}

			if ( ! is_callable( $field['sanitize_callback'] ) ) {
				return $this->sanitize_deafult( $value );
			}

			$key = ! empty( $field['name'] ) ? $field['name'] : null;

			return call_user_func(
				$field['sanitize_callback'],
				$value,
				$key,
				$field
			);

		}

		/**
		 * Cleare value with sanitize_text_field if not is array
		 *
		 * @since  1.1.3
		 * @param  mixed $value Passed value.
		 * @return mixed
		 */
		public function sanitize_deafult( $value ) {
			return is_array( $value ) ? $value : sanitize_text_field( $value );
		}

		public function is_allowed_on_current_admin_hook( $hook ) {
			if ( null !== $this->is_allowed_on_admin_hook ) {
				return $this->is_allowed_on_admin_hook;
			}

			$allowed_hooks = array(
				'user-edit.php',
				'profile.php',
			);

			if ( ! in_array( $hook, $allowed_hooks ) ) {
				$this->is_allowed_on_admin_hook = false;
				return $this->is_allowed_on_admin_hook;
			}

			$this->is_allowed_on_admin_hook = true;
			return $this->is_allowed_on_admin_hook;
		}

		/**
		 * Returns the repeater separate field key.
		 *
		 * @param $repeater_key
		 * @param $field_key
		 *
		 * @return string
		 */
		public function get_repeater_separate_field_key( $repeater_key, $field_key ) {
			return apply_filters(
				'jet-engine/user-meta/repeater/separate_field_key',
				$repeater_key . '_' . $field_key,
				$repeater_key,
				$field_key
			);
		}

		/**
		 * Delete all separate fields of repeater.
		 *
		 * @param $user_id
		 * @param $key
		 * @param $field
		 */
		public function delete_repeater_separate_fields( $user_id, $key, $field ) {

			if ( empty( $field['fields'] ) ) {
				return;
			}

			foreach ( $field['fields'] as $repeater_field_key => $repeater_field ) {
				delete_user_meta( $user_id, $this->get_repeater_separate_field_key( $key, $repeater_field_key ) );
			}
		}

		/**
		 * Save the value of each repeater field as a separate field.
		 *
		 * @param $user_id
		 * @param $key
		 * @param $value
		 * @param $field
		 */
		public function save_repeater_separate_fields( $user_id, $key, $value, $field ) {

			$this->delete_repeater_separate_fields( $user_id, $key, $field );

			if ( empty( $value ) || ! is_array( $value ) ) {
				return;
			}

			foreach ( $value as $repeater_item_value ) {

				if ( empty( $repeater_item_value ) ) {
					continue;
				}

				foreach ( $repeater_item_value as $repeater_field_key => $repeater_field_value ) {
					add_user_meta(
						$user_id,
						$this->get_repeater_separate_field_key( $key, $repeater_field_key ),
						$repeater_field_value
					);
				}
			}
		}

	}

}
