<?php

/**
 * Suppress "error - 0 - No summary was found for this file" on phpdoc generation
 *
 * @package WPDataAccess\Simple_Form
 */

namespace WPDataAccess\Simple_Form {

	use WPDataAccess\Utilities\WPDA_Message_Box;
	use WPDataAccess\WPDA;

	/**
	 * Class WPDA_Simple_Form_Item
	 *
	 * Simple forms consist of items. Items correspond with table columns. Basically an item is generated for every
	 * table column in the base table. Class WPDA_Simple_Form_Item handles a database column as a standard text item.
	 *
	 * It's possible to add dummy columns. Values for dummy columns however are lost when data is saved.
	 *
	 * Check out {@see WPDA_Simple_Form} to see how to use simple form items.
	 *
	 * @author  Peter Schulz
	 * @since   1.0.0
	 */
	class WPDA_Simple_Form_Item {

		/**
		 * Database column name
		 *
		 * @var string
		 */
		protected $item_name;

		/**
		 * MySQL data type
		 *
		 * @var string
		 */
		protected $data_type;

		/**
		 * Item label
		 *
		 * @var string
		 */
		protected $item_label;

		/**
		 * Current column value in the database
		 *
		 * @var mixed
		 */
		protected $item_value;

		/**
		 * Default value
		 *
		 * @var mixed
		 */
		protected $item_default_value;

		/**
		 * Database column specific info
		 *
		 * Like auto_increment, on update, etc
		 *
		 * @var string
		 */
		protected $item_extra;

		/**
		 * Enum values for column or empty
		 *
		 * @var array
		 */
		protected $item_enum;

		/**
		 * Enum options for column or empty
		 *
		 * @var array
		 */
		protected $item_enum_options;

		/**
		 * Enum text for column or empty
		 *
		 * @var array
		 */
		protected $item_enum_text;

		/**
		 * Database column type
		 *
		 * Column type offers more info than data type, like column length or values for enum types.
		 *
		 * @var string
		 */
		protected $column_type;

		/**
		 * Icon type
		 *
		 * @var string
		 */
		protected $item_icon_type;

		/**
		 * Array of events
		 *
		 * Add event to item for example: ["onclick" => "check_item_value()"]
		 *
		 * @var array
		 */
		protected $item_event;

		/**
		 * Item specific Javascript code
		 *
		 * Code is added to the end of the form.
		 *
		 * @var string
		 */
		protected $item_js;

		/**
		 * Show item icon (data type)
		 *
		 * TRUE = icon is shown after item, FALSE = hide icon (default FALSE)
		 *
		 * @var boolean
		 */
		protected $item_hide_icon;

		/**
		 * Item CSS class
		 *
		 * @var string
		 */
		protected $item_class;

		/**
		 * TRUE = item not shown, FALSE = item shown
		 *
		 * @var boolean
		 */
		protected $hide_item;

		/**
		 * TRUE = item not shown on init, FALSE = item shown on init
		 *
		 * @var boolean
		 */
		protected $hide_item_init = false;

		/**
		 * TRUE = null values are allowed, FALSE = no null values allowed
		 *
		 * @var boolean
		 */
		protected $is_nullable;

		/**
		 * TRUE = is auto_increment column
		 *
		 * @var boolean
		 */
		protected $is_auto_increment = false;

		/**
		 * TRUE = column is part of primary key, FALSE = column is not part of primary key
		 *
		 * @var boolean
		 */
		protected $is_key_column;

		/**
		 * TRUE = column is part of foreign key, FALSE = column is not part of foreign  key
		 *
		 * @var boolean
		 */
		protected $is_foreign_key_column = false;

		/**
		 * Set to true to make item readonly
		 *
		 * @var boolean
		 */
		protected $is_readonly = false;

		/**
		 * Context variable to keep logic for showing items maintainable
		 *
		 * @var string
		 */
		protected $show_context_action;

		/**
		 * Context variable to keep logic for showing items maintainable
		 *
		 * @var string
		 */
		protected $show_context_update_keys_allowed;

