<?php

declare(strict_types=1);

if (
	! defined( 'WP_UNINSTALL_PLUGIN' )
	|| WP_UNINSTALL_PLUGIN !== 'sitefort/sitefort.php'
) {
	exit;
}

$sitefort_installer_file = __DIR__ . '/core/Installer.php';
if ( file_exists( $sitefort_installer_file ) ) {
	require_once $sitefort_installer_file;
}

$sitefort_autoload_file = __DIR__ . '/vendor/autoload.php';
if ( file_exists( $sitefort_autoload_file ) ) {
	require_once $sitefort_autoload_file;
}

if ( ! function_exists( 'sitefort_log' ) ) {
	/**
	 * Shim: uninstall.php runs without the main plugin file, but the classes
	 * used for Cloudflare cleanup call sitefort_log() on failure.
	 *
	 * @param mixed $message Ignored.
	 */
	function sitefort_log( $message ): void {
		unset( $message );
	}
}

/**
 * Remove SiteFort-managed Cloudflare enforcement (rule, access rules, lists).
 */
function sitefort_cleanup_cloudflare_edge(): void {
	if ( ! class_exists( '\SiteFort\Modules\Cloudflare\Manager' ) ) {
		return;
	}

	try {
		( new \SiteFort\Modules\Cloudflare\Manager() )->clear_edge_enforcement( true );
	} catch ( \Throwable $e ) {
		// Never fatal an uninstall on remote cleanup.
	}
}

/**
 * Delete all options in the current blog matching the provided prefixes.
 *
 * @param array<int,string> $prefixes Option prefixes.
 */
function sitefort_delete_blog_options_by_prefixes( array $prefixes ): void {
	global $wpdb;

	foreach ( $prefixes as $prefix ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Removes this plugin's option rows during an explicit uninstall cleanup.
		$wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
				$wpdb->esc_like( $prefix ) . '%'
			)
		);
	}
}

/**
 * Drop all SiteFort tables for the current blog.
 *
 * @param array<int,string> $tables Table suffixes without $wpdb->prefix.
 */
function sitefort_drop_blog_tables( array $tables ): void {
	global $wpdb;

	foreach ( $tables as $table ) {
		$full_name = $wpdb->prefix . $table;
		if ( $full_name === '' || strlen( $full_name ) > 64 || ! preg_match( '/^[A-Za-z0-9_]+$/', $full_name ) ) {
			continue;
		}

		$table_identifier = '`' . str_replace( '`', '``', $full_name ) . '`';
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Drops validated plugin-owned tables during uninstall after explicit capability and uninstall checks.
		$wpdb->query( "DROP TABLE IF EXISTS {$table_identifier}" );
	}
}

/**
 * Remove all scheduled actions and cron events for a hook, including variant args.
 */
function sitefort_clear_scheduled_hook_everywhere( string $hook ): void {
	wp_clear_scheduled_hook( $hook );

	if ( function_exists( '_get_cron_array' ) ) {
		$crons = _get_cron_array();
		if ( is_array( $crons ) ) {
			foreach ( $crons as $timestamp => $cron_hooks ) {
				if ( ! isset( $cron_hooks[ $hook ] ) || ! is_array( $cron_hooks[ $hook ] ) ) {
					continue;
				}

				foreach ( $cron_hooks[ $hook ] as $event ) {
					$args = [];
					if ( is_array( $event ) && isset( $event['args'] ) && is_array( $event['args'] ) ) {
						$args = $event['args'];
					}

					wp_unschedule_event( (int) $timestamp, $hook, $args );
				}
			}
		}
	}

	if ( function_exists( 'as_unschedule_all_actions' ) ) {
		as_unschedule_all_actions( $hook );
	}
}

/**
 * Recursively remove a directory and all nested files.
 */
function sitefort_delete_directory_recursive( string $path ): void {
	if ( ! is_dir( $path ) ) {
		return;
	}

	$entries = scandir( $path );
	if ( ! is_array( $entries ) ) {
		return;
	}

	foreach ( $entries as $entry ) {
		if ( $entry === '.' || $entry === '..' ) {
			continue;
		}

		$target = $path . '/' . $entry;
		if ( is_dir( $target ) ) {
			sitefort_delete_directory_recursive( $target );
			continue;
		}

		if ( file_exists( $target ) ) {
			wp_delete_file( $target );
		}
	}

	sitefort_remove_empty_directory( $path );
}

