<?php
/**
 * User list management for New User Approve.
 *
 * @package New_User_Approve
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit();
}
if ( ! class_exists( 'Pw_New_User_Approve_User_List' ) ) {
	/**
	 * Class Pw_New_User_Approve_User_List
	 *
	 * Handles the user list table modifications for approval workflow.
	 */
	class Pw_New_User_Approve_User_List {

		/**
		 * The only instance of pw_new_user_approve_user_list.
		 *
		 * @var Pw_New_User_Approve_User_List
		 */
		private static $instance;

		/**
		 * Returns the main instance.
		 *
		 * @return Pw_New_User_Approve_User_List
		 */
		public static function instance() {
			if ( ! isset( self::$instance ) ) {
				self::$instance = new Pw_New_User_Approve_User_List();
			}
			return self::$instance;
		}

		/**
		 * Constructor.
		 */
		private function __construct() {
			// Actions.
			add_action( 'load-users.php', array( $this, 'update_action' ) );
			add_action(
				'restrict_manage_users',
				array( $this, 'status_filter' ),
				10,
				1
			);
			add_action( 'pre_user_query', array( $this, 'filter_by_status' ) );
			add_action( 'admin_footer-users.php', array( $this, 'admin_footer' ) );
			add_action( 'load-users.php', array( $this, 'bulk_action' ) );
			add_action( 'admin_notices', array( $this, 'admin_notices' ) );
			add_action( 'show_user_profile', array( $this, 'profile_status_field' ) );
			add_action( 'edit_user_profile', array( $this, 'profile_status_field' ) );
			add_action(
				'edit_user_profile_update',
				array(
					$this,
					'save_profile_status_field',
				)
			);
			add_action( 'admin_menu', array( $this, 'pending_users_bubble' ), 999 );

			// Filters.
			add_filter(
				'user_row_actions',
				array( $this, 'user_table_actions' ),
				10,
				2
			);
			add_filter( 'manage_users_columns', array( $this, 'add_column' ) );
			add_filter(
				'manage_users_custom_column',
				array( $this, 'status_column' ),
				10,
				3
			);
		}

		/**
		 * Update the user status if the approve or deny link was clicked.
		 *
		 * @uses load-users.php
		 */
		public function update_action() {
			if (
				isset( $_GET['action'] ) &&
				in_array( $_GET['action'], array( 'approve', 'deny' ), true ) &&
				! isset( $_GET['new_role'] )
			) {
				check_admin_referer( 'new-user-approve' );

				$sendback = esc_url(
					remove_query_arg(
						array(
							'approved',
							'denied',
							'deleted',
							'ids',
							'pw-status-query-submit',
							'new_role',
						),
						wp_get_referer()
					)
				);
				if ( ! $sendback ) {
					$sendback = admin_url( 'users.php' );
				}

				$wp_list_table = _get_list_table( 'WP_Users_List_Table' );

				$pagenum  = $wp_list_table->get_pagenum();
				$sendback = esc_url(
					add_query_arg( 'paged', $pagenum, $sendback )
				);

				$status = ! empty( $_GET['action'] )
					? sanitize_key( $_GET['action'] )
					: '';
				$user   = ! empty( $_GET['user'] )
					? absint( wp_unslash( $_GET['user'] ) )
					: '';

				pw_new_user_approve()->update_user_status( $user, $status );

				if ( 'approve' === $_GET['action'] ) {
					$sendback = esc_url(
						add_query_arg(
							array(
								'approved' => 1,
								'ids'      => $user,
							),
							$sendback
						)
					);
				} else {
					$sendback = esc_url(
						add_query_arg(
							array(
								'denied' => 1,
								'ids'    => $user,
							),
							$sendback
						)
					);
				}

				wp_safe_redirect( $sendback );
				exit();
			}
		}

		/**
		 * Add the approve or deny link where appropriate.
		 *
		 * @uses user_row_actions
		 * @param array  $actions The row actions.
		 * @param object $user    The user object.
		 * @return array
		 */
		public function user_table_actions( $actions, $user ) {
			if ( get_current_user_id() === $user->ID ) {
				return $actions;
			}

			if ( is_super_admin( $user->ID ) ) {
				return $actions;
			}

			$user_status = pw_new_user_approve()->get_user_status( $user->ID );

			$approve_link = add_query_arg(
				array(
					'action' => 'approve',
					'user'   => $user->ID,
				)
			);
			$approve_link = remove_query_arg( array( 'new_role' ), $approve_link );
			$approve_link = wp_nonce_url( $approve_link, 'new-user-approve' );

			$deny_link = add_query_arg(
				array(
					'action' => 'deny',
					'user'   => $user->ID,
				)
			);
			$deny_link = remove_query_arg( array( 'new_role' ), $deny_link );
			$deny_link = wp_nonce_url( $deny_link, 'new-user-approve' );

			$approve_action =
				'<a href="' .
				esc_url( $approve_link ) .
				'">' .
				__( 'Approve', 'new-user-approve' ) .
				'</a>';
			$deny_action    =
				'<a href="' .
				esc_url( $deny_link ) .
				'">' .
				__( 'Deny', 'new-user-approve' ) .
				'</a>';

			if ( 'pending' === $user_status ) {
				$actions[] = $approve_action;
				$actions[] = $deny_action;
			} elseif ( 'approved' === $user_status ) {
				$actions[] = $deny_action;
			} elseif ( 'denied' === $user_status ) {
				$actions[] = $approve_action;
			}

			return $actions;
		}

		/**
		 * Add the status column to the user table.
		 *
		 * @uses manage_users_columns
		 * @param array $columns The columns array.
		 * @return array
		 */
		public function add_column( $columns ) {
			$the_columns['pw_user_status'] = __( 'Status', 'new-user-approve' );

			$newcol  = array_slice( $columns, 0, -1 );
			$newcol  = array_merge( $newcol, $the_columns );
			$columns = array_merge( $newcol, array_slice( $columns, 1 ) );

			return $columns;
		}

		/**
		 * Show the status of the user in the status column.
		 *
		 * @uses manage_users_custom_column
		 * @param string $val         The custom column content.
		 * @param string $column_name The column name.
		 * @param int    $user_id     The user ID.
		 * @return string
		 */
		public function status_column( $val, $column_name, $user_id ) {
			switch ( $column_name ) {
				case 'pw_user_status':
					$status = pw_new_user_approve()->get_user_status( $user_id );
					if ( 'approved' === $status ) {
						$status_i18n = __( 'approved', 'new-user-approve' );
					} elseif ( 'denied' === $status ) {
						$status_i18n = __( 'denied', 'new-user-approve' );
					} elseif ( 'pending' === $status ) {
						$status_i18n = __( 'pending', 'new-user-approve' );
					}
					return $status_i18n;

				default:
			}

			return $val;
		}

		/**
		 * Add a filter to the user table to filter by user status.
		 *
		 * @uses restrict_manage_users
		 * @param string $which Top or bottom filter position.
		 */
		public function status_filter( $which ) {
			$id = 'new_user_approve_filter-' . $which;

			$filter_button   = submit_button(
				__( 'Filter', 'new-user-approve' ),
				'button',
				'pw-status-query-submit-' . $which,
				false,
				array( 'id' => 'pw-status-query-submit-' . $which )
			);
			$filtered_status = $this->selected_status();
			?>
		<label class="screen-reader-text" for="
			<?php
			echo esc_attr(
				$id
			);
			?>
												"><?php esc_html_e( 'View all users', 'new-user-approve' ); ?></label>
		<select id="
			<?php
			echo esc_attr(
				$id
			);
			?>
					" name="<?php echo esc_attr( $id ); ?>" style="float: none; margin: 0 0 0 15px;">
			<option value="view_all">
			<?php
			esc_html_e(
				'View all users',
				'new-user-approve'
			);
			?>
										</option>
			<?php foreach ( pw_new_user_approve()->get_valid_statuses() as $status ) : ?>
			<option value="<?php echo esc_attr( $status ); ?>"
				<?php
				selected(
					$status,
					$filtered_status
				);
				?>
							><?php echo esc_html( $status ); ?></option>
		<?php endforeach; ?>
		</select>
			<?php
			if ( ! empty( $filter_button ) ) {
				echo wp_kses_post(
					apply_filters( 'new_user_approve_filter_button', $filter_button )
				);
			}
			?>
		<style>
			#pw-status-query-submit-top,#pw-status-query-submit-bottom {
				float: right;
				margin: 2px 0 0 5px;
			}
		</style>
			<?php
		}

		/**
		 * Modify the user query if the status filter is being used.
		 *
		 * @uses pre_user_query
		 * @param WP_User_Query $query The user query object.
		 */
		public function filter_by_status( $query ) {
			global $wpdb;

			if ( ! is_admin() ) {
				return;
			}

			if ( ! function_exists( 'get_current_screen' ) ) {
				return;
			}

			$screen = get_current_screen();

			if ( isset( $screen ) && 'users' !== $screen->id ) {
				return;
			}

			if ( null !== $this->selected_status() ) {
				$filter = $this->selected_status();

				if ( 'view_all' === $filter ) {
					return;
				}
				$query->query_from .= " INNER JOIN {$wpdb->usermeta} ON ( {$wpdb->users}.ID = $wpdb->usermeta.user_id )";

				if ( 'approved' === $filter ) {
					$query->query_fields = "DISTINCT SQL_CALC_FOUND_ROWS {$wpdb->users}.ID";
					$query->query_from  .= " LEFT JOIN {$wpdb->usermeta} AS mt1 ON ({$wpdb->users}.ID = mt1.user_id AND mt1.meta_key = 'pw_user_status')";
					$query->query_where .= " AND ( ( $wpdb->usermeta.meta_key = 'pw_user_status' AND CAST($wpdb->usermeta.meta_value AS CHAR) = 'approved' ) OR mt1.user_id IS NULL )";
				} else {
					$query->query_where .= " AND ( ($wpdb->usermeta.meta_key = 'pw_user_status' AND CAST($wpdb->usermeta.meta_value AS CHAR) = '{$filter}') )";
				}
			}
		}

		/**
		 * Get the currently selected user status filter.
		 *
		 * @return string|null
		 */
		private function selected_status() {
			if (
				isset( $_REQUEST['pw-status-query-submit-bottom'] ) && // phpcs:ignore WordPress.Security.NonceVerification.Recommended
				! empty( $_REQUEST['pw-status-query-submit-bottom'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			) {
				return esc_attr(
					isset( $_REQUEST['new_user_approve_filter-bottom'] ) && // phpcs:ignore WordPress.Security.NonceVerification.Recommended
					! empty( $_REQUEST['new_user_approve_filter-bottom'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
						? sanitize_text_field(
							wp_unslash(
								$_REQUEST['new_user_approve_filter-bottom'] // phpcs:ignore WordPress.Security.NonceVerification.Recommended
							)
						)
						: ''
				);
			} elseif (
				isset( $_REQUEST['new_user_approve_filter-top'] ) && // phpcs:ignore WordPress.Security.NonceVerification.Recommended
				! empty( $_REQUEST['new_user_approve_filter-bottom'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			) {
				return esc_attr(
					sanitize_text_field(
						wp_unslash( $_REQUEST['new_user_approve_filter-top'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
					)
				);
			}

			return null;
		}

		/**
		 * Use javascript to add the ability to bulk modify the status of users.
		 *
		 * @uses admin_footer-users.php
		 */
		public function admin_footer() {
			$screen = get_current_screen();

			if ( 'users' === $screen->id ) :
				?>
			<script type="text/javascript">
				jQuery(document).ready(function ($) {

					$('<option>').val('approve').text('
					<?php
					esc_attr_e(
						'Approve',
						'new-user-approve'
					);
					?>
														').appendTo("select[name='action']");
					$('<option>').val('approve').text('
					<?php
					esc_attr_e(
						'Approve',
						'new-user-approve'
					);
					?>
														').appendTo("select[name='action2']");

					$('<option>').val('deny').text('
					<?php
					esc_attr_e(
						'Deny',
						'new-user-approve'
					);
					?>
													').appendTo("select[name='action']");
					$('<option>').val('deny').text('
					<?php
					esc_attr_e(
						'Deny',
						'new-user-approve'
					);
					?>
													').appendTo("select[name='action2']");
				});
			</script>
				<?php
			endif;
		}

		/**
		 * Process the bulk status updates.
		 *
		 * @uses load-users.php
		 */
		public function bulk_action() {
			$screen = get_current_screen();

			if ( 'users' === $screen->id ) {
				// Get the action.
				$wp_list_table = _get_list_table( 'WP_Users_List_Table' );
				$action        = $wp_list_table->current_action();

				$allowed_actions = array( 'approve', 'deny' );
				if ( ! in_array( $action, $allowed_actions, true ) ) {
					return;
				}

				// Security check.
				check_admin_referer( 'bulk-users' );

				// Make sure ids are submitted.
				if ( isset( $_REQUEST['users'] ) ) {
					$user_ids = array_map( 'intval', $_REQUEST['users'] );
				}

				if ( empty( $user_ids ) ) {
					return;
				}

				$sendback = remove_query_arg(
					array(
						'approved',
						'denied',
						'deleted',
						'ids',
						'new_user_approve_filter',
						'new_user_approve_filter2',
						'pw-status-query-submit',
						'new_role',
					),
					wp_get_referer()
				);
				if ( ! $sendback ) {
					$sendback = admin_url( 'users.php' );
				}

				$pagenum  = $wp_list_table->get_pagenum();
				$sendback = add_query_arg( 'paged', $pagenum, $sendback );

				switch ( $action ) {
					case 'approve':
						$approved = 0;
						foreach ( $user_ids as $user_id ) {
							pw_new_user_approve()->update_user_status(
								$user_id,
								'approve'
							);
							++$approved;
						}

						$sendback = add_query_arg(
							array(
								'approved' => $approved,
								'ids'      => join( ',', $user_ids ),
							),
							$sendback
						);
						break;

					case 'deny':
						$denied = 0;
						foreach ( $user_ids as $user_id ) {
							pw_new_user_approve()->update_user_status(
								$user_id,
								'deny'
							);
							++$denied;
						}

						$sendback = add_query_arg(
							array(
								'denied' => $denied,
								'ids'    => join( ',', $user_ids ),
							),
							$sendback
						);
						break;

					default:
						return;
				}

				$sendback = remove_query_arg(
					array(
						'action',
						'action2',
						'tags_input',
						'post_author',
						'comment_status',
						'ping_status',
						'_status',
						'post',
						'bulk_edit',
						'post_view',
					),
					$sendback
				);

				wp_safe_redirect( esc_url( $sendback ) );
				exit();
			}
		}

		/**
		 * Show a message on the users page if a status has been updated.
		 *
		 * @uses admin_notices
		 */
		public function admin_notices() {
			$screen = get_current_screen();

			if ( 'users' !== $screen->id ) {
				return;
			}

			$message = null;

			if ( isset( $_REQUEST['denied'] ) && (int) $_REQUEST['denied'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
				$denied  = sanitize_text_field( wp_unslash( $_REQUEST['denied'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
				$message = sprintf(
					// translators: %s is the number of denied users.
					_n(
						'%s User denied.',
						'%s users denied.',
						$denied,
						'new-user-approve'
					),
					number_format_i18n( $denied )
				);
			}

			if ( isset( $_REQUEST['approved'] ) && (int) $_REQUEST['approved'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
				$approved = sanitize_text_field(
					wp_unslash( $_REQUEST['approved'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
				);
				$message  = sprintf(
					// translators: %s is the number of approved users.
					_n(
						'%s User approved.',
						'%s users approved.',
						$approved,
						'new-user-approve'
					),
					number_format_i18n( $approved )
				);
			}

			if ( ! empty( $message ) ) {
				echo wp_kses_post(
					'<div class="updated"><p>' . $message . '</p></div>'
				);
			}
		}

		/**
		 * Display the dropdown on the user profile page to allow an admin to update the user status.
		 *
		 * @uses show_user_profile
		 * @uses edit_user_profile
		 * @param object $user The user object.
		 */
		public function profile_status_field( $user ) {
			if ( get_current_user_id() === $user->ID ) {
				return;
			}

			$user_status = pw_new_user_approve()->get_user_status( $user->ID );
			?>
		<table class="form-table">
			<tr>
				<th><label for="new_user_approve_status">
				<?php
				esc_html_e(
					'Access Status',
					'new-user-approve'
				);
				?>
															</label>
				</th>
				<td>
				<?php wp_nonce_field( 'nua_approve_status_action', 'nua_approve_status_nonce' ); ?>

					<select id="new_user_approve_status" name="new_user_approve_status">
						<?php if ( 'pending' === $user_status ) : ?>
							<option value="">
							<?php
							esc_html_e(
								'-- Status --',
								'new-user-approve'
							);
							?>
												</option>
						<?php endif; ?>
						<?php foreach ( array( 'approved', 'denied' ) as $status ) : ?>
							<option
								value="<?php echo esc_attr( $status ); ?>"
								<?php
								selected(
									$status,
									$user_status
								);
								?>
										><?php echo esc_html( ucfirst( $status ) ); ?></option>
						<?php endforeach; ?>
					</select>
					<span
						class="description">
						<?php
						esc_html_e(
							'If user has access to sign in or not.',
							'new-user-approve'
						);
						?>
											</span>
					<?php if ( 'pending' === $user_status ) : ?>
						<br/>
						<span class="description">
						<?php
						printf(
							// translators: %s is the user pending status.
							esc_html__( 'Current user status is %s', 'new-user-approve' ),
							'<strong>' . esc_html( $user_status ) . '</strong>'
						);
						?>
						</span>
					<?php endif; ?>
				</td>
			</tr>
		</table>
			<?php
		}

		/**
		 * Save the user status when updating from the user profile.
		 *
		 * @uses edit_user_profile_update
		 * @param int $user_id The user ID.
		 * @return bool
		 */
		public function save_profile_status_field( $user_id ) {
			if ( ! current_user_can( 'edit_user', $user_id ) ) {
				return false;
			}
			if (
				isset( $_POST['nua_approve_status_nonce'] ) &&
				wp_verify_nonce(
					sanitize_text_field( wp_unslash( $_POST['nua_approve_status_nonce'] ) ),
					'nua_approve_status_action'
				)
			) {
				if ( ! empty( $_POST['new_user_approve_status'] ) ) {
					$new_status = sanitize_text_field(
						wp_unslash( $_POST['new_user_approve_status'] )
					);

					if ( 'approved' === $new_status ) {
						$new_status = 'approve';
					} elseif ( 'denied' === $new_status ) {
						$new_status = 'deny';
					}

					pw_new_user_approve()->update_user_status(
						$user_id,
						$new_status
					);
				}
			}
		}

		/**
		 * Add bubble for number of users pending to the user menu.
		 *
		 * @uses admin_menu
		 */
		public function pending_users_bubble() {
			global $menu;

			// Get the count stored in the option.
			$users = get_option( 'new_user_approve_user_statuses_count', array() );

			// If option is empty, initialize it.
			if ( empty( $users ) ) {
				$users = pw_new_user_approve()->get_count_of_user_statuses();
			}

			// Fetch the actual count of pending users from the database.
			global $wpdb;
			$actual_pending_users = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				"SELECT COUNT(*) FROM $wpdb->users 
				 WHERE ID IN (
					SELECT user_id FROM $wpdb->usermeta 
					WHERE meta_key = 'pw_user_status' AND meta_value = 'pending'
				 )"
			);

			// If the stored count is incorrect, update it.
			if ( $users['pending'] !== $actual_pending_users ) {
				$users['pending'] = $actual_pending_users;
				update_option( 'new_user_approve_user_statuses_count', $users );
			}

			// Make sure there are pending users.
			if ( $actual_pending_users > 0 ) {
				// Locate the key of users.php in the menu.
				$key = $this->recursive_array_search( 'users.php', $menu );

				// Not found, just in case.
				if ( ! $key ) {
					return;
				}

				// Modify menu item.
				// phpcs:ignore
				$menu[$key][0] .= sprintf(
					'<span class="update-plugins dddd count-%1$s" style="background-color:white;color:black;margin-left:5px;">
						<span class="plugin-count">%1$s</span>
					</span>',
					$actual_pending_users
				);
			}
		}

		/**
		 * Recursively search the menu array to determine the key to place the bubble.
		 *
		 * @param string $needle   The value to search for.
		 * @param array  $haystack The array to search.
		 * @return bool|int|string
		 */
		public function recursive_array_search( $needle, $haystack ) {
			foreach ( $haystack as $key => $value ) {
				$current_key = $key;
				if (
					$needle === $value ||
					( is_array( $value ) &&
						$this->recursive_array_search( $needle, $value ) !==
							false )
				) {
					return $current_key;
				}
			}
			return false;
		}
	}
}
// phpcs:ignore
function pw_new_user_approve_user_list() {
	return Pw_New_User_Approve_User_List::instance();
}

pw_new_user_approve_user_list();