		/**
		 * Context variable to keep logic for showing items maintainable
		 *
		 * @var string
		 */
		protected $show_context_column_value;

		/**
		 * Context variable to keep logic for showing items maintainable
		 *
		 * @var string
		 */
		protected $show_context_class_primary_key;

		/**
		 * Context variable to keep logic for showing items maintainable
		 *
		 * @var string
		 */
		protected $show_context_item_events;

		/**
		 * Item placeholder
		 *
		 * @var string
		 */
		protected $item_placeholder = '';

		/**
		 * Max length text column
		 *
		 * @var int
		 */
		protected $character_maximum_length;

		/**
		 * NUmeric precision
		 *
		 * @var int
		 */
		protected $numeric_precision;

		/**
		 * Numeric scale
		 *
		 * @var int
		 */
		protected $numeric_scale;

		public $checkbox_value_on; // Needed for Data Publisher

		/**
		 * WPDA_Simple_Form_Item constructor
		 *
		 * Declare item with all its properties.
		 *
		 * @param array $args [
		 *
		 * 'item_name'          => item name
		 *
		 * 'data_type'          => data type
		 *
		 * 'item_label'         => label
		 *
		 * 'item_value'         => value (in database)
		 *
		 * 'item_default_value' => default value
		 *
		 * 'item_extra'         => check column extra in information_schema.columns
		 *
		 * 'item_enum'          => enum (if applicable)
		 *
		 * 'item_enum_options'  => enum options (if applicable)
		 *
		 * 'column_type'        => type
		 *
		 * 'item_event'         => JS event(s)
		 *
		 * 'item_js'            => JS code (global)
		 *
		 * 'item_hide_icon'     => icon (showing data type)
		 *
		 * 'item_class'         => css class
		 *
		 * 'hide_item'          => item visibility
		 *
		 * 'is_nullable'        => allow null values?
		 *
		 * 'is_key_column'      => is key column?
		 *
		 * 'character_maximum_length' => max width text column
		 *
		 * 'numeric_precision'        => numeric precision numeric column
		 *
		 * 'numeric_scale'            => numeric scale numeric column
		 *
		 * ].
		 *
		 * @since   1.0.0
		 */
		public function __construct( $args = array() ) {
			if ( is_array( $args ) ) {
				$args = wp_parse_args(
					$args,
					array(
						'item_name'                => '',
						'data_type'                => '',
						'item_label'               => '',
						'item_value'               => null,
						'item_default_value'       => null,
						'item_extra'               => '',
						'item_enum'                => '',
						'item_enum_options'        => '',
						'item_enum_text'           => '',
						'column_type'              => '',
						'item_event'               => '',
						'item_js'                  => '',
						'item_hide_icon'           => false,
						'item_class'               => '',
						'hide_item'                => false,
						'is_nullable'              => null,
						'is_key_column'            => null,
						'character_maximum_length' => 0,
						'numeric_precision'        => 0,
						'numeric_scale'            => 0,
					)
				);

				if ( '' === $args['item_name'] ) {
					// Without an item name it makes no sense to continue
					wp_die( __( 'ERROR: Wrong arguments [missing item name]', 'wp-data-access' ) );
				}

				if ( '' === $args['data_type'] ) {
					// Without a data type it makes no sense to continue
					wp_die( __( 'ERROR: Wrong arguments [missing data type]', 'wp-data-access' ) );
				}

				$this->item_name      = $args['item_name'];
				$this->data_type      = WPDA::get_type( $args['data_type'] );
				$this->item_icon_type = $this->data_type;
				$this->item_label     = (string) $args['item_label']; //phpcs:ignore - 8.1 proof
				if ( null === $args['item_value'] ) {
					$this->item_value = null;
				} else {
					$this->item_value = (string) $args['item_value']; //phpcs:ignore - 8.1 proof
				}
				if (
                    'current_timestamp()' !== strtolower( $args['item_default_value'] ) &&
                    'current_timestamp' !== strtolower( $args['item_default_value'] )
                ) {
					$this->item_default_value = $args['item_default_value'];
				}
				$this->item_extra = $args['item_extra'];
				if ( 'auto_increment' === $this->item_extra ) {
					$this->is_auto_increment = true;
				}
				$this->item_enum         = '';
				$this->item_enum_options = '';
				$this->item_enum_text    = '';
				if ( 'enum' === $this->data_type ) {
					$this->item_enum = explode(
						',',
						str_replace(
							'\'',
							'',
							substr( substr( ( string ) $args['item_enum'], 5 ), 0, - 1 )
						)
					);//phpcs:ignore - 8.1 proof
				}
				if ( 'set' === $this->data_type ) {
					$this->item_enum = explode(
						',',
						str_replace(
							'\'',
							'',
							substr( substr( ( string ) $args['item_enum'], 4 ), 0, - 1 )
						)
					);//phpcs:ignore - 8.1 proof
				}
				$this->column_type    = $args['column_type'];
				$this->item_event     = $args['item_event'];
				$this->item_js        = $args['item_js'];
				$this->item_hide_icon = $args['item_hide_icon'];
				$this->item_class     = $args['item_class'];
				$this->hide_item      = $args['hide_item'];
				$this->is_nullable    = $args['is_nullable'];
				$this->is_key_column  = $args['is_key_column'];

				// Column length and numeric scale and precision
				$this->character_maximum_length = $args['character_maximum_length'];
				$this->numeric_precision        = $args['numeric_precision'];
				$this->numeric_scale            = $args['numeric_scale'];

			} elseif ( is_object( $args ) ) {
				if ( 0 <= strpos( self::class, 'WPDA_Simple_Form_Item' ) ) {
					foreach ( $args as $property => $value ) {
						$this->$property = $value;
					}
				} else {
					wp_die( __( 'Class must be of (sub)type WPDA_Simple_Form_Item', 'wp-data-access' ) );
				}
			} else {
				wp_die( __( 'Could not create form item instance [argument must be array or object]', 'wp-data-access' ) );
			}
		}