/**
 * Recursively delete a directory's contents while preserving specific filenames.
 *
 * @param string            $path             Directory to clean.
 * @param array<int,string> $preserve_entries Basename entries to keep.
 */
function sitefort_delete_directory_contents_except( string $path, array $preserve_entries ): void {
	if ( ! is_dir( $path ) ) {
		return;
	}

	$entries = scandir( $path );
	if ( ! is_array( $entries ) ) {
		return;
	}

	foreach ( $entries as $entry ) {
		if ( $entry === '.' || $entry === '..' || in_array( $entry, $preserve_entries, true ) ) {
			continue;
		}

		$target = $path . '/' . $entry;
		if ( is_dir( $target ) ) {
			sitefort_delete_directory_recursive( $target );
			continue;
		}

		if ( file_exists( $target ) ) {
			wp_delete_file( $target );
		}
	}
}

/**
 * Remove a SiteFort-managed config block from a root config file.
 */
function sitefort_cleanup_root_config_file( string $path, string $begin_marker, string $end_marker, string $managed_directive_pattern, ?callable $postprocess = null ): void {
	if ( ! is_file( $path ) || ! wp_is_writable( $path ) ) {
		return;
	}

	$contents = @file_get_contents( $path );
	if ( $contents === false ) {
		return;
	}

	$pattern = '/' . preg_quote( $begin_marker, '/' ) . '.*?' . preg_quote( $end_marker, '/' ) . '\s*/s';
	$cleaned = preg_replace( $pattern, '', $contents );
	$cleaned = is_string( $cleaned ) ? $cleaned : $contents;

	$directive_cleaned = preg_replace( $managed_directive_pattern, '', $cleaned );
	if ( is_string( $directive_cleaned ) ) {
		$cleaned = $directive_cleaned;
	}

	if ( null !== $postprocess ) {
		$postprocessed = $postprocess( $cleaned );
		if ( is_string( $postprocessed ) ) {
			$cleaned = $postprocessed;
		}
	}

	if ( $cleaned === $contents ) {
		return;
	}

	$cleaned = trim( $cleaned );
	if ( $cleaned === '' ) {
		wp_delete_file( $path );
		if ( file_exists( $path ) ) {
			sitefort_write_existing_file_in_place( $path, '' );
		}
		return;
	}

	sitefort_write_existing_file_atomically( $path, $cleaned . PHP_EOL, true );
}

/**
 * Restore foreign auto_prepend_file directives that SiteFort disabled during install.
 */
function sitefort_restore_disabled_user_ini_prepend_directives( string $contents ): string {
	$restored = preg_replace(
		'/^([ \t]*); SITEFORT-DISABLED (auto_prepend_file[ \t]*=.*)$/mi',
		'$1$2',
		$contents
	);
	if ( is_string( $restored ) ) {
		$contents = $restored;
	}

	return $contents;
}

function sitefort_get_home_path(): string {
	$server_software = sitefort_get_server_value( 'SERVER_SOFTWARE' );
	$document_root   = sitefort_get_server_value( 'DOCUMENT_ROOT' );

	if ( strpos( $server_software, 'Flywheel/' ) === 0 && $document_root !== '' && false === strpos( $document_root, "\0" ) ) {
		return trailingslashit( wp_normalize_path( $document_root ) );
	}

	if ( ! function_exists( 'get_home_path' ) && defined( 'ABSPATH' ) ) {
		require_once ABSPATH . 'wp-admin/includes/file.php';
	}

	if ( function_exists( 'get_home_path' ) ) {
		$home_path = get_home_path();
		if ( is_string( $home_path ) && trim( $home_path ) !== '' ) {
			return trailingslashit( wp_normalize_path( $home_path ) );
		}
	}

	return trailingslashit( wp_normalize_path( ABSPATH ) );
}

function sitefort_get_user_ini_paths(): array {
	$paths    = [];
	$filename = ini_get( 'user_ini.filename' );
	$home     = sitefort_get_home_path();

	if ( is_string( $filename ) ) {
		$filename = trim( $filename );
		if ( $filename !== '' && preg_match( '/^(?!\.{1,2}$)[A-Za-z0-9._-]+$/', $filename ) === 1 ) {
			$paths[] = $home . $filename;
		}
	}

	return array_values( array_unique( $paths ) );
}

function sitefort_get_htaccess_paths(): array {
	return [ sitefort_get_home_path() . '.htaccess' ];
}

