<?php

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

final class iron_securi_Login_Logout_Functionality {

	const OPTION_NAME = 'wpironis_plugin_settings_loginlogout';
	const OPTION_NAME_ACTIVITY = 'iron_security_last_activity';
	const SESSION_LAST_ACTIVITY = 'iron_security_last_activity';
	const OPTION_NAME_ADMIN_SLUG = 'wpironis_custom_login_slug';
	const WPIRONIS_USE_404 = false;

	public $version;
	private WpironHelper $helper;

	public function __construct(WpironHelper $helper) {
		$this->helper      = $helper;
	}

	public function wpironis_deny_and_exit() {
		if ( self::WPIRONIS_USE_404 ) {
			status_header( 404 );
			if ( $tmpl = get_404_template() ) {
				include $tmpl;
			}
		} else {
			wp_safe_redirect( home_url( '/' ) );
		}
		nocache_headers();
		exit;
	}

	public function wpironis_loginUrl( $redirect ) {
		return add_query_arg( 'redirect_to', urlencode( $redirect ), home_url( '/' . $this->getAdminSlug() ) );
	}

	public function wpironis_siteUrl( $url, $path, $scheme ) {
		if ( stripos( $path, 'wp-login.php' ) === false ) {
			return $url;
		}

		$is_core_logout  = ( $scheme === 'logout' );
		$is_query_logout = isset( $path ) && str_contains( $path, 'action=logout' );

		if ( $is_core_logout || $is_query_logout ) {
			return $url;
		}

		return home_url( '/' . $this->getAdminSlug() );
	}

	public function wpironis_parseRequest( $wp ) {
		if ( trim( $wp->request, '/' ) === $this->getAdminSlug() ) {
			define( 'WPIRONIS_CUSTOM_LOGIN', true );

			if ( isset( $_GET['action'] ) && $_GET['action'] === 'logout' ) {
				$logout_nonce = $_GET['_wpnonce'] ?? '';

				if ( ! wp_verify_nonce( $logout_nonce, 'log-out' ) ) {
					wp_die( esc_attr(__( 'Logout nonce failed.', 'iron-security' )) );
				}

				wp_logout();
				wp_safe_redirect( add_query_arg( 'loggedout', 'true', home_url( '/' . cau_get_slug() ) ) );
				exit;
			}

			global $user_login, $error, $interim_login;
			$user_login    = '';
			$error         = '';
			$interim_login = false;

			require_once ABSPATH . 'wp-login.php';
			exit;
		}
	}

	function wpironis_register_rewrites() {
		$slug = $this->getAdminSlug();
		add_rewrite_tag( '%wpironis_login%', '1' );
		add_rewrite_rule( '^' . preg_quote( $slug, '/' ) . '/?$', 'index.php?wpironis_login=1', 'top' );
	}

	private function getAdminSlug() {
		return sanitize_title( $this->helper->wpironis_getOption( self::OPTION_NAME, self::OPTION_NAME_ADMIN_SLUG ) );
	}

	public function settingsFields( $self ) {
		add_settings_section(
			'wpironis_login_logout_section',
			'',
			false,
			'wpironis_login_logout'
		);
		add_settings_field(
			'wpironis_custom_login_slug',
			'Custom Admin Area URL slug (wp-admin)',
			false,
			'wpironis_login_logout',
			'wpironis_login_logout_section'
		);
		add_settings_field(
			'wpironis_session_timeout',
			'Session Timeout',
			false,
			'wpironis_login_logout',
			'wpironis_login_logout_section'
		);
		add_settings_field(
			'wpironis_limit_login_attempts',
			'Limit Login Attempts',
			false,
			'wpironis_login_logout',
			'wpironis_login_logout_section'
		);
		add_settings_field(
			'wpironis_2fa',
			'2FA - Two Factor Authenticator',
			false,
			'wpironis_login_logout',
			'wpironis_login_logout_section'
		);
	}

	public function wpironis_set_session_timeout( $expireIn, $is_enabled, $timeout_value ) {
		if ( $is_enabled && $timeout_value ) {
			return (int) $timeout_value;
		}

		return $expireIn;
	}

	public function wpironsecwp_block_locked_out_ip_early() {
		$ip = $this->get_user_ip();

		if ( get_transient( $ip . '_wpis_lockout' ) ) {
			$request_uri      = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( $_SERVER['REQUEST_URI'] ) : '';
			$is_admin_request = ( str_contains( $request_uri, '/wp-admin' ) || str_contains( $request_uri,
					'wp-login.php' ) );

			if ( $is_admin_request ) {
				header( 'HTTP/1.1 403 Forbidden' );
				header( 'Content-Type: text/html; charset=UTF-8' );
				$this->wpironis_lockout_html();
				exit;
			}
		}
	}