		/**
		 * Show item row
		 *
		 * @param string $action Requested action
		 * @param string $update_keys_allowed TRUE = allow key updates
		 */
		public function show( $action, $update_keys_allowed ) {
			if ( $this->hide_item && ! $this->is_auto_increment && ! $this->is_foreign_key_column ) {
				if ( 'new' === $action && '' !== $this->get_item_default_value() && null !== $this->get_item_default_value() ) {
					// Add hidden item to apply default value
				} else {
					// Do not show item, do not add hidden item to form, user is not allowed to view this item
					return;
				}
			}

			// Set context variables
			$this->show_context_action              = $action;
			$this->show_context_update_keys_allowed = $update_keys_allowed;

			if ( 'row' === substr( $this->item_class, 0, 3 ) ) {
				// Process row level class
				$row_class        = $this->item_class;
				$this->item_class = '';
			} else {
				$row_class = '';
			}

			if ( true === $this->hide_item_init || true === $this->hide_item ) {
				?>
				<tr style='display:none'<?php echo '' === $row_class ? '' : ' class="' . esc_attr( $row_class ) . '"'; ?>>
				<?php
			} else {
				?>
				<tr <?php echo '' === $row_class ? '' : ' class="' . esc_attr( $row_class ) . '"'; ?>>
				<?php
			}

			$label        = explode( '|', esc_attr( $this->item_label ) );//phpcs:ignore - 8.1 proof
			$label_before = $label[0];
			$label_after  = '';
			if ( isset( $label[1] ) ) {
				$label_after = $label[1];
			}

			if ( 'view' === $action ) {
				$label_title = ' ';
			} else {
				$label_title  = $label_before . ' ';
				$label_title .= 'NO' === $this->is_nullable ? __( 'must be entered', 'wp-data-access' ) : __( 'is optional', 'wp-data-access' );
			}
			?>
			<td class="label">
				<label for="<?php echo esc_attr( $this->item_name ); ?>"
					   title="<?php echo esc_attr( $label_title ); ?>"
					   class="wpda_tooltip"
				>
					<?php echo 'NO' === $this->is_nullable ? '*' : ''; ?>
					<?php echo esc_attr( $label_before ); ?>
				</label>
			</td>
			<td class="data">
				<?php
				// Get column value
				$this->show_context_column_value = esc_html( str_replace( '&', '&amp;', (string) $this->item_value ) );

				// Set primary key class(es)
				$this->show_context_class_primary_key = $this->is_key_column ? 'wpda_primary_key' : '';
				if ( $this->is_auto_increment ) {
					$this->show_context_class_primary_key .= ' auto_increment';
				}

				// Prepare events
				$this->show_context_item_events = '';
				if ( is_array( $this->item_event ) ) {
					foreach ( $this->item_event as $event_name => $event_code ) {
						$this->show_context_item_events .= "$event_name=$event_code ";
					}
				}

				// Show item
				$this->show_item();
				?>
				<input type="hidden"
					   name="<?php echo esc_attr( $this->item_name ); ?>_old"
					   value="<?php echo $this->show_context_column_value; // phpcs:ignore WordPress.Security.EscapeOutput ?>"
				/>
				<?php echo '' === $label_after ? '' : '<label>' . esc_attr( $label_after ) . '</label>'; ?>
			</td>
			<td class="icon">
				<?php
				// Add data type icon.
				if ( ! $this->item_hide_icon ) {
					$type_button = new WPDA_Simple_Form_Type_Icon( $this->item_name, $this->item_icon_type );
					$type_button->show();
				}
				?>
			</td>
			</tr>
			<?php
		}