function sitefort_get_wp_config_paths(): array {
	$candidates = [];
	$primary    = ABSPATH . 'wp-config.php';
	if ( file_exists( $primary ) ) {
		$candidates[] = $primary;
	}

	$parent = dirname( ABSPATH ) . '/wp-config.php';
	if ( ! file_exists( $primary ) && file_exists( $parent ) && ! file_exists( dirname( ABSPATH ) . '/wp-settings.php' ) ) {
		$candidates[] = $parent;
	}

	return array_values( array_unique( $candidates ) );
}

function sitefort_apply_file_mode( string $path, int $mode ): void {
	$filesystem = sitefort_get_direct_filesystem();
	if ( is_object( $filesystem ) && method_exists( $filesystem, 'chmod' ) ) {
		$filesystem->chmod( $path, $mode );
	}
}

function sitefort_rename_path_atomically( string $source, string $destination ): bool {
	// phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename -- Atomic local replacement is required here; WP_Filesystem_Direct::move() deletes the destination before moving.
	return @rename( $source, $destination );
}

function sitefort_write_existing_file_in_place( string $path, string $contents ): bool {
	if ( ! is_file( $path ) || ! wp_is_writable( $path ) ) {
		return false;
	}

	$original = @file_get_contents( $path );
	if ( ! is_string( $original ) ) {
		return false;
	}

	$bytes = @file_put_contents( $path, $contents, LOCK_EX );
	if ( ! is_int( $bytes ) || $bytes !== strlen( $contents ) ) {
		@file_put_contents( $path, $original, LOCK_EX );
		return false;
	}

	if ( @file_get_contents( $path ) !== $contents ) {
		@file_put_contents( $path, $original, LOCK_EX );
		return false;
	}

	return true;
}

function sitefort_write_existing_file_atomically( string $path, string $contents, bool $allow_in_place_fallback = false ): bool {
	$dir = dirname( $path );
	if ( ! is_dir( $dir ) || ! wp_is_writable( $dir ) ) {
		if ( $allow_in_place_fallback && is_dir( $dir ) ) {
			return sitefort_write_existing_file_in_place( $path, $contents );
		}

		return false;
	}

	$suffix = function_exists( 'wp_generate_password' )
		? preg_replace( '/[^A-Za-z0-9]/', '', wp_generate_password( 12, false, false ) )
		: str_replace( '.', '', uniqid( '', true ) );
	$backup = $path . '.sitefort-uninstall-backup-' . $suffix;
	if ( ! @copy( $path, $backup ) ) {
		return false;
	}

	$temp = tempnam( $dir, '.sitefort-uninstall-' );
	if ( ! is_string( $temp ) || $temp === '' ) {
		wp_delete_file( $backup );
		return false;
	}

	$perms = @fileperms( $path );
	if ( is_int( $perms ) ) {
		sitefort_apply_file_mode( $temp, $perms & 0777 );
	}

	$bytes = @file_put_contents( $temp, $contents, LOCK_EX );
	if ( ! is_int( $bytes ) || $bytes !== strlen( $contents ) ) {
		wp_delete_file( $temp );
		wp_delete_file( $backup );
		return false;
	}

	if ( ! sitefort_rename_path_atomically( $temp, $path ) ) {
		wp_delete_file( $temp );
		wp_delete_file( $backup );
		return false;
	}

	if ( is_int( $perms ) ) {
		sitefort_apply_file_mode( $path, $perms & 0777 );
	}

	if ( @file_get_contents( $path ) !== $contents ) {
		sitefort_rename_path_atomically( $backup, $path );
		return false;
	}

	wp_delete_file( $backup );
	return true;
}

/**
 * Remove SiteFort-managed wp-config.php hardening rules.
 */
function sitefort_remove_wp_config_hardening_rules(): void {
	foreach ( sitefort_get_wp_config_paths() as $path ) {
		if ( ! is_file( $path ) || ! is_readable( $path ) ) {
			continue;
		}

		$contents = @file_get_contents( $path );
		if ( ! is_string( $contents ) || $contents === '' ) {
			continue;
		}

	$marker_pairs = [
		[ '// BEGIN SiteFort - Do not modify or remove this line', '// END SiteFort - Do not modify or remove this line' ],
	];
		$patterns     = [];
		foreach ( $marker_pairs as $pair ) {
			$patterns[] = preg_quote( $pair[0], '/' ) . '.*?' . preg_quote( $pair[1], '/' );
		}

		$cleaned = preg_replace( '/(?:' . implode( '|', $patterns ) . ')\s*/s', '', $contents );
		if ( ! is_string( $cleaned ) || $cleaned === $contents ) {
			continue;
		}

		$cleaned = preg_replace( "/\n{3,}/", "\n\n", $cleaned );
		$cleaned = is_string( $cleaned ) ? rtrim( $cleaned ) . PHP_EOL : rtrim( $contents ) . PHP_EOL;
		sitefort_write_existing_file_atomically( $path, $cleaned );
	}
}