	public function wpironsecwp_block_locked_out_users( $user, $username, $password ) {
		$ip_address = $this->get_user_ip();
		if ( get_transient( $ip_address . '_wpis_lockout' ) ) {
			$this->wpisec_force_logout_by_ip( $ip_address );
			add_action( 'login_init', function () {
				status_header( 403 );
				nocache_headers();
				$this->wpironis_lockout_html();
				exit;
			} );

			add_action( 'login_form', function () {
				exit;
			} );

			add_action( 'admin_init', function () {
				status_header( 403 );
				nocache_headers();
				$this->wpironis_lockout_html();
				exit;
			} );

			add_filter( 'login_message', function () {
				status_header( 403 );
				nocache_headers();
				$this->wpironis_lockout_html();
				exit;
			} );

			return new WP_Error(
				'locked_out',
				sprintf(
					__( 'Too many failed login attempts. ', 'iron-security' )
				)
			);
		}

		return $user;
	}

	public function wpisec_force_logout_by_ip( $ip_address ) {
		$users = get_users();

		foreach ( $users as $user ) {
			$sessions = WP_Session_Tokens::get_instance( $user->ID );
			$sessions->destroy_all();
		}

		if ( is_user_logged_in() ) {
			wp_destroy_current_session();
			wp_clear_auth_cookie();
			wp_redirect( home_url() );
			exit;
		}
	}

	public function get_user_ip() {
		$remote_addr     = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( $_SERVER['REMOTE_ADDR'] ) : '0.0.0.0';
		$trusted_proxies = apply_filters( 'iron_security_trusted_proxies', [
			'173.245.48.0/20',
			'103.21.244.0/22',
			'103.22.200.0/22',
			'103.31.4.0/22',
			'141.101.64.0/18',
			'108.162.192.0/18',
			'190.93.240.0/20',
			'188.114.96.0/20',
			'197.234.240.0/22',
			'198.41.128.0/17',
			'162.158.0.0/15',
			'104.16.0.0/13',
			'104.24.0.0/14',
			'172.64.0.0/13',
			'131.0.72.0/22',
		] );

		$is_trusted_proxy = $this->ip_in_trusted_range( $remote_addr, $trusted_proxies );

		if ( $is_trusted_proxy && ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
			$forwarded_ips       = array_map( 'trim',
				explode( ',', sanitize_text_field( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) );
			$client_ip_candidate = reset( $forwarded_ips );
			if ( filter_var( $client_ip_candidate, FILTER_VALIDATE_IP ) ) {
				return $client_ip_candidate;
			}
		}

		return $remote_addr;
	}

	private function ip_in_trusted_range( $ip, $ranges ) {
		if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
			return false;
		}

		foreach ( $ranges as $range ) {
			if ( str_contains( $range, '/' ) ) {
				list( $subnet, $mask ) = explode( '/', $range );

				$ip_long     = ip2long( $ip );
				$subnet_long = ip2long( $subnet );

				$mask_bits = 0xffffffff << ( 32 - intval( $mask ) );
				$mask_ip   = $ip_long & $mask_bits;

				if ( $mask_ip === ( $subnet_long & $mask_bits ) ) {
					return true;
				}
			} else {
				if ( $ip === $range ) {
					return true;
				}
			}
		}

		return false;
	}

	public function wpironis_lockout_html() {
		echo '<!DOCTYPE html>
				<html>
					<head>
						<title>' .esc_attr(__( 'Access Denied', 'iron-security' )) . '</title>
						<style>
							body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
							h1 { color: #d63638; }
							.message { background: #f8f8f8; border-left: 4px solid #d63638; padding: 12px; margin: 20px 0; }
							.timer { font-weight: bold; }
						</style>
					</head>
					<body>
						<h1>' . esc_attr(__( 'Access Denied', 'iron-security' )) . '</h1>
						<div class="message">
							<p>' . esc_attr(__( 'Your IP address has been temporarily blocked due to too many failed login attempts.',
				'iron-security' )) . '</p>
						</div>
					</body>
				</html>';
	}

	public function customUrlToggle( $enabled ) {
		$enabled = $enabled ?? false;
		$options = $this->helper->wpironis_getOption( self::OPTION_NAME );

		$options['enable_slug_change'] = $enabled ? '1' : '0';
		$updated                       = $this->helper->wpironis_updateOption( self::OPTION_NAME, $options );

		if ( $updated ) {
			flush_rewrite_rules();
			wp_send_json_success( array(
				'message' => __( 'Custom admin URL setting updated successfully', 'iron-security' ),
				'enabled' => $enabled,
				'options' => $options
			) );
		} else {
			$current_options = $this->helper->wpironis_getOption( self::OPTION_NAME );
			if ( isset( $current_options['enable_slug_change'] ) && $current_options['enable_slug_change'] === ( $enabled ? '1' : '0' ) ) {
				wp_send_json_success( array(
					'message' => __( 'Custom admin URL setting is already set to this value', 'iron-security' ),
					'enabled' => $enabled,
					'options' => $current_options
				) );
			} else {
				wp_send_json_error( __( 'Failed to update custom admin URL setting', 'iron-security' ) );
			}
		}
	}