		/**
		 * Displays default text item
		 *
		 * Overwrite this method to define specific item processing like enum, set, image and so on.
		 */
		protected function show_item() {
			// Set column value.
			if ( 'new' === $this->show_context_action ) {
				// Check if there is a default value.
				if ( $this->item_default_value !== null &&
					 strtolower( $this->item_default_value ) !== 'null' ) {
					$this->show_context_column_value = $this->item_default_value;
				}
			}

			// Add input box.
			$data_type = $this->data_type;
			if ( 'number' === $this->data_type ) {
				if ( 0 < $this->numeric_scale ) {
					$data_type = 'float';
				}
			}
			?>
			<input name="<?php echo esc_attr( $this->item_name ); ?>"
				   id="<?php echo esc_attr( $this->item_name ); ?>"
				   type="text"
				   value="<?php echo $this->show_context_column_value; // phpcs:ignore WordPress.Security.EscapeOutput ?>"
				   data-db-is-null="<?php echo null === $this->item_value ? 'true' : 'false'; ?>"
				   class="wpda_data_type_<?php echo esc_attr( $data_type ); ?> <?php echo esc_attr( $this->show_context_class_primary_key ); ?> <?php echo esc_attr( $this->item_class ); ?> <?php
					if ( 'NO' === $this->is_nullable && ! $this->is_auto_increment ) {
						echo 'wpda_not_null';
					}
					?>"
					<?php
					switch( $this->data_type ) {
						case 'string':
							echo 'maxlength="' . esc_attr( $this->character_maximum_length ) . '"';
							break;
						case 'number':
							echo 'data-number-format="' . esc_attr( $this->numeric_precision ) . ',' . esc_attr( $this->numeric_scale ) . '"';
					}
					?>
					<?php echo $this->is_readonly ? ' readonly' : ''; ?>
					<?php echo esc_attr( $this->show_context_item_events ); ?>
				    placeholder="<?php echo esc_attr( $this->item_placeholder ); ?>"
			/>
			<?php
			if ( 'string' === $this->data_type ) {
				?>
				<input name="<?php echo esc_attr( $this->item_name ); ?>_db_is_null"
					   value="<?php echo null === $this->item_value ? 'true' : 'false'; ?>"
					   type="hidden"
				/>
				<?php
			}
		}

		/**
		 * Het item name
		 *
		 * @return string
		 * @since   1.0.0
		 */
		public function get_item_name() {

			return $this->item_name;

		}