/**
 * Remove SiteFort-managed auto_prepend_file directives.
 */
function sitefort_remove_extended_waf_directives(): void {
	$markers = [
		[ 'SiteFort', 'sitefort-data' ],
	];

	foreach ( sitefort_get_user_ini_paths() as $user_ini_path ) {
		foreach ( $markers as $marker ) {
			sitefort_cleanup_root_config_file(
				$user_ini_path,
				'; BEGIN ' . $marker[0] . ' WAF',
				'; END ' . $marker[0] . ' WAF',
				'/^\s*auto_prepend_file\s*=.*' . preg_quote( $marker[1], '/' ) . '\/bootstrap\.php.*(?:\R)?/mi',
				'sitefort_restore_disabled_user_ini_prepend_directives'
			);
		}
	}

	foreach ( sitefort_get_htaccess_paths() as $htaccess_path ) {
		foreach ( $markers as $marker ) {
			sitefort_cleanup_root_config_file(
				$htaccess_path,
				'# BEGIN ' . $marker[0] . ' WAF',
				'# END ' . $marker[0] . ' WAF',
				'/^\s*php_value\s+auto_prepend_file\s+["\']?.*' . preg_quote( $marker[1], '/' ) . '\/bootstrap\.php["\']?.*(?:\R)?/mi'
			);
		}
	}
}

/**
 * Read a scalar server value using the standard unslash + sanitize flow.
 */
function sitefort_get_server_value( string $key ): string {
	if ( ! isset( $_SERVER[ $key ] ) || is_array( $_SERVER[ $key ] ) ) {
		return '';
	}

	$value = sanitize_text_field( wp_unslash( (string) $_SERVER[ $key ] ) );
	$value = is_string( $value ) ? trim( $value ) : '';

	if ( $value === '' || strpos( $value, "\0" ) !== false ) {
		return '';
	}

	return $value;
}

/**
 * Leave a no-op bootstrap in place until cached auto_prepend_file values expire.
 */
function sitefort_write_safe_waf_bootstrap_stub( string $data_dir ): void {
	if ( ! is_dir( $data_dir ) && ! wp_mkdir_p( $data_dir ) ) {
		return;
	}

	$bootstrap = $data_dir . '/bootstrap.php';
	$index     = $data_dir . '/index.php';
	$htaccess  = $data_dir . '/.htaccess';

	$stub  = "<?php\n";
	$stub .= "declare(strict_types=1);\n";
	$stub .= "// Left in place after SiteFort uninstall to avoid fatal errors while PHP-FPM refreshes cached auto_prepend_file values.\n";
	$stub .= "return;\n";

	@file_put_contents( $bootstrap, $stub, LOCK_EX );

	if ( ! is_file( $index ) ) {
		@file_put_contents( $index, "<?php\n// Silence is golden.\n", LOCK_EX );
	}

	if ( ! is_file( $htaccess ) ) {
		$server_software = strtolower( sitefort_get_server_value( 'SERVER_SOFTWARE' ) );
		$is_nginx        = false !== strpos( $server_software, 'nginx' );
		if ( ! $is_nginx ) {
			@file_put_contents(
				$htaccess,
				"<IfModule mod_authz_core.c>\nRequire all denied\n</IfModule>\n<IfModule mod_litespeed.c>\nOrder Deny,Allow\nDeny from all\n</IfModule>\n<IfModule !mod_authz_core.c>\n  <IfModule !mod_litespeed.c>\n    Order Deny,Allow\n    Deny from all\n  </IfModule>\n</IfModule>\n",
				LOCK_EX
			);
		}
	}
}

/**
 * Remove server-level WAF directives while keeping the bootstrap stub in place.
 */