	public function wpironis_block_entry_points() {
		$uri         = $_SERVER['REQUEST_URI'] ?? '';
		$path        = rawurldecode( wp_parse_url( $uri, PHP_URL_PATH ) );
		$path_lc     = strtolower( $path );
		$is_wp_login = strpos( $path_lc, 'wp-login.php' ) !== false;
		$is_logout   = isset( $_GET['action'], $_GET['_wpnonce'] )
		               && $_GET['action'] === 'logout'
		               && wp_verify_nonce( $_GET['_wpnonce'], 'log-out' );

		if ( $is_wp_login && ! defined( 'WPIRONIS_CUSTOM_LOGIN' ) && ! $is_logout ) {
			$this->wpironis_deny_and_exit();
		}

		if ( is_admin() && ! is_user_logged_in() && ! wp_doing_ajax() &&
		     ! preg_match( '#(admin-post|install)\.php$#i', $path_lc ) ) {
			$this->wpironis_deny_and_exit();
		}
	}

	public function customUrlSave( $url ) {
		if ( empty( $url ) ) {
			wp_send_json_error( 'Invalid URL provided' );

			return;
		}

		$options                               = $this->helper->wpironis_getOption( self::OPTION_NAME );
		$options['wpironis_custom_login_slug'] = $url;
		$updated                               = $this->helper->wpironis_updateOption( self::OPTION_NAME, $options );

		if ( $updated ) {
			$this->wpironis_register_rewrites();
			flush_rewrite_rules();
			wp_send_json_success( array(
				'message' => __( 'Custom admin URL saved successfully', 'iron-security' ),
				'url'     => $url,
				'options' => $options
			) );
		} else {
			$current_options = $this->helper->wpironis_getOption( self::OPTION_NAME );
			if ( isset( $current_options['wpironis_custom_login_slug'] ) && $current_options['wpironis_custom_login_slug'] === $url ) {
				wp_send_json_success( array(
					'message' => __( 'Custom admin URL is already set to this value', 'iron-security' ),
					'url'     => $url,
					'options' => $current_options
				) );
			} else {
				wp_send_json_error( __( 'Failed to save custom admin URL', 'iron-security' ) );
			}
		}
	}

	public function sessionTimeoutToggle( $enabled ) {
		$enabled = $enabled ?? false;
		$options = $this->helper->wpironis_getOption( self::OPTION_NAME );

		$options['enable_session_timeout'] = $enabled ? '1' : '0';
		$updated                           = $this->helper->wpironis_updateOption( self::OPTION_NAME, $options );

		if ( $updated ) {
			wp_send_json_success( array(
				'message' => __( 'Session timeout setting updated successfully', 'iron-security' ),
				'enabled' => $enabled,
				'options' => $options
			) );
		} else {
			$current_options = $this->helper->wpironis_getOption( self::OPTION_NAME );
			if ( isset( $current_options['enable_session_timeout'] ) && $current_options['enable_session_timeout'] === ( $enabled ? '1' : '0' ) ) {
				wp_send_json_success( array(
					'message' => __( 'Session timeout setting is already set to this value', 'iron-security' ),
					'enabled' => $enabled,
					'options' => $current_options
				) );
			} else {
				wp_send_json_error( __( 'Failed to update session timeout setting', 'iron-security' ) );
			}
		}
	}

	public function sessionTimeoutSave( $timeout ) {
		if ( $timeout < 1 ) {
			wp_send_json_error( 'Invalid timeout value. Must be at least 1 second.' );

			return;
		}

		$options                          = $this->helper->wpironis_getOption( self::OPTION_NAME );
		$options['session_timeout_value'] = $timeout;
		$updated                          = $this->helper->wpironis_updateOption( self::OPTION_NAME, $options );

		if ( $updated ) {
			wp_send_json_success( array(
				'message' => __( 'Session timeout value saved successfully', 'iron-security' ),
				'timeout' => $timeout,
				'options' => $options
			) );
		} else {
			$current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
			if ( isset( $current_options['session_timeout_value'] ) &&
			     $current_options['session_timeout_value'] === (string) $timeout ) {
				wp_send_json_success( array(
					'message' => __( 'Session timeout value is already set to this value', 'iron-security' ),
					'timeout' => $timeout,
					'options' => $current_options
				) );
			} else {
				wp_send_json_error( __( 'Failed to save session timeout value', 'iron-security' ) );
			}
		}
	}

	public function limitLoginToggle( $enabled ) {
		$enabled = $enabled ?? false;

		$options                                = $this->helper->wpironis_getOption( self::OPTION_NAME );
		$options['enable_limit_login_attempts'] = $enabled ? '1' : '0';
		$updated                                = $this->helper->wpironis_updateOption( self::OPTION_NAME, $options );

		if ( $updated ) {
			wp_send_json_success( array(
				'message' => __( 'Limit login attempts setting updated successfully', 'iron-security' ),
				'enabled' => $enabled,
				'options' => $options
			) );
		} else {
			$current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
			if ( isset( $current_options['enable_limit_login_attempts'] ) && $current_options['enable_limit_login_attempts'] === ( $enabled ? '1' : '0' ) ) {
				wp_send_json_success( array(
					'message' => __( 'Limit login attempts setting is already set to this value', 'iron-security' ),
					'enabled' => $enabled,
					'options' => $current_options
				) );
			} else {
				wp_send_json_error( __( 'Failed to update limit login attempts setting', 'iron-security' ) );
			}
		}
	}