		/**
		 * Get item data type
		 *
		 * @return string
		 * @since   1.0.0
		 */
		public function get_data_type() {

			return $this->data_type;

		}

		/**
		 * Get item label
		 *
		 * @return string
		 * @since   1.0.0
		 */
		public function get_item_label() {

			return $this->item_label;

		}

		/**
		 * Get item value
		 *
		 * @return mixed
		 * @since   1.0.0
		 */
		public function get_item_value() {

			return $this->item_value;

		}

		/**
		 * Get item default value
		 *
		 * @return mixed
		 * @since   1.0.0
		 */
		public function get_item_default_value() {

			return $this->item_default_value;

		}

		/**
		 * Get item 'extra' info
		 *
		 * @return mixed
		 * @see WPDA_Simple_Form_Item::$item_extra
		 *
		 * @since   1.0.0
		 */
		public function get_item_extra() {

			return $this->item_extra;

		}

		/**
		 * Get enum values or empty
		 *
		 * @return array
		 * @since   1.0.0
		 */
		public function get_item_enum() {

			return $this->item_enum;

		}

		/**
		 * Get enum options or empty
		 *
		 * @return array
		 * @since   1.6.9
		 */
		public function get_item_enum_options() {

			return $this->item_enum_options;

		}

		/**
		 * Get enum text or empty
		 *
		 * @return array
		 * @since   1.6.9
		 */
		public function get_item_enum_text() {

			return $this->item_enum_text;

		}

		/**
		 * Get column type
		 *
		 * @return string
		 * @since   1.0.0
		 */
		public function get_column_type() {

			return $this->column_type;

		}

		/**
		 * Get item event
		 *
		 * @return String
		 * @since   1.0.0
		 */
		public function get_item_event() {

			return $this->item_event;

		}

		/**
		 * Get item Javascript code
		 *
		 * @return mixed
		 * @since   1.0.0
		 */
		public function get_item_js() {

			return $this->item_js;

		}

		/**
		 * Hide icon?
		 *
		 * @return boolean
		 * @since   1.0.0
		 */
		public function get_item_hide_icon() {

			return $this->item_hide_icon;

		}

		/**
		 * Get item CSS class
		 *
		 * @return string
		 * @since   1.0.0
		 */
		public function get_item_class() {

			return $this->item_class;

		}

		/**
		 * Get item visibility
		 *
		 * @return boolean
		 * @since   1.6.9
		 */
		public function get_hide_item() {

			return $this->hide_item;

		}

		/**
		 * Null values allowed?
		 *
		 * @return boolean
		 * @since   2.0.0
		 */
		public function is_nullable() {

			return $this->is_nullable;

		}

		/**
		 * Is column part of the primary key?
		 *
		 * @return boolean
		 * @since   2.0.0
		 */
		public function is_key_column() {

			return $this->is_key_column;

		}

		/**
		 * Item value
		 *
		 * @param string $value Item value
		 */
		public function set_item_value( $value ) {

			$this->item_value = $value;

		}

		/**
		 * Item label
		 *
		 * @param string $label Item label
		 */
		public function set_label( $label ) {

			$this->item_label = $label;

		}

		/**
		 * Set item default value
		 *
		 * @param string $item_default_value Default value
		 *
		 * @since   1.6.2
		 */
		public function set_item_default_value( $item_default_value ) {

			$this->item_default_value = $item_default_value;

		}

		/**
		 * Set item CSS class
		 *
		 * @param string $item_class HTML class name
		 *
		 * @since   1.6.2
		 */
		public function set_item_class( $item_class ) {

			$this->item_class = $item_class;

		}

		/**
		 * Set item visibility
		 *
		 * @param boolean $hide_item TRUE = hide item
		 *
		 * @since   1.6.9
		 */
		public function set_hide_item( $hide_item ) {

			$this->hide_item = $hide_item;

		}

		/**
		 * Set item js code
		 *
		 * @param string $item_js Item specific javascript code
		 *
		 * @since   1.6.9
		 */
		public function set_item_js( $item_js ) {

			$this->item_js = $item_js;

		}