function sitefort_cleanup_extended_waf_artifacts( string $data_dir ): void {
	sitefort_remove_extended_waf_directives();
	sitefort_remove_early_loader_mu_plugin();
	sitefort_write_safe_waf_bootstrap_stub( $data_dir );
	sitefort_delete_directory_contents_except( $data_dir, [ 'bootstrap.php', 'index.php', '.htaccess' ] );
}

/**
 * Remove the SiteFort Early Loader must-use plugin if it is present.
 *
 * Refuses to delete a file that lacks the SiteFort version marker so an
 * unrelated mu-plugin sharing the filename is never touched.
 */
function sitefort_remove_early_loader_mu_plugin(): void {
	$dir = defined( 'WPMU_PLUGIN_DIR' )
		? (string) WPMU_PLUGIN_DIR
		: ( ( defined( 'WP_CONTENT_DIR' ) ? WP_CONTENT_DIR : ABSPATH . 'wp-content' ) . '/mu-plugins' );
	$loaders = [
		[ $dir . '/00-sitefort-early-loader.php', 'SITEFORT_EARLY_LOADER_VERSION' ],
	];

	foreach ( $loaders as $loader ) {
		[ $path, $marker ] = $loader;
		if ( ! is_file( $path ) ) {
			continue;
		}

		$contents = @file_get_contents( $path );
		if ( ! is_string( $contents ) || strpos( $contents, $marker ) === false ) {
			continue;
		}

		if ( function_exists( 'wp_delete_file' ) ) {
			wp_delete_file( $path );
		}
	}
}

/**
 * Remove an empty directory using the WordPress filesystem layer.
 */
function sitefort_remove_empty_directory( string $path ): void {
	if ( ! function_exists( 'WP_Filesystem' ) ) {
		require_once ABSPATH . 'wp-admin/includes/file.php';
	}

	if ( ! WP_Filesystem() ) {
		$filesystem = sitefort_get_direct_filesystem();
		if ( $filesystem instanceof \WP_Filesystem_Base && method_exists( $filesystem, 'rmdir' ) ) {
			$filesystem->rmdir( $path, false );
		}
		return;
	}

	global $wp_filesystem;

	if ( is_object( $wp_filesystem ) && method_exists( $wp_filesystem, 'rmdir' ) ) {
		$wp_filesystem->rmdir( $path, false );
	}
}

/**
 * Create a direct filesystem adapter when transport bootstrap is unavailable.
 */
function sitefort_get_direct_filesystem(): ?\WP_Filesystem_Base {
	if ( ! class_exists( '\WP_Filesystem_Direct', false ) ) {
		require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
		require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
	}

	$filesystem = class_exists( '\WP_Filesystem_Direct', false ) ? new \WP_Filesystem_Direct( null ) : null;

	return $filesystem instanceof \WP_Filesystem_Base ? $filesystem : null;
}

/**
 * Clean up all blog-scoped SiteFort storage for the current blog.
 *
 * @param array<int,string> $option_prefixes Option prefixes in wp_options.
 * @param array<int,string> $tables          Table suffixes without $wpdb->prefix.
 */
function sitefort_cleanup_current_blog( array $option_prefixes, array $tables ): void {
	// Needs the stored token, so it must run before the option wipe below.
	sitefort_cleanup_cloudflare_edge();
	delete_option( 'sitefort_adaptive_batch_size' );
	delete_option( 'sitefort_safe_network_ranges_status' );
	sitefort_delete_blog_options_by_prefixes( $option_prefixes );
	sitefort_drop_blog_tables( $tables );
}

$sitefort_blog_option_prefixes = [
	'sitefort_',
	'_transient_sitefort_',
	'_transient_timeout_sitefort_',
];

$sitefort_network_option_prefixes = [
	'sitefort_',
	'_site_transient_sitefort_',
	'_site_transient_timeout_sitefort_',
];

$sitefort_table_suffixes = [
	'scan_queue',
	'task_queue',
	'commands',
	'audit_log',
	'file_states',
	'cloud_jobs',
	'file_index',
	'hash_cache',
	'discovery_state',
	'discovery_stack',
	'issues',
	'issue_instances',
	'ignored_issues',
	'quarantine_items',
	'scan_runs',
	'scan_status',
	'settings',
	'request_nonces',
	'two_factor',
	'two_factor_recovery_codes',
	'two_factor_sessions',
	'login_attempts',
	'rate_limits',
	'blocklist',
	'blocklist_ranges',
	'waf_runtime',
	'firewall_events',
];