	public function limitLoginSave( $attempts, $duration ) {
		if ( $attempts < 1 || $duration < 1 ) {
			wp_send_json_error( __( 'Invalid attempts value. Must be at least 1.', 'iron-security' ) );

			return;
		}

		if ( $duration < 1 ) {
			wp_send_json_error( __( 'Invalid duration value. Must be at least 1 second.', 'iron-security' ) );

			return;
		}

		$options                                  = $this->helper->wpironis_getOption( self::OPTION_NAME );
		$options['wpironis_limit_login_attempts'] = $attempts;
		$options['wpironis_lockout_duration']     = $duration;
		$updated                                  = $this->helper->wpironis_updateOption( self::OPTION_NAME, $options );

		if ( $updated ) {
			wp_send_json_success( array(
				'message'  => __( 'Login attempt limits saved successfully', 'iron-security' ),
				'attempts' => (string) $attempts,
				'duration' => (string) $duration,
				'options'  => $options
			) );
		} else {
			$current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
			if ( isset( $current_options['wpironis_limit_login_attempts'] ) && isset( $current_options['wpironis_lockout_duration'] ) && $current_options['wpironis_limit_login_attempts'] === (string) $attempts &&
			     $current_options['wpironis_lockout_duration'] === (string) $duration ) {
				wp_send_json_success( array(
					'message'  => __( 'Login attempt limits are already set to these values', 'iron-security' ),
					'attempts' => $attempts,
					'duration' => $duration,
					'options'  => $current_options
				) );
			} else {
				wp_send_json_error( __( 'Failed to save login attempt limits', 'iron-security' ) );
			}
		}
	}

	public function userEnumerationToggle( $enabled ) {
		$enabled                            = $enabled ?? false;
		$options                            = $this->helper->wpironis_getOption( self::OPTION_NAME );
		$options['enable_user_enumeration'] = $enabled ? '1' : '0';
		$updated                            = $this->helper->wpironis_updateOption( self::OPTION_NAME, $options );

		if ( $updated ) {
			wp_send_json_success( array(
				'message' => __( 'User enumeration protection setting updated successfully', 'iron-security' ),
				'enabled' => $enabled,
				'options' => $options
			) );
		} else {
			$current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
			if ( isset( $current_options['enable_user_enumeration'] ) &&
			     $current_options['enable_user_enumeration'] === ( $enabled ? '1' : '0' ) ) {
				wp_send_json_success( array(
					'message' => __( 'User enumeration protection setting is already set to this value',
						'iron-security' ),
					'enabled' => $enabled,
					'options' => $current_options
				) );
			} else {
				wp_send_json_error( __( 'Failed to update user enumeration protection setting', 'iron-security' ) );
			}
		}
	}

	public function userEnumerationSave( $message ) {
		if ( empty( $message ) ) {
			wp_send_json_error( 'Invalid message provided' );

			return;
		}

		$options                             = $this->helper->wpironis_getOption( self::OPTION_NAME );
		$options['user_enumeration_message'] = $message;
		$updated                             = $this->helper->wpironis_updateOption( self::OPTION_NAME, $options );

		if ( $updated ) {
			wp_send_json_success( array(
				'message'      => __( 'Custom error message saved successfully', 'iron-security' ),
				'user_message' => $message,
				'options'      => $options
			) );
		} else {
			$current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
			if ( isset( $current_options['user_enumeration_message'] ) && $current_options['user_enumeration_message'] === $message ) {
				wp_send_json_success( array(
					'message'      => __( 'Custom error message is already set to this value', 'iron-security' ),
					'user_message' => $message,
					'options'      => $current_options
				) );
			} else {
				wp_send_json_error( __( 'Failed to save custom error message', 'iron-security' ) );
			}
		}
	}

	public function modifyAdminLoginErrorMessage( $error ) {
		$options = $this->helper->wpironis_getOption( self::OPTION_NAME );

		if ( ! empty( $options['enable_user_enumeration'] ) && $options['enable_user_enumeration'] === '1' ) {
			$custom_message = ! empty( $options['user_enumeration_message'] )
				? $options['user_enumeration_message']
				: __( 'Something went wrong. Please try again.', 'iron-security' );

			if (
				str_contains( $error, 'password you entered for' ) ||
				str_contains( $error, 'is not registered on this site' ) ||
				str_contains( $error, 'password you entered for' )
			) {
				return $custom_message;
			}

			$options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
			if ( ! empty( $options['custom_login_error'] ) ) {
				return $options['custom_login_error'];
			}
		}

		return $error;
	}