		/**
		 * Set item enum
		 *
		 * @param string $item_enum Item enum value list
		 *
		 * @since   1.6.9
		 */
		public function set_enum( $item_enum ) {

			$this->item_enum = $item_enum;

		}

		/**
		 * Set item enum options
		 *
		 * @param string $item_enum_options Item enum option list
		 *
		 * @since   1.6.9
		 */
		public function set_enum_options( $item_enum_options ) {

			$this->item_enum_options = $item_enum_options;

		}

		/**
		 * Set item enum text
		 *
		 * @param string $item_enum_text Item enum text list.
		 *
		 * @since   1.6.9
		 */
		public function set_enum_text( $item_enum_text ) {

			$this->item_enum_text = $item_enum_text;

		}

		/**
		 * Set item data_type
		 *
		 * @param string $data_type Item data type
		 *
		 * @since   1.6.9
		 */
		public function set_data_type( $data_type ) {

			$this->data_type = $data_type;

		}

		/**
		 * Set item visibility
		 *
		 * @param boolean $item_hide_icon TRUE = hide type icon behind text field
		 *
		 * @since   2.0.8
		 */
		public function set_item_hide_icon( $item_hide_icon ) {

			$this->item_hide_icon = $item_hide_icon;

		}

		/**
		 * Set item is key column
		 *
		 * @param boolean $is_key_column TRUE|FALSE
		 */
		public function set_is_key_column( $is_key_column ) {

			$this->is_key_column = $is_key_column;

		}

		/**
		 * Set item is foreign key column
		 *
		 * @param boolean $is_foreign_key_column TRUE|FALSE
		 */
		public function set_foreign_key_column( $is_foreign_key_column ) {

			$this->is_foreign_key_column = $is_foreign_key_column;

		}

		/**
		 * Change item readonly property
		 *
		 * @param boolean $is_readonly TRUE|FALSE
		 * @return void
		 */
		public function set_readonly( $is_readonly ) {
			$this->is_readonly = $is_readonly;
		}

		/**
		 * Set item visibility on initialization
		 *
		 * @param boolean $hide_item_init TRUE = hide item init
		 *
		 * @since   1.6.9
		 */
		public function set_hide_item_init( $hide_item_init ) {

			$this->hide_item_init = $hide_item_init;

		}

		/**
		 * Check if item is valid
		 *
		 * @param $pre_insert
		 *
		 * @return bool
		 */
		public function is_valid( $pre_insert = false ) {
			if ( $this->is_auto_increment && $pre_insert ) {
				return true;
			}

			if ( 'NO' === $this->is_nullable ) {
				// Empty values are not allowed for this column: check value.
				if ( '' === $this->item_value ) {
					if (
						isset( $_REQUEST[ "{$this->item_name}_db_is_null" ] ) &&
						'false' === strtolower( $_REQUEST[ "{$this->item_name}_db_is_null" ] ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
					) {
						// Allow update if original column already was an empty string
						return true;
					}

					// No value: inform user and set validation to failed.
					$msg = new WPDA_Message_Box(
						array(
							'message_text'           => ucfirst( str_replace( '_', ' ', $this->item_name ) ) .
														' ' . __( 'must be entered', 'wp-data-access' ),
							'message_type'           => 'error',
							'message_is_dismissible' => false,
						)
					);
					$msg->box();

					return false;
				}
			}

			if ( 'number' === $this->data_type ) {
				if ( '' !== $this->item_value && null !== $this->item_value && ! is_numeric( $this->item_value ) ) {
					// Value is not numeric: inform user and set validation to failed.
					$msg = new WPDA_Message_Box(
						array(
							'message_text'           => ucfirst( str_replace( '_', ' ', $this->item_name ) ) .
														' ' . __( 'must be numeric', 'wp-data-access' ),
							'message_type'           => 'error',
							'message_is_dismissible' => false,
						)
					);
					$msg->box();

					return false;
				}
			}

			return true;
		}

	}

}