$sitefort_tables = [];
foreach ( $sitefort_table_suffixes as $sitefort_table_suffix ) {
	$sitefort_tables[] = 'sitefort_' . $sitefort_table_suffix;
}

$sitefort_cron_hooks = class_exists( '\SiteFort\Core\Installer' )
	? \SiteFort\Core\Installer::scheduled_hooks()
	: [
		'sitefort_daily_scan_cron',
		'sitefort_weekly_report_cron',
		'sitefort_telemetry_heartbeat',
		'sitefort_daily_event',
		'sitefort_entitlement_refresh',
		'sitefort_state_push',
		'sitefort_keepalive',
		'sitefort_state_dispatch',
		'sitefort_state_dispatch_low',
		'sitefort_state_notification_dispatch',
		'sitefort_state_audit_dispatch',
		'sitefort_scheduled_scan_job',
		'sitefort_scan_cron_check',
		'sitefort_scan_worker_fallback',
		'sitefort_scanner_watchdog',
		'sitefort_blocklist_sync',
		'sitefort_geoip_update',
		'sitefort_safe_network_ranges_update',
		'sitefort_verified_bot_ranges_update',
		'sitefort_login_attempt_cleanup',
		'sitefort_rate_limit_cleanup',
		'sitefort_firewall_lockout_cleanup',
		'sitefort_audit_log_cleanup',
		'sitefort_db_migration_runner',
		'sitefort_quarantine_purge',
		'sitefort_firewall_log_cleanup',
		'sitefort_firewall_waf_log_ingest',
		'sitefort_email_digest',
		'sitefort_fw_block_digest_send',
		'sitefort_webhook_retry',
		'sitefort_metrics_push',
		'sitefort_cf_reconcile',
		'sitefort_vulnerability_refresh',
		'sitefort_command_execute',
		'sitefort_command_queue_maintenance',
		'sitefort_server_rule_reconcile',
		'sitefort_server_rule_maintenance',
		'sitefort_server_rule_verify',
		'sitefort_user_nicename_remediation',
	];

sitefort_cleanup_current_blog( $sitefort_blog_option_prefixes, $sitefort_tables );

if ( is_multisite() ) {
	global $wpdb;

	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Enumerates multisite blogs once during uninstall cleanup.
	$sitefort_blog_ids = $wpdb->get_col( "SELECT blog_id FROM {$wpdb->blogs}" );
	if ( is_array( $sitefort_blog_ids ) ) {
		foreach ( $sitefort_blog_ids as $sitefort_blog_id ) {
			$blog_id = (int) $sitefort_blog_id;
			if ( $blog_id <= 0 || $blog_id === (int) get_current_blog_id() ) {
				continue;
			}

			switch_to_blog( $blog_id );
			sitefort_cleanup_current_blog( $sitefort_blog_option_prefixes, $sitefort_tables );
			restore_current_blog();
		}
	}

	foreach ( $sitefort_network_option_prefixes as $sitefort_prefix ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Removes this plugin's network option rows during multisite uninstall cleanup.
		$wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$wpdb->sitemeta} WHERE meta_key LIKE %s",
				$wpdb->esc_like( $sitefort_prefix ) . '%'
			)
		);
	}
}

global $wpdb;

foreach ( [ 'sitefort_' ] as $sitefort_usermeta_prefix ) {
	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Removes this plugin's usermeta rows during uninstall cleanup.
	$wpdb->query(
		$wpdb->prepare(
			"DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE %s",
			$wpdb->esc_like( $sitefort_usermeta_prefix ) . '%'
		)
	);
}

foreach ( $sitefort_cron_hooks as $sitefort_hook ) {
	if ( class_exists( '\SiteFort\Core\Installer' ) ) {
		\SiteFort\Core\Installer::clear_scheduled_hook_everywhere( $sitefort_hook );
	} else {
		sitefort_clear_scheduled_hook_everywhere( $sitefort_hook );
	}
}

sitefort_remove_wp_config_hardening_rules();
sitefort_cleanup_extended_waf_artifacts( WP_CONTENT_DIR . '/sitefort-data' );

$sitefort_upload_dir = wp_upload_dir();
if ( is_array( $sitefort_upload_dir ) && ! empty( $sitefort_upload_dir['basedir'] ) ) {
	sitefort_delete_directory_recursive( $sitefort_upload_dir['basedir'] . '/sitefort-logs' );
}