	public function blockUserEnumeration( $author ) {
		$options = $this->helper->wpironis_getOption( self::OPTION_NAME );

		if ( ! empty( $options['enable_user_enumeration'] ) && $options['enable_user_enumeration'] === '1' ) {
			if ( is_author() ) {
				wp_redirect( home_url(), 301 );
				exit;
			}

			add_filter( 'rest_endpoints', function ( $endpoints ) {
				if ( isset( $endpoints['/wp/v2/users'] ) ) {
					unset( $endpoints['/wp/v2/users'] );
				}
				if ( isset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ) ) {
					unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
				}

				return $endpoints;
			} );

			if ( isset( $author ) && is_numeric( $author ) ) {
				wp_redirect( home_url(), 301 );
				exit;
			}
		}
	}

	public function handleAdminIdChangesToggle( $enabled ) {
		$loginlogout_settings                               = $this->helper->wpironis_getOption( self::OPTION_NAME );
		$loginlogout_settings['enable_admin_id_protection'] = $enabled ? '1' : '0';

		if ( $enabled ) {
			if ( ! empty( $loginlogout_settings['current_admin_id'] ) ) {
				$admin_id = $loginlogout_settings['current_admin_id'];
				$message  = __( 'Admin ID protection enabled. Using current administrator ID: ',
						'iron-security' ) . $admin_id;
			} elseif ( ! get_userdata( 1 ) ) {
				$admins = get_users( array( 'role' => 'administrator', 'number' => 1 ) );
				if ( ! empty( $admins ) ) {
					$admin_id                                 = $admins[0]->ID;
					$loginlogout_settings['current_admin_id'] = $admin_id;
					$message                                  = __( 'Admin ID protection enabled. Using existing administrator ID: ',
							'iron-security' ) . $admin_id;
				} else {
					$loginlogout_settings['enable_admin_id_protection'] = '0';
					wp_send_json_error( __( 'No administrator accounts found.', 'iron-security' ) );

					return;
				}
			} else {
				try {
					$new_admin_id = $this->wpironis_change_admin_id();
					if ( $new_admin_id ) {
						$admin_id                                 = $new_admin_id;
						$loginlogout_settings['current_admin_id'] = $new_admin_id;
						$message                                  = __( 'Admin ID protection enabled. Administrator ID changed from 1 to ',
								'iron-security' ) . $admin_id;
					} else {
						$loginlogout_settings['enable_admin_id_protection'] = '0';
						wp_send_json_error( __( 'Failed to change admin ID. Please try again.', 'iron-security' ) );

						return;
					}
				} catch ( Exception $e ) {
					$loginlogout_settings['enable_admin_id_protection'] = '0';
					wp_send_json_error( 'Error: ' . $e->getMessage() );

					return;
				}
			}
		} else {
			$admin_id = ! empty( $loginlogout_settings['current_admin_id'] ) ? $loginlogout_settings['current_admin_id'] : '1';
			$message  = __( 'Admin ID protection disabled. Note that the admin ID will remain changed.',
				'iron-security' );
		}

		$this->helper->wpironis_updateOption( self::OPTION_NAME, $loginlogout_settings );

		wp_send_json_success( array(
			'message'  => $message,
			'admin_id' => $admin_id
		) );
	}

	private function wpironis_change_admin_id() {
		global $wpdb;
		$user = get_userdata( 1 );
		if ( ! $user ) {
			return false;
		}

		if ( ! in_array( 'administrator', $user->roles ) ) {
			return false;
		}

		$wpdb->query( 'START TRANSACTION' );
		try {
			$highest_id = $wpdb->get_var( "SELECT MAX(ID) FROM {$wpdb->users}" );
			$new_id     = (int) $highest_id + 1;
			$result     = $wpdb->query( $wpdb->prepare(
				"UPDATE {$wpdb->users} SET ID = %d WHERE ID = 1",
				$new_id
			) );

			if ( $result === false ) {
				throw new Exception( __( 'Failed to update user ID in users table: ',
						'iron-security' ) . $wpdb->last_error );
			}

			$result = $wpdb->query( $wpdb->prepare(
				"UPDATE {$wpdb->usermeta} SET user_id = %d WHERE user_id = 1",
				$new_id
			) );

			if ( $result === false ) {
				throw new Exception( __( 'Failed to update user ID in usermeta table: ',
						'iron-security' ) . $wpdb->last_error );
			}

			$result = $wpdb->query( $wpdb->prepare(
				"UPDATE {$wpdb->posts} SET post_author = %d WHERE post_author = 1",
				$new_id
			) );

			if ( $result === false ) {
				throw new Exception( __( 'Failed to update post authors: ', 'iron-security' ) . $wpdb->last_error );
			}

			$result = $wpdb->query( $wpdb->prepare(
				"UPDATE {$wpdb->comments} SET user_id = %d WHERE user_id = 1",
				$new_id
			) );

			if ( $result === false ) {
				throw new Exception( __( 'Failed to update comment authors: ', 'iron-security' ) . $wpdb->last_error );
			}

			if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}woocommerce_orders'" ) ) {
				$wpdb->query( $wpdb->prepare(
					"UPDATE {$wpdb->prefix}woocommerce_orders SET customer_id = %d WHERE customer_id = 1",
					$new_id
				) );
			}

			if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}bp_activity'" ) ) {
				$wpdb->query( $wpdb->prepare(
					"UPDATE {$wpdb->prefix}bp_activity SET user_id = %d WHERE user_id = 1",
					$new_id
				) );
			}

			$wpdb->query( 'COMMIT' );

			wp_cache_delete( 1, 'users' );
			wp_cache_delete( 1, 'user_meta' );
			wp_cache_delete( $new_id, 'users' );
			wp_cache_delete( $new_id, 'user_meta' );

			return (string) $new_id;
		} catch ( Exception $e ) {
			$wpdb->query( 'ROLLBACK' );

			throw $e;
		}
	}

	public function handleAdminUsernameSave( $new_username, $new_nickname = '' ) {

		if ( empty( $new_username ) ) {
			wp_send_json_error( __( 'Username cannot be empty.', 'iron-security' ) );
		}

		if ( ! validate_username( $new_username ) ) {
			wp_send_json_error( __( 'Invalid username. Use only letters, numbers, underscores.', 'iron-security' ) );
		}

		$admin_users = get_users(
			array(
				'role'    => 'administrator',
				'meta_query' => array(
					'relation' => 'OR',
					array(
						'key'       => 'user_login',
						'value'     => 'admin',
						'compare'   => '=',
						'column'    => 'user_login',
					),
					array(
						'key'       => 'nickname',
						'value'     => 'admin',
						'compare'   => '=',
					),
				),
				'fields'  => 'all',
			)
		);

		if ( empty( $admin_users ) ) {
			wp_send_json_success(
				array( 'message' => __( 'No insecure administrator accounts found.', 'iron-security' ) )
			);
		}

		$make_unique = function ( $base ) {
			$candidate = $base;
			$suffix    = 1;
			while ( username_exists( $candidate ) ) {
				$candidate = $base . $suffix;
				$suffix++;
			}
			return $candidate;
		};

		$changed = array();

		foreach ( $admin_users as $user ) {

			$unique_login = $make_unique( $new_username );
			$old_nickname = get_user_meta( $user->ID, 'nickname', true );

			wp_update_user(
				array(
					'ID'         => $user->ID,
					'user_login' => $unique_login,
				)
			);

			if ( ! empty( $new_nickname ) ) {
				update_user_meta( $user->ID, 'nickname', $new_nickname );

				if ( $user->display_name === $old_nickname ) {
					wp_update_user(
						array(
							'ID'           => $user->ID,
							'display_name' => $new_nickname,
						)
					);
				}
			}

			clean_user_cache( $user->ID );

			$changed[] = array(
				'ID'            => $user->ID,
				'old_username'  => $user->user_login,
				'new_username'  => $unique_login,
				'old_nickname'  => $old_nickname,
				'new_nickname'  => $new_nickname,
			);
		}

		wp_send_json_success(
			array(
				'message'       => sprintf(
				/* translators: %d: Name of a nadministrator */
					_n(
						'%d administrator account was updated.',
						'%d administrator accounts were updated.',
						count( $changed ),
						'iron-security'
					),
					count( $changed )
				),
				'changed_users' => $changed,
			)
		);
	}

	private function wpironis_get_unique_username( $username ) {
		$original_username = $username;
		$suffix            = 1;

		while ( username_exists( $username ) ) {
			$username = $original_username . $suffix;
			$suffix ++;
		}

		return $username;
	}

	private function wpironis_get_admin_usernames() {
		$insecure_usernames = array( 'admin', 'administrator', 'wordpress' );
		$found_usernames    = array();

		foreach ( $insecure_usernames as $username ) {
			$user = get_user_by( 'login', $username );
			if ( $user && in_array( 'administrator', $user->roles ) ) {
				$found_usernames[] = $username;
			}
		}

		return $found_usernames;
	}

	public function handleLimitsForAdminAccountsToggle( $enabled ) {
		$options                        = $this->helper->wpironis_getOption( self::OPTION_NAME );
		$options['enable_limit_admins'] = $enabled ? '1' : '0';
		$updated                        = $this->helper->wpironis_updateOption( self::OPTION_NAME, $options );

		if ( $updated ) {
			$response_data = array(
				'message' => $enabled ? __( 'Admin limitation has been enabled',
					'iron-security' ) : __( 'Admin limitation has been disabled', 'iron-security' ),
				'enabled' => $enabled
			);

			if ( $enabled ) {
				$admin_users = get_users( array( 'role' => 'administrator', ) );
				$admin_count = count( $admin_users );
				$users       = array();

				foreach ( $admin_users as $user ) {
					$users[] = array(
						'ID'           => $user->ID,
						'user_login'   => $user->user_login,
						'display_name' => $user->display_name,
						'user_email'   => $user->user_email,
					);
				}

				$response_data['current_admin_count'] = $admin_count;
				$response_data['admin_users']         = $users;
			}

			wp_send_json_success( $response_data );
		} else {
			$current_options = $options;
			if ( isset( $current_options['enable_limit_admins'] ) && $current_options['enable_limit_admins'] === ( $enabled ? '1' : '0' ) ) {
				$response_data = array(
					'message' => __( 'Admin limitation setting is already set to this value', 'iron-security' ),
					'enabled' => $enabled
				);

				if ( $enabled ) {
					$admin_users = get_users( array( 'role' => 'administrator' ) );
					$admin_count = count( $admin_users );
					$users       = array();

					foreach ( $admin_users as $user ) {
						$users[] = array(
							'ID'           => $user->ID,
							'user_login'   => $user->user_login,
							'display_name' => $user->display_name,
							'user_email'   => $user->user_email,
						);
					}

					$response_data['current_admin_count'] = $admin_count;
					$response_data['admin_users']         = $users;
				}

				wp_send_json_success( $response_data );
			} else {
				wp_send_json_error( __( 'Failed to update admin limitation setting', 'iron-security' ) );
			}
		}
	}

	public function LimitsForAdminAccountsSave( $maxAdmins, $fallbackRole ) {
		if ( $maxAdmins < 1 ) {
			$maxAdmins = 1;
		}

		$valid_roles = array( 'editor', 'author', 'contributor', 'subscriber' );

		if ( ! in_array( $fallbackRole, $valid_roles ) ) {
			$fallbackRole = 'subscriber';
		}

		$options                                 = $this->helper->wpironis_getOption( self::OPTION_NAME );
		$options['wpironis_max_admins']          = (string) $maxAdmins;
		$options['wpironis_admin_fallback_role'] = $fallbackRole;
		$updated                                 = $this->helper->wpironis_updateOption( self::OPTION_NAME, $options );

		$admin_users = get_users( array( 'role' => 'administrator', ) );
		$admin_count = count( $admin_users );

		$users = [];
		foreach ( $admin_users as $user ) {
			$users[] = array(
				'ID'           => $user->ID,
				'user_login'   => $user->user_login,
				'display_name' => $user->display_name,
				'user_email'   => $user->user_email,
			);
		}

		if ( $updated ) {
			$this->wpironis_enforce_admin_limit_now();

			$updated_admin_users = get_users( array( 'role' => 'administrator' ) );
			$updated_admin_count = count( $updated_admin_users );

			$updated_users = array();
			foreach ( $updated_admin_users as $user ) {
				$updated_users[] = array(
					'ID'           => $user->ID,
					'user_login'   => $user->user_login,
					'display_name' => $user->display_name,
					'user_email'   => $user->user_email,
				);
			}

			wp_send_json_success( array(
				'message'             => __( 'Admin limitation settings have been saved', 'iron-security' ),
				'max_admins'          => $maxAdmins,
				'fallback_role'       => $fallbackRole,
				'current_admin_count' => $updated_admin_count,
				'admin_users'         => $updated_users,
				'admins_changed'      => ( $admin_count !== $updated_admin_count )
			) );
		} else {
			$current_options = $this->helper->wpironis_getOption( self::OPTION_NAME );
			if (
				isset( $current_options['wpironis_max_admins'] ) &&
				$current_options['wpironis_max_admins'] === (string) $maxAdmins &&
				isset( $current_options['wpironis_admin_fallback_role'] ) &&
				$current_options['wpironis_admin_fallback_role'] === $fallbackRole
			) {
				wp_send_json_success( array(
					'message'             => __( 'Admin limitation settings are already set to these values',
						'iron-security' ),
					'max_admins'          => $maxAdmins,
					'fallback_role'       => $fallbackRole,
					'current_admin_count' => $admin_count,
					'admin_users'         => $users
				) );
			} else {
				wp_send_json_error( __( 'Failed to save admin limitation settings', 'iron-security' ) );
			}
		}
	}

	private function wpironis_enforce_admin_limit_now() {
		$options = $this->helper->wpironis_getOption( self::OPTION_NAME );

		if ( ! isset( $options['enable_limit_admins'] ) || $options['enable_limit_admins'] !== '1' ) {
			return;
		}

		$max_admins    = isset( $options['wpironis_max_admins'] ) ? absint( $options['wpironis_max_admins'] ) : 1;
		$fallback_role = isset( $options['wpironis_admin_fallback_role'] ) ? $options['wpironis_admin_fallback_role'] : 'subscriber';
		$admin_users   = get_users( array(
			'role'    => 'administrator',
			'orderby' => 'ID',
			'order'   => 'ASC'
		) );

		if ( count( $admin_users ) <= $max_admins ) {
			return;
		}

		$admin_count = 0;
		foreach ( $admin_users as $admin_user ) {
			$admin_count ++;

			if ( $admin_count <= $max_admins ) {
				continue;
			}

			$admin_user->set_role( $fallback_role );
		}
	}

	public function checkSessionTimeout() {
		$options = $this->helper->wpironis_getOption( self::OPTION_NAME );
		if ( empty( $options['enable_session_timeout'] ) || $options['enable_session_timeout'] !== '1' ) {
			return;
		}

		$timeout       = isset( $options['session_timeout_value'] ) ? intval( $options['session_timeout_value'] ) : 3600;
		$last_activity = get_user_meta( get_current_user_id(), self::SESSION_LAST_ACTIVITY, true );

		if ( empty( $last_activity ) ) {
			update_user_meta( get_current_user_id(), self::SESSION_LAST_ACTIVITY, time() );

			return;
		}

		update_user_meta( get_current_user_id(), self::SESSION_LAST_ACTIVITY, time() );
	}

	public function clearActivity() {
		$options = $this->helper->wpironis_getOption( self::OPTION_NAME_ACTIVITY );
		$user_id = get_current_user_id();

		if ( isset( $options[ $user_id ] ) ) {
			unset( $options[ $user_id ] );
			update_option( 'iron_security_last_activity', $options );
		}
	}

	public function clearUserActivity() {
		update_user_meta( get_current_user_id(), self::OPTION_NAME_ACTIVITY, null );
	}

	public function enforceAdminLimits( $userId ) {
		if ( ! isset( $options['enable_limit_admins'] ) || $options['enable_limit_admins'] !== '1' ) {
			return;
		}

		$options       = $this->helper->wpironis_getOption( self::OPTION_NAME );
		$max_admins    = isset( $options['wpironis_max_admins'] ) ? absint( $options['wpironis_max_admins'] ) : 1;
		$fallback_role = isset( $options['wpironis_admin_fallback_role'] ) ? $options['wpironis_admin_fallback_role'] : 'subscriber';
		$admin_count   = $this->wpironis_count_admins();

		if ( $admin_count > $max_admins ) {
			remove_action( 'set_user_role', [ $this, 'wpironis_enforce_admin_limit' ], 10 );

			$user = new WP_User( $userId );
			$user->set_role( $fallback_role );

			add_action( 'admin_notices', function () use ( $user, $fallback_role, $max_admins ) {
				echo '<div class="notice notice-warning is-dismissible">';
				echo '<p>' . sprintf(
						'User "%s" could not be made an administrator because the maximum limit of %d administrators has been reached. ' .
						'The user has been assigned the %s role instead.',
						esc_html( $user->user_login ),
						esc_html( $max_admins ),
						esc_html( $fallback_role )
					) . '</p>';
				echo '</div>';
			} );

			add_action( 'set_user_role', [ $this, 'wpironis_enforce_admin_limit' ], 10, 3 );
		}
	}

	private function wpironis_count_admins() {
		$users_query = new WP_User_Query( array(
			'role'   => 'administrator',
			'fields' => 'ID',
		) );

		return $users_query->get_total();
	}

	public function checkAdminLimitOnRegister( $user_id ) {
		$user_id = absint( $user_id );
		$user    = new WP_User( $user_id );

		if ( ! in_array( 'administrator', $user->roles ) ) {
			return;
		}

		$options = $this->helper->wpironis_getOption( self::OPTION_NAME );
		if ( ! isset( $options['enable_limit_admins'] ) || $options['enable_limit_admins'] !== '1' ) {
			return;
		}
		$max_admins    = isset( $options['wpironis_max_admins'] ) ? absint( $options['wpironis_max_admins'] ) : 1;
		$fallback_role = isset( $options['wpironis_admin_fallback_role'] ) ? $options['wpironis_admin_fallback_role'] : 'editor';
		$admin_count   = $this->wpironis_count_admins();

		if ( $admin_count > $max_admins ) {
			$user->set_role( $fallback_role );
		}
	}

	public function filterRestUserresponse( $response, $handler, $request ) {
		if ( is_wp_error( $response ) ||
		     ( is_array( $response ) && ! is_object( $response ) ) ||
		     ( is_object( $response ) && method_exists( $response,
				     'get_status' ) && $response->get_status() >= 400 ) ) {
			return $response;
		}

		$route = $request->get_route();
		if ( ! str_contains( $route, '/wp/v2/users' ) ) {
			return $response;
		}

		$params = $request->get_params();
		if ( ! isset( $params['roles'] ) || ! in_array( 'administrator', (array) $params['roles'] ) ) {
			return $response;
		}

		$options = $this->helper->wpironis_getOption( self::OPTION_NAME );
		if ( ! isset( $options['enable_limit_admins'] ) || $options['enable_limit_admins'] !== '1' ) {
			return $response;
		}

		$max_admins    = isset( $options['wpironis_max_admins'] ) ? absint( $options['wpironis_max_admins'] ) : 1;
		$fallback_role = isset( $options['wpironis_admin_fallback_role'] ) ? $options['wpironis_admin_fallback_role'] : 'editor';
		$admin_count   = $this->wpironis_count_admins();
		$user_id       = isset( $params['id'] ) ? $params['id'] : null;

		if ( ! $user_id && isset( $response->data['id'] ) ) {
			$user_id = $response->data['id'];
		}

		$user      = get_user_by( 'id', $user_id );
		$was_admin = $user && in_array( 'administrator', $user->roles );

		if ( ! $was_admin && $admin_count >= $max_admins ) {
			$user = new WP_User( $user_id );
			$user->set_role( $fallback_role );

			if ( isset( $response->data['roles'] ) ) {
				$response->data['roles'] = array( $fallback_role );
			}

			if ( ! isset( $response->data['iron_security_message'] ) ) {
				$response->data['iron_security_message'] = sprintf(
					'User could not be made an administrator because the maximum limit of %d has been reached. The user has been assigned the %s role instead.',
					$max_admins,
					$fallback_role
				);
			}
		}

		return $response;
	}

	public function getAdminInfo() {
		$admin_users = get_users( array(
			'role' => 'administrator',
		) );

		$users = array();
		foreach ( $admin_users as $user ) {
			$users[] = array(
				'ID'           => $user->ID,
				'user_login'   => $user->user_login,
				'display_name' => $user->display_name,
				'user_email'   => $user->user_email,
			);
		}

		$count = count( $admin_users );
		wp_send_json_success( array(
			'count'   => $count,
			'users'   => $users,
			'message' => __( 'Admin information retrieved successfully.', 'iron-security' )
		) );
		wp_die();
	}
}