<?php

declare(strict_types=1);

namespace ShieldX\Admin;

defined('ABSPATH') || exit;

use ShieldX\Core\Plugin;
use ShieldX\Core\AddonRegistry;
use ShieldX\Core\FileTypeSettings;
use ShieldX\Core\NotificationManager;
use ShieldX\Core\SchedulerBridge;
use ShieldX\Core\SettingsPortability;
use ShieldX\Core\SetupModeManager;
use ShieldX\Core\Uninstaller;
use ShieldX\Licensing\LicenseManager;
use ShieldX\Licensing\LicenseSettings;
use ShieldX\Maintenance\AdminChangeShadowScanner;
use ShieldX\Maintenance\BackupStorageManager;
use ShieldX\Maintenance\BackupStorageSettings;
use ShieldX\Maintenance\DatabaseBackupManager;
use ShieldX\Maintenance\FileShadowCopyManager;
use ShieldX\Maintenance\GoogleDriveSettings;
use ShieldX\Maintenance\LogRotationSettings;
use ShieldX\Maintenance\LogRotator;
use ShieldX\Maintenance\RestorePointManager;
use ShieldX\Maintenance\ScheduledBackupSettings;
use ShieldX\Maintenance\ScheduledRestorePointSettings;
use ShieldX\Security\Hardening;
use ShieldX\Security\HardeningSettings;
use ShieldX\Security\HumanChallengeSettings;
use ShieldX\Security\IncidentTimeline;
use ShieldX\Security\LockdownSettings;
use ShieldX\Security\LoginSecurity;
use ShieldX\Security\LoginSecuritySettings;
use ShieldX\Security\ClientIpResolver;
use ShieldX\Security\MinimalWaf;
use ShieldX\Security\QrCode;
use ShieldX\Security\RateLimitSettings;
use ShieldX\Security\ReputationFeed;
use ShieldX\Security\UserProfiler;
use ShieldX\Security\WafSettings;
use ShieldX\Security\WafStats;
use ShieldX\Scanner\CoreFileVerifier;
use ShieldX\Scanner\DatabaseRemediation;
use ShieldX\Scanner\LocalScanner;
use ShieldX\Scanner\PluginFileVerifier;
use ShieldX\Scanner\ScanRuleEngine;
use ShieldX\Scanner\ScanRepository;
use ShieldX\Scanner\ScannerRemediation;
use ShieldX\Scanner\ScheduledScanSettings;
use ShieldX\Scanner\VulnerabilityScanner;
use ShieldX\Storage\FileStorage;
use ShieldX\Support\AdminFlashNotice;
use ShieldX\Support\CryptoBox;
use ShieldX\Support\WordPressPaths;

final class AdminPage
{
    private const FILE_REVIEW_PREVIEW_BYTES = 524288;
    private const FILE_REVIEW_PREVIEW_LINES = 500;
    private const FILE_REVIEW_LINE_BYTES = 4096;
    private const EDITABLE_PLUGIN_COMPARE_MAX_BYTES = 262144;
    private const LOG_PREVIEW_BYTES = 262144;
    private const WAF_ACCESS_DISMISSED_EVENTS_OPTION = 'shieldx_waf_access_dismissed_events';
    private const WAF_ACCESS_DISMISSED_EVENTS_LIMIT = 500;
    private const OVERVIEW_DISABLED_CARDS_META = 'shieldx_overview_disabled_cards';
    private const OVERVIEW_DISABLED_CARDS_INIT_META = 'shieldx_overview_disabled_cards_initialized';
    private const OVERVIEW_DISABLED_CARDS_INIT_VERSION = '2';
    private const OVERVIEW_DEFAULT_DISABLED_CARD_IDS = [
        'active_attack_sources',
        'current_ip_safety',
        'server_ip_safety',
        'license_attention',
    ];
    private const OVERVIEW_CARD_IDS = [
        'needs_attention',
        'critical_security_issues',
        'firewall_action',
        'active_attack_sources',
        'access_control_review',
        'current_ip_safety',
        'server_ip_safety',
        'login_security_gaps',
        'two_factor_enrollment',
        'emergency_recovery',
        'backup_protection',
        'restore_point_missing',
        'backup_storage_warning',
        'scheduler_problems',
        'license_attention',
        'hardening_gaps',
        'lockdown_gaps',
    ];
    private const ALLOWED_AJAX_CONTEXT_KEYS = [
        'q',
        's',
        'p',
        'page_num',
        'tab',
        'scope',
        'status',
        'source',
        'severity',
        'type',
        'limit',
        'page_id',
        'scan_id',
        'shieldx_db_remediated',
        'shieldx_db_restored',
        'shieldx_db_review',
        'shieldx_db_rechecked',
        'shieldx_recheck_count',
        'shieldx_recheck_status',
        'shieldx_scan_action_error',
        'shieldx_core_compare',
        'shieldx_plugin_compare',
        'shieldx_shadow_compare',
        'shieldx_shadow_scan',
        'shieldx_review_file',
        'shieldx_review_file_token',
        'shieldx_vuln_review',
        'shieldx_protected_file',
        'shieldx_protected_file_token',
        'shieldx_core_compare_token',
        'job_id',
        'restore_id',
        'backup_id',
        'shieldx_restore_created',
        'shieldx_restore_view',
        'shieldx_restore_action_failed',
        'shieldx_restore_preview',
        'shieldx_restore_file_search',
        'shieldx_restore_file_page',
    ];
    private const TOAST_QUERY_KEYS = [
        'shieldx_saved',
        'shieldx_waf_error',
        'shieldx_waf_access_updated',
        'shieldx_2fa_enabled',
        'shieldx_2fa_disabled',
        'shieldx_admin_2fa_disabled',
        'shieldx_admin_2fa_error',
        'shieldx_2fa_reset_sent',
        'shieldx_2fa_reset_confirmed',
        'shieldx_codes_regenerated',
        'shieldx_codes_email_sent',
        'shieldx_codes_email_failed',
        'shieldx_email_sent',
        'shieldx_email_failed',
        'shieldx_2fa_invites_sent',
        'shieldx_2fa_invites_failed',
        'shieldx_2fa_invites_total',
        'shieldx_scanned',
        'shieldx_shadow_scanned',
        'shieldx_shadow_changes',
        'shieldx_schedule_saved',
        'shieldx_backup_schedule_saved',
        'shieldx_restore_point_schedule_saved',
        'shieldx_vuln_scanned',
        'shieldx_core_ready',
        'shieldx_plugin_packages_ready',
        'shieldx_plugin_packages_prepared',
        'shieldx_plugin_packages_errors',
        'shieldx_plugin_packages_error',
        'shieldx_plugin_package_uploaded',
        'shieldx_plugin_package_upload_error',
        'shieldx_plugin_package_trusted',
        'shieldx_plugin_package_trust_error',
        'shieldx_theme_package_uploaded',
        'shieldx_theme_package_upload_error',
        'shieldx_theme_package_trusted',
        'shieldx_theme_package_trust_error',
        'shieldx_core_restored',
        'shieldx_core_error',
        'shieldx_quarantined',
        'shieldx_quarantine_restored',
        'shieldx_ignored',
        'shieldx_ignore_removed',
        'shieldx_htaccess_repaired',
        'shieldx_file_trusted',
        'shieldx_file_rechecked',
        'shieldx_file_saved',
        'shieldx_config_cleaned',
        'shieldx_db_remediated',
        'shieldx_db_restored',
        'shieldx_db_rechecked',
        'shieldx_recheck_status',
        'shieldx_recheck_count',
        'shieldx_removed_lines',
        'shieldx_bulk_action',
        'shieldx_bulk_ok',
        'shieldx_bulk_failed',
        'shieldx_scan_action_error',
        'shieldx_lockdown_preview',
        'shieldx_lockdown_applied',
        'shieldx_lockdown_reverted',
        'shieldx_lockdown_force_reverted',
        'shieldx_lockdown_error',
        'shieldx_lockdown_warning',
        'shieldx_permission_fixed',
        'shieldx_permission_files',
        'shieldx_permission_dirs',
        'shieldx_permission_unchanged',
        'shieldx_permission_skipped',
        'shieldx_permission_failed',
        'shieldx_permission_error',
        'shieldx_hardening_error',
        'shieldx_recovery_rotated',
        'shieldx_imported',
        'shieldx_import_modules',
        'shieldx_import_error',
        'shieldx_file_types_saved',
        'shieldx_notifications_saved',
        'shieldx_license_activated',
        'shieldx_license_checked',
        'shieldx_license_deactivated',
        'shieldx_license_error',
        'shieldx_blocked',
        'shieldx_block_error',
        'shieldx_log_rotation_saved',
        'shieldx_uninstall_settings_saved',
        'shieldx_log_rotation_run',
        'shieldx_log_rotation_status',
        'shieldx_log_rotation_failed_files',
        'shieldx_log_rotation_deleted_files',
        'shieldx_log_rotation_deleted_rows',
        'shieldx_log_scan',
        'shieldx_log_scan_marked',
        'shieldx_log_scan_total',
        'shieldx_temp_cleanup_run',
        'shieldx_temp_cleanup_files',
        'shieldx_temp_cleanup_dirs',
        'shieldx_temp_cleanup_bytes',
        'shieldx_temp_cleanup_failed',
        'shieldx_temp_cleanup_truncated',
        'shieldx_restore_created',
        'shieldx_restore_restored',
        'shieldx_restore_protected_skipped',
        'shieldx_restore_action_failed',
        'shieldx_restore_error',
        'shieldx_restore_point_deleted',
        'shieldx_restore_points_cleaned',
        'shieldx_restore_points_deleted',
        'shieldx_restore_points_skipped',
        'shieldx_restore_points_skipped_active',
        'shieldx_restore_points_skipped_required',
        'shieldx_restore_point_cleanup_files',
        'shieldx_restore_point_cleanup_bytes',
        'shieldx_restore_point_cleanup_failed',
        'shieldx_restore_point_cleanup_before',
        'shieldx_db_backup_created',
        'shieldx_db_backup_restored',
        'shieldx_db_backups_cleaned',
        'shieldx_db_backups_deleted',
        'shieldx_db_backup_cleanup_files',
        'shieldx_db_backup_cleanup_bytes',
        'shieldx_db_backup_cleanup_failed',
        'shieldx_db_backup_cleanup_before',
        'shieldx_db_backup_cleanup_safety_backup',
        'shieldx_shadow_restored',
        'shieldx_shadow_confirmed',
        'shieldx_shadow_session_confirmed',
        'shieldx_shadow_ok',
        'shieldx_shadow_failed',
        'shieldx_shadow_more',
        'shieldx_backup_error',
        'shieldx_backup_storage_saved',
        'shieldx_google_drive_oauth_saved',
        'shieldx_google_drive_auth_started',
        'shieldx_google_drive_connected',
        'shieldx_google_drive_disconnected',
        'shieldx_google_drive_tested',
        'shieldx_google_drive_error',
        'shieldx_action_path',
        'shieldx_setup_mode_applied',
        'shieldx_setup_mode_warnings',
        'shieldx_setup_mode_error',
    ];
    private const FLASH_QUERY_KEYS = [
        'shieldx_module_notice',
        'shieldx_module_error',
        'shieldx_waf_error',
        'shieldx_2fa_error',
        'shieldx_admin_2fa_error',
        'shieldx_login_security_error',
        'shieldx_plugin_packages_error',
        'shieldx_plugin_package_uploaded',
        'shieldx_plugin_package_upload_error',
        'shieldx_plugin_package_trusted',
        'shieldx_plugin_package_trust_error',
        'shieldx_theme_package_uploaded',
        'shieldx_theme_package_upload_error',
        'shieldx_theme_package_trusted',
        'shieldx_theme_package_trust_error',
        'shieldx_core_error',
        'shieldx_scan_action_error',
        'shieldx_lockdown_error',
        'shieldx_lockdown_warning',
        'shieldx_permission_error',
        'shieldx_hardening_error',
        'shieldx_import_modules',
        'shieldx_import_error',
        'shieldx_license_error',
        'shieldx_restore_error',
        'shieldx_backup_error',
        'shieldx_google_drive_error',
        'shieldx_action_path',
        'shieldx_setup_mode_error',
        'shieldx_cap_error',
    ];

    /** @var Plugin */
    private $plugin;

    /** @var bool */
    private $renderingAjaxContent = false;

    /** @var array<string,string> */
    private $ajaxQueryContext = [];

    public function __construct(Plugin $plugin)
    {
        $this->plugin = $plugin;
    }

    public function register(): void
    {
        $capability = self::manageCapability();

        add_menu_page(
            __('SHIELD-X', 'xerotact-shield-x-security'),
            __('SHIELD-X', 'xerotact-shield-x-security'),
            $capability,
            'shield-x',
            [$this, 'renderOverview'],
            Iconoir::dataUri('health-shield'),
            65
        );

        add_submenu_page('shield-x', __('Overview', 'xerotact-shield-x-security'), __('Overview', 'xerotact-shield-x-security'), $capability, 'shield-x', [$this, 'renderOverview']);
        add_submenu_page('shield-x', __('Setup', 'xerotact-shield-x-security'), __('Setup', 'xerotact-shield-x-security'), $capability, 'shield-x-setup', [$this, 'renderSetup']);
        add_submenu_page('shield-x', __('Firewall', 'xerotact-shield-x-security'), __('Firewall', 'xerotact-shield-x-security'), $capability, 'shield-x-waf', [$this, 'renderWaf']);
        add_submenu_page('shield-x', __('Login Security', 'xerotact-shield-x-security'), __('Login Security', 'xerotact-shield-x-security'), $capability, 'shield-x-login-security', [$this, 'renderLoginSecurity']);
        add_submenu_page('shield-x', __('Malware/Virus', 'xerotact-shield-x-security'), __('Malware/Virus', 'xerotact-shield-x-security'), $capability, 'shield-x-scan', [$this, 'renderScan']);
        add_submenu_page('shield-x', __('Backup', 'xerotact-shield-x-security'), __('Backup', 'xerotact-shield-x-security'), $capability, 'shield-x-backup', [$this, 'renderBackup']);
        add_submenu_page('shield-x', __('Hardening', 'xerotact-shield-x-security'), __('Hardening', 'xerotact-shield-x-security'), $capability, 'shield-x-hardening', [$this, 'renderHardening']);
        add_submenu_page('shield-x', __('Lockdown', 'xerotact-shield-x-security'), __('Lockdown', 'xerotact-shield-x-security'), $capability, 'shield-x-lockdown', [$this, 'renderLockdown']);
        add_submenu_page('shield-x', __('Incident/Activities', 'xerotact-shield-x-security'), __('Incident/Activities', 'xerotact-shield-x-security'), $capability, 'shield-x-activity', [$this, 'renderActivity']);
        add_submenu_page('shield-x', __('Cleanup', 'xerotact-shield-x-security'), __('Cleanup', 'xerotact-shield-x-security'), $capability, 'shield-x-log-rotation', [$this, 'renderLogRotation']);
        add_submenu_page('shield-x', __('System', 'xerotact-shield-x-security'), __('System', 'xerotact-shield-x-security'), $capability, 'shield-x-system', [$this, 'renderSystem']);
        // Preserve the private-management route without exposing it in core navigation.
        add_submenu_page(null, __('Extensions', 'xerotact-shield-x-security'), __('Extensions', 'xerotact-shield-x-security'), $capability, 'shield-x-addons', [$this, 'renderAddons']);
        add_submenu_page(null, __('WAF Access', 'xerotact-shield-x-security'), __('WAF Access', 'xerotact-shield-x-security'), $capability, 'shield-x-waf-access', [$this, 'renderWafAccess']);
        add_submenu_page(null, __('Restore Point Contents', 'xerotact-shield-x-security'), __('Restore Point Contents', 'xerotact-shield-x-security'), $capability, 'shield-x-backup-restore', [$this, 'renderRestorePoint']);

        foreach (AddonRegistry::fromWordPress()->submenus() as $submenu) {
            $callback = $submenu['callback'] ?? null;
            if (!is_callable($callback)) {
                self::logInvalidAddonSubmenuCallback((string) ($submenu['menu_slug'] ?? ''));
                continue;
            }

            add_submenu_page(
                (string) ($submenu['parent_slug'] ?? 'shield-x'),
                (string) ($submenu['page_title'] ?? ''),
                (string) ($submenu['menu_title'] ?? ''),
                (string) ($submenu['capability'] ?? $capability),
                (string) ($submenu['menu_slug'] ?? ''),
                $callback,
                (int) ($submenu['position'] ?? 90)
            );
        }
    }

    private static function logInvalidAddonSubmenuCallback(string $menuSlug): void
    {
        static $loggedMenuSlugs = [];

        $menuSlug = $menuSlug !== '' ? $menuSlug : 'unknown';
        if (isset($loggedMenuSlugs[$menuSlug])) {
            return;
        }

        $loggedMenuSlugs[$menuSlug] = true;
        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- An invalid third-party add-on callback is logged once per menu slug so site owners can diagnose the skipped submenu.
        \ShieldX\Support\ErrorLog::write(sprintf('[SHIELD-X] Skipped add-on submenu "%s": callback is not callable.', $menuSlug));
    }

    public function registerDashboardWidgets(): void
    {
        if (!function_exists('wp_add_dashboard_widget') || !self::currentUserCanManageShieldX()) {
            return;
        }

        wp_add_dashboard_widget(
            'shieldx_security_trends',
            __('SHIELD-X Security Trends', 'xerotact-shield-x-security'),
            [$this, 'renderDashboardSecurityTrendsWidget']
        );
    }

    public function renderAdminToolbarStatus($wpAdminBar): void
    {
        if (!self::currentUserCanManageShieldX() || !is_object($wpAdminBar) || !method_exists($wpAdminBar, 'add_node')) {
            return;
        }

        $license = LicenseManager::fromWordPress()->summaryForCurrentSession();
        $statusKey = LicenseSettings::sanitizeStatus((string) ($license['status'] ?? LicenseSettings::STATUS_INACTIVE));
        $statusLabel = (string) ($license['label'] ?? LicenseSettings::statusLabel($statusKey));
        $statusClass = $statusKey === LicenseSettings::STATUS_ACTIVE ? 'is-active' : 'needs-attention';

        $wpAdminBar->add_node([
            'id' => 'shieldx-status',
            'title' => sprintf(
                '<span class="ab-icon dashicons dashicons-shield-alt" aria-hidden="true"></span><span class="ab-label">%s</span><span class="%s">%s</span>',
                esc_html__('SHIELD-X', 'xerotact-shield-x-security'),
                esc_attr('shieldx-adminbar-state shieldx-adminbar-state--' . $statusClass),
                esc_html($statusLabel)
            ),
            'href' => admin_url('admin.php?page=shield-x'),
            'meta' => [
                'class' => 'shieldx-adminbar-status-node',
                'title' => __('Open SHIELD-X', 'xerotact-shield-x-security'),
            ],
        ]);

        $coreItems = [
            'overview' => [__('Overview', 'xerotact-shield-x-security'), 'shield-x'],
            'firewall' => [__('Firewall', 'xerotact-shield-x-security'), 'shield-x-waf'],
            'activity' => [__('Incident/Activities', 'xerotact-shield-x-security'), 'shield-x-activity'],
            'system' => [__('System', 'xerotact-shield-x-security'), 'shield-x-system'],
        ];

        foreach ($coreItems as $key => $item) {
            $wpAdminBar->add_node([
                'id' => 'shieldx-' . $key,
                'parent' => 'shieldx-status',
                'title' => esc_html((string) $item[0]),
                'href' => admin_url('admin.php?page=' . (string) $item[1]),
            ]);
        }

        foreach (AddonRegistry::fromWordPress()->submenus() as $submenu) {
            $slug = (string) ($submenu['menu_slug'] ?? '');
            $title = (string) ($submenu['menu_title'] ?? '');
            $capability = (string) ($submenu['capability'] ?? self::manageCapability());
            if ($slug === '' || $title === '' || (function_exists('current_user_can') && !current_user_can($capability))) {
                continue;
            }

            $wpAdminBar->add_node([
                'id' => 'shieldx-addon-' . sanitize_key($slug),
                'parent' => 'shieldx-status',
                'title' => esc_html($title),
                'href' => admin_url('admin.php?page=' . rawurlencode($slug)),
            ]);
        }
    }

    public function renderDashboardSecurityTrendsWidget(string $adminCss = '', string $adminScript = ''): void
    {
        if (!self::currentUserCanManageShieldX()) {
            return;
        }

        // Render the indexed aggregate in the original Dashboard response. A
        // second admin-ajax.php bootstrap roughly doubles the time before the
        // widget becomes useful on slower WordPress hosts. The audit-log
        // fallback is disabled here so this path remains bounded.
        $incidentTimeline = IncidentTimeline::fromWordPress()->dashboardActivitySummary(365, false);

        ?>
        <div class="shieldx-admin shieldx-dashboard-widget" data-shieldx-admin>
            <div
                data-shieldx-dashboard-widget-content
                data-shieldx-dashboard-widget-ready="1"
                data-shieldx-dashboard-widget-css="<?php echo esc_url($adminCss); ?>"
                data-shieldx-dashboard-widget-script="<?php echo esc_url($adminScript); ?>"
                aria-live="polite"
            >
                <?php $this->renderOverviewActivityChart($incidentTimeline, 'dashboard'); ?>
            </div>
        </div>
        <?php
    }

    public function suppressForeignAdminNotices(): void
    {
        if (!$this->isShieldXAdminScreen()) {
            return;
        }

        if (!apply_filters('shieldx_suppress_foreign_admin_notices', true)) {
            return;
        }

        foreach (['admin_notices', 'all_admin_notices', 'network_admin_notices', 'user_admin_notices'] as $hook) {
            remove_all_actions($hook);
        }
    }

    public function renderOverview(): void
    {
        if ($this->renderAsyncPageShell('shield-x')) {
            return;
        }

        $status = $this->statusForPage();
        $this->openPage('shield-x', $status);
        $this->renderOverviewContent($status);
        $this->closePage();
    }

    public function ajaxDashboardOverview(): void
    {
        if (!self::currentUserCanManageShieldX()) {
            wp_send_json_error([
                'message' => __('You do not have permission to access SHIELD-X.', 'xerotact-shield-x-security'),
            ], 403);
        }

        if (function_exists('check_ajax_referer')) {
            check_ajax_referer('shieldx_dashboard_overview');
        }

        $timeline = IncidentTimeline::fromWordPress();
        $incidentTimeline = $timeline->dashboardActivitySummary();

        ob_start();
        $this->renderOverviewActivityChart($incidentTimeline, 'dashboard');
        $html = (string) ob_get_clean();

        wp_send_json_success([
            'html' => $html,
        ]);
    }

    public function ajaxOverviewCardVisibility(): void
    {
        if (!self::currentUserCanManageShieldX()) {
            wp_send_json_error([
                'message' => __('You do not have permission to access SHIELD-X.', 'xerotact-shield-x-security'),
            ], 403);
        }

        if (function_exists('check_ajax_referer')) {
            check_ajax_referer('shieldx_overview_card_visibility');
        }

        $cardId = '';
        if (isset($_POST['card_id']) && is_scalar($_POST['card_id'])) {
            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Value is unslashed, sanitized, and restricted to OVERVIEW_CARD_IDS below.
            $cardId = sanitize_key((string) wp_unslash($_POST['card_id']));
        }

        if ($cardId === 'needs_attention') {
            $disabledIds = $this->disabledOverviewCardIds();
            wp_send_json_success([
                'disabled_ids' => array_values($disabledIds),
                'count' => count($disabledIds),
            ]);
        }

        if (!in_array($cardId, self::OVERVIEW_CARD_IDS, true)) {
            wp_send_json_error([
                'message' => __('Overview card was not recognized.', 'xerotact-shield-x-security'),
            ], 400);
        }

        $disabled = isset($_POST['disabled']) && is_scalar($_POST['disabled'])
            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Compared to a fixed scalar after unslashing.
            && (string) wp_unslash($_POST['disabled']) === '1';
        $disabledIds = $this->disabledOverviewCardIds();

        if ($disabled) {
            $disabledIds[$cardId] = $cardId;
        } else {
            unset($disabledIds[$cardId]);
        }

        $disabledIds = $this->normalizeOverviewCardIds(array_values($disabledIds));
        if (function_exists('update_user_meta') && function_exists('get_current_user_id')) {
            $userId = (int) get_current_user_id();
            update_user_meta($userId, self::OVERVIEW_DISABLED_CARDS_META, array_values($disabledIds));
            update_user_meta($userId, self::OVERVIEW_DISABLED_CARDS_INIT_META, self::OVERVIEW_DISABLED_CARDS_INIT_VERSION);
        }

        wp_send_json_success([
            'disabled_ids' => array_values($disabledIds),
            'count' => count($disabledIds),
        ]);
    }

    public function ajaxAdminPageContent(): void
    {
        $this->sendAjaxAdminPageContent('', 'shieldx_admin_page_content');
    }

    private function sendAjaxAdminPageContent(string $forcedPage, string $nonceAction): void
    {
        if (!self::currentUserCanManageShieldX()) {
            wp_send_json_error([
                'message' => __('You do not have permission to access SHIELD-X.', 'xerotact-shield-x-security'),
            ], 403);
        }

        if (function_exists('check_ajax_referer')) {
            check_ajax_referer($nonceAction);
        }

        $page = $forcedPage;
        if ($page === '' && isset($_POST['page']) && is_scalar($_POST['page'])) {
            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- The page is unslashed and restricted to the fixed renderer allowlist below.
            $page = sanitize_key((string) wp_unslash($_POST['page']));
        }

        $renderers = $this->ajaxPageRenderers();
        if (!isset($renderers[$page])) {
            wp_send_json_error([
                'message' => __('The requested SHIELD-X page is not available.', 'xerotact-shield-x-security'),
            ], 400);
        }

        $previousAjaxQueryContext = $this->ajaxQueryContext;
        $this->ajaxQueryContext = $this->ajaxQueryContextFromPost();
        $this->ajaxQueryContext['page'] = $page;

        try {
            $this->renderingAjaxContent = true;
            ob_start();
            try {
                call_user_func([$this, $renderers[$page]]);
                $html = (string) ob_get_clean();
            } catch (\Throwable $error) {
                ob_end_clean();
                throw $error;
            } finally {
                $this->renderingAjaxContent = false;
            }

            ob_start();
            $this->renderAddonHeaderActions($page, $this->asyncPageShellStatus());
            $headerActionsHtml = (string) ob_get_clean();
        } catch (\Throwable $error) {
            $this->ajaxQueryContext = $previousAjaxQueryContext;
            throw $error;
        }

        $this->ajaxQueryContext = $previousAjaxQueryContext;

        wp_send_json_success([
            'html' => $html,
            'header_actions_html' => $headerActionsHtml,
        ]);
    }

    /**
     * @return array<string,string>
     */
    private function ajaxPageRenderers(): array
    {
        return [
            'shield-x' => 'renderOverview',
            'shield-x-setup' => 'renderSetup',
            'shield-x-addons' => 'renderAddons',
            'shield-x-waf' => 'renderWaf',
            'shield-x-waf-access' => 'renderWafAccess',
            'shield-x-login-security' => 'renderLoginSecurity',
            'shield-x-scan' => 'renderScan',
            'shield-x-backup' => 'renderBackup',
            'shield-x-backup-restore' => 'renderRestorePoint',
            'shield-x-hardening' => 'renderHardening',
            'shield-x-lockdown' => 'renderLockdown',
            'shield-x-activity' => 'renderActivity',
            'shield-x-log-rotation' => 'renderLogRotation',
            'shield-x-system' => 'renderSystem',
        ];
    }

    /**
     * @return array<string,string>
     */
    // phpcs:disable WordPress.Security.NonceVerification.Recommended,WordPress.Security.NonceVerification.Missing -- sendAjaxAdminPageContent() verifies the nonce and capability before copying this read-only render context.
    private function ajaxQueryContextFromPost(): array
    {
        if (!isset($_POST['context']) || !is_array($_POST['context'])) {
            return [];
        }

        $context = map_deep(wp_unslash($_POST['context']), 'sanitize_text_field');
        $queryContext = [];

        foreach ($context as $key => $value) {
            $key = sanitize_key((string) $key);
            if (
                $key === ''
                || in_array($key, ['action', '_ajax_nonce', 'context', 'page'], true)
                || !$this->isAllowedAjaxContextKey($key)
                || !is_scalar($value)
            ) {
                continue;
            }

            $queryContext[$key] = (string) $value;
        }

        return $queryContext;
    }

    private function isAllowedAjaxContextKey(string $key): bool
    {
        return strpos($key, 'shieldx_') === 0
            || in_array($key, self::ALLOWED_AJAX_CONTEXT_KEYS, true);
    }
    // phpcs:enable WordPress.Security.NonceVerification.Recommended,WordPress.Security.NonceVerification.Missing

    /**
     * @param array<int,mixed> $ids
     * @return array<string,string>
     */
    private function normalizeOverviewCardIds(array $ids): array
    {
        $normalized = [];

        foreach ($ids as $id) {
            $id = sanitize_key((string) $id);
            if ($id !== '' && $id !== 'needs_attention' && in_array($id, self::OVERVIEW_CARD_IDS, true)) {
                $normalized[$id] = $id;
            }
        }

        return $normalized;
    }

    /**
     * @return array<string,string>
     */
    private function disabledOverviewCardIds(): array
    {
        if (!function_exists('get_user_meta') || !function_exists('get_current_user_id')) {
            return [];
        }

        $userId = (int) get_current_user_id();
        if ($userId <= 0) {
            return [];
        }

        $stored = get_user_meta($userId, self::OVERVIEW_DISABLED_CARDS_META, true);
        $storedIds = is_array($stored) ? $this->normalizeOverviewCardIds(array_values($stored)) : [];
        $initVersion = (string) get_user_meta($userId, self::OVERVIEW_DISABLED_CARDS_INIT_META, true);
        $initialized = function_exists('metadata_exists')
            ? metadata_exists('user', $userId, self::OVERVIEW_DISABLED_CARDS_INIT_META)
            : $initVersion !== '';

        if ($initialized) {
            if ($initVersion !== self::OVERVIEW_DISABLED_CARDS_INIT_VERSION) {
                $storedIds = $this->normalizeOverviewCardIds(array_merge(['active_attack_sources'], array_values($storedIds)));
                if (function_exists('update_user_meta')) {
                    update_user_meta($userId, self::OVERVIEW_DISABLED_CARDS_META, array_values($storedIds));
                    update_user_meta($userId, self::OVERVIEW_DISABLED_CARDS_INIT_META, self::OVERVIEW_DISABLED_CARDS_INIT_VERSION);
                }
            }

            return $storedIds;
        }

        return $this->normalizeOverviewCardIds(array_merge(self::OVERVIEW_DEFAULT_DISABLED_CARD_IDS, array_values($storedIds)));
    }

    /**
     * @param array<string,mixed> $status
     */
    private function renderOverviewContent(array $status): void
    {
        $setup = $this->setupAssistant($status);
        $readiness = $this->readinessReport($status, $setup);
        $timeline = IncidentTimeline::fromWordPress();
        $incidentTimeline = $timeline->summary(250);
        $incidentTimeline['daily_summaries'] = $timeline->dailyActivitySummary(365);
        $cards = $this->overviewAttentionCards($status, $readiness, $incidentTimeline);
        $disabledCards = $this->disabledOverviewCardIds();

        ?>
        <?php $this->renderOverviewAttentionCards($cards, $disabledCards); ?>

        <?php $this->renderOverviewActivityChart($incidentTimeline); ?>

        <?php $this->renderSetupProgressPanel($status, $setup); ?>
        <?php $this->renderAddonStatusPanel($status); ?>

        <div class="shieldx-layout shieldx-layout--single">
            <?php $this->renderEmergencyRecoveryPanel($status); ?>
        </div>
        <?php
    }

    private function adminPageAjaxReady(): bool
    {
        return function_exists('admin_url') && function_exists('wp_create_nonce');
    }

    /**
     * @return array<string,mixed>
     */
    private function asyncPageShellStatus(): array
    {
        return [
            'licensing' => LicenseManager::fromWordPress()->summaryForCurrentSession(),
        ];
    }

    private function renderAsyncPageShell(string $activePage): bool
    {
        if ($this->renderingAjaxContent || !$this->adminPageAjaxReady()) {
            return false;
        }

        if (!self::currentUserCanManageShieldX()) {
            wp_die(esc_html__('You do not have permission to access SHIELD-X.', 'xerotact-shield-x-security'));
        }

        $this->openPage($activePage, $this->asyncPageShellStatus(), true);
        $this->renderAdminPageShell($activePage);
        $this->closePage();

        return true;
    }

    private function renderAdminPageShell(string $activePage): void
    {
        ?>
        <div
            data-shieldx-admin-page-content
            data-shieldx-admin-page="<?php echo esc_attr($activePage); ?>"
            data-shieldx-admin-page-url="<?php echo esc_url(admin_url('admin-ajax.php')); ?>"
            data-shieldx-admin-page-nonce="<?php echo esc_attr(wp_create_nonce('shieldx_admin_page_content')); ?>"
            data-shieldx-admin-page-error="<?php echo esc_attr__('SHIELD-X could not load this page. Try again or reload the page.', 'xerotact-shield-x-security'); ?>"
        >
            <?php if ($activePage === 'shield-x') : ?>
            <section class="shieldx-overview-loading shieldx-skeleton-overview-shell" aria-live="polite">
                <span class="shieldx-screen-reader-text"><?php echo esc_html__('Loading SHIELD-X page content.', 'xerotact-shield-x-security'); ?></span>
                <div class="shieldx-skeleton-overview-metrics" aria-hidden="true">
                    <div class="shieldx-skeleton-summary-band">
                        <span class="shieldx-skeleton-ring shieldx-skeleton-ring--summary"></span>
                        <div class="shieldx-skeleton-summary-copy">
                            <span class="shieldx-skeleton-line shieldx-skeleton-line--summary-detail"></span>
                            <span class="shieldx-skeleton-line shieldx-skeleton-line--summary-status"></span>
                        </div>
                        <span class="shieldx-skeleton-line shieldx-skeleton-line--summary-action"></span>
                    </div>
                    <div class="shieldx-skeleton-metric-grid">
                        <?php for ($index = 0; $index < 8; $index++) : ?>
                            <div class="shieldx-skeleton-metric">
                                <div class="shieldx-skeleton-card-head">
                                    <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-label"></span>
                                    <span class="shieldx-skeleton-icon"></span>
                                </div>
                                <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-value"></span>
                                <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-detail"></span>
                                <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-stats"></span>
                                <div class="shieldx-skeleton-card-footer">
                                    <span class="shieldx-skeleton-line shieldx-skeleton-line--action"></span>
                                    <span class="shieldx-skeleton-toggle"></span>
                                </div>
                            </div>
                        <?php endfor; ?>
                    </div>
                </div>
            </section>
            <section class="shieldx-panel shieldx-panel--full shieldx-overview-loading shieldx-skeleton-chart-panel" aria-hidden="true">
                <div class="shieldx-section-head">
                    <div>
                        <span class="shieldx-skeleton-line shieldx-skeleton-line--title"></span>
                        <span class="shieldx-skeleton-line shieldx-skeleton-line--subtitle"></span>
                    </div>
                    <span class="shieldx-skeleton-line shieldx-skeleton-line--button"></span>
                </div>
                <div class="shieldx-skeleton-chart-grid">
                    <div class="shieldx-skeleton-chart">
                        <?php for ($line = 0; $line < 4; $line++) : ?>
                            <span class="shieldx-skeleton-chart-rule"></span>
                        <?php endfor; ?>
                        <span class="shieldx-skeleton-chart-wave shieldx-skeleton-chart-wave--one"></span>
                        <span class="shieldx-skeleton-chart-wave shieldx-skeleton-chart-wave--two"></span>
                    </div>
                    <div class="shieldx-skeleton-signal-grid">
                        <?php for ($index = 0; $index < 6; $index++) : ?>
                            <div class="shieldx-skeleton-signal-card">
                                <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-label"></span>
                                <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-value"></span>
                                <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-detail"></span>
                            </div>
                        <?php endfor; ?>
                    </div>
                </div>
            </section>
            <div class="shieldx-layout shieldx-skeleton-lower" aria-hidden="true">
                <?php for ($panel = 0; $panel < 2; $panel++) : ?>
                    <section class="shieldx-panel shieldx-overview-loading">
                        <div class="shieldx-section-head">
                            <div>
                                <span class="shieldx-skeleton-line shieldx-skeleton-line--title"></span>
                                <span class="shieldx-skeleton-line shieldx-skeleton-line--subtitle"></span>
                            </div>
                        </div>
                        <?php for ($row = 0; $row < 4; $row++) : ?>
                            <span class="shieldx-skeleton-row"></span>
                        <?php endfor; ?>
                    </section>
                <?php endfor; ?>
            </div>
            <?php else : ?>
                <?php $this->renderPageSpecificSkeleton($activePage); ?>
            <?php endif; ?>
        </div>
        <?php
    }

    private function renderPageSpecificSkeleton(string $activePage): void
    {
        $layout = str_replace('shield-x-', '', $activePage);
        if ($layout === $activePage || $layout === '') {
            $layout = 'settings';
        }
        ?>
        <span class="shieldx-screen-reader-text"><?php echo esc_html__('Loading SHIELD-X page content.', 'xerotact-shield-x-security'); ?></span>
        <div class="<?php echo esc_attr('shieldx-skeleton-page shieldx-skeleton-page--' . $layout); ?>" data-shieldx-skeleton-layout="<?php echo esc_attr($layout); ?>" aria-hidden="true">
            <?php
            switch ($activePage) {
                case 'shield-x-setup':
                    $this->renderSetupPageSkeleton();
                    break;
                case 'shield-x-addons':
                    $this->renderSkeletonMetrics(4);
                    $this->renderSkeletonTablePanel(5);
                    break;
                case 'shield-x-scan':
                    $this->renderSkeletonMetrics(5);
                    $this->renderSkeletonTabs(3);
                    $this->renderSkeletonDetailPanel(3);
                    break;
                case 'shield-x-backup':
                    $this->renderSkeletonMetrics(6);
                    $this->renderSkeletonPanel(5, true);
                    $this->renderSkeletonPanel(4, false);
                    $this->renderSkeletonPanel(4, false);
                    break;
                case 'shield-x-backup-restore':
                    $this->renderSkeletonDetailPanel(4);
                    break;
                case 'shield-x-waf':
                    $this->renderSkeletonMetrics(4);
                    $this->renderSkeletonEventPanel(3);
                    $this->renderSkeletonOptionPanel(3);
                    break;
                case 'shield-x-waf-access':
                    $this->renderSkeletonAccessPanel();
                    $this->renderSkeletonTablePanel(6);
                    break;
                case 'shield-x-login-security':
                    $this->renderSkeletonMetrics(5);
                    $this->renderSkeletonPanelGrid(2, 4);
                    $this->renderSkeletonTablePanel(4);
                    break;
                case 'shield-x-hardening':
                    $this->renderSkeletonMetrics(5);
                    $this->renderSkeletonOptionPanel(6);
                    $this->renderSkeletonPanel(4, false);
                    break;
                case 'shield-x-lockdown':
                    $this->renderSkeletonMetrics(5);
                    $this->renderSkeletonOptionPanel(3);
                    $this->renderSkeletonPanelGrid(2, 4);
                    break;
                case 'shield-x-activity':
                    $this->renderSkeletonMetrics(7);
                    $this->renderSkeletonPanelGrid(2, 5);
                    $this->renderSkeletonTablePanel(5);
                    break;
                case 'shield-x-system':
                    $this->renderSkeletonTabs(5);
                    $this->renderSkeletonPanelGrid(2, 5);
                    $this->renderSkeletonTablePanel(5);
                    break;
                case 'shield-x-log-rotation':
                    $this->renderSkeletonMetrics(5);
                    $this->renderSkeletonPanel(5, true);
                    $this->renderSkeletonPanel(5, true);
                    break;
                default:
                    $this->renderSkeletonMetrics(4);
                    $this->renderSkeletonPanelGrid(2, 4);
                    break;
            }
            ?>
        </div>
        <?php
    }

    private function renderSkeletonMetrics(int $count): void
    {
        ?>
        <section class="shieldx-skeleton-summary-grid" data-shieldx-skeleton-region="metrics">
            <?php for ($index = 0; $index < $count; $index++) : ?>
                <div class="shieldx-skeleton-summary-card">
                    <span class="shieldx-skeleton-icon"></span>
                    <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-label"></span>
                    <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-value"></span>
                    <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-detail"></span>
                </div>
            <?php endfor; ?>
        </section>
        <?php
    }

    private function renderSkeletonTabs(int $count): void
    {
        ?>
        <nav class="shieldx-skeleton-tab-grid" data-shieldx-skeleton-region="selector">
            <?php for ($index = 0; $index < $count; $index++) : ?>
                <div class="shieldx-skeleton-tab-card">
                    <span class="shieldx-skeleton-icon"></span>
                    <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-label"></span>
                    <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-detail"></span>
                </div>
            <?php endfor; ?>
        </nav>
        <?php
    }

    private function renderSkeletonPanel(int $rows, bool $button): void
    {
        ?>
        <section class="shieldx-panel shieldx-panel--full shieldx-overview-loading shieldx-skeleton-page-panel" data-shieldx-skeleton-region="panel">
            <?php $this->renderSkeletonPanelHeading($button); ?>
            <?php for ($row = 0; $row < $rows; $row++) : ?>
                <span class="shieldx-skeleton-row"></span>
            <?php endfor; ?>
        </section>
        <?php
    }

    private function renderSkeletonPanelHeading(bool $button): void
    {
        ?>
        <div class="shieldx-section-head">
            <div>
                <span class="shieldx-skeleton-line shieldx-skeleton-line--title"></span>
                <span class="shieldx-skeleton-line shieldx-skeleton-line--subtitle"></span>
            </div>
            <?php if ($button) : ?>
                <span class="shieldx-skeleton-line shieldx-skeleton-line--button"></span>
            <?php endif; ?>
        </div>
        <?php
    }

    private function renderSkeletonPanelGrid(int $panels, int $rows): void
    {
        ?>
        <div class="shieldx-skeleton-panel-grid" data-shieldx-skeleton-region="panel-grid">
            <?php for ($panel = 0; $panel < $panels; $panel++) : ?>
                <?php $this->renderSkeletonPanel($rows, false); ?>
            <?php endfor; ?>
        </div>
        <?php
    }

    private function renderSkeletonOptionPanel(int $options): void
    {
        ?>
        <section class="shieldx-panel shieldx-panel--full shieldx-overview-loading shieldx-skeleton-page-panel" data-shieldx-skeleton-region="options">
            <?php $this->renderSkeletonPanelHeading(true); ?>
            <div class="shieldx-skeleton-option-grid">
                <?php for ($option = 0; $option < $options; $option++) : ?>
                    <div class="shieldx-skeleton-option-card">
                        <span class="shieldx-skeleton-option-control"></span>
                        <div>
                            <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-label"></span>
                            <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-detail"></span>
                        </div>
                    </div>
                <?php endfor; ?>
            </div>
        </section>
        <?php
    }

    private function renderSkeletonDetailPanel(int $cards): void
    {
        ?>
        <section class="shieldx-panel shieldx-panel--full shieldx-overview-loading shieldx-skeleton-page-panel" data-shieldx-skeleton-region="detail">
            <?php $this->renderSkeletonPanelHeading(true); ?>
            <div class="shieldx-skeleton-detail-grid">
                <?php for ($card = 0; $card < $cards; $card++) : ?>
                    <div class="shieldx-skeleton-detail-card">
                        <span class="shieldx-skeleton-line shieldx-skeleton-line--title"></span>
                        <?php for ($row = 0; $row < 4; $row++) : ?>
                            <span class="shieldx-skeleton-row"></span>
                        <?php endfor; ?>
                    </div>
                <?php endfor; ?>
            </div>
        </section>
        <?php
    }

    private function renderSkeletonEventPanel(int $events): void
    {
        ?>
        <section class="shieldx-panel shieldx-panel--full shieldx-overview-loading shieldx-skeleton-page-panel" data-shieldx-skeleton-region="events">
            <?php $this->renderSkeletonPanelHeading(true); ?>
            <?php for ($event = 0; $event < $events; $event++) : ?>
                <div class="shieldx-skeleton-event-row">
                    <span class="shieldx-skeleton-icon"></span>
                    <div>
                        <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-label"></span>
                        <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-detail"></span>
                    </div>
                    <span class="shieldx-skeleton-line shieldx-skeleton-line--button"></span>
                </div>
            <?php endfor; ?>
        </section>
        <?php
    }

    private function renderSkeletonTablePanel(int $rows): void
    {
        ?>
        <section class="shieldx-panel shieldx-panel--full shieldx-overview-loading shieldx-skeleton-page-panel" data-shieldx-skeleton-region="table">
            <?php $this->renderSkeletonPanelHeading(true); ?>
            <div class="shieldx-skeleton-table">
                <?php for ($row = 0; $row < $rows + 1; $row++) : ?>
                    <div class="<?php echo esc_attr('shieldx-skeleton-table-row' . ($row === 0 ? ' is-heading' : '')); ?>">
                        <?php for ($column = 0; $column < 5; $column++) : ?>
                            <span class="shieldx-skeleton-line"></span>
                        <?php endfor; ?>
                    </div>
                <?php endfor; ?>
            </div>
        </section>
        <?php
    }

    private function renderSkeletonAccessPanel(): void
    {
        ?>
        <section class="shieldx-panel shieldx-panel--full shieldx-overview-loading shieldx-skeleton-page-panel" data-shieldx-skeleton-region="access-summary">
            <?php $this->renderSkeletonPanelHeading(true); ?>
            <?php $this->renderSkeletonMetrics(4); ?>
            <div class="shieldx-skeleton-filter-row">
                <?php for ($field = 0; $field < 3; $field++) : ?>
                    <span class="shieldx-skeleton-filter-field"></span>
                <?php endfor; ?>
                <span class="shieldx-skeleton-line shieldx-skeleton-line--button"></span>
            </div>
        </section>
        <?php
    }

    private function renderSetupPageSkeleton(): void
    {
        ?>
        <section class="shieldx-panel shieldx-panel--full shieldx-overview-loading shieldx-skeleton-setup-hero" data-shieldx-skeleton-region="setup-hero">
            <div>
                <span class="shieldx-skeleton-line shieldx-skeleton-line--title"></span>
                <span class="shieldx-skeleton-line shieldx-skeleton-line--subtitle"></span>
                <span class="shieldx-skeleton-row"></span>
            </div>
            <span class="shieldx-skeleton-ring"></span>
        </section>
        <?php $this->renderSkeletonOptionPanel(3); ?>
        <section class="shieldx-panel shieldx-panel--full shieldx-overview-loading shieldx-skeleton-page-panel" data-shieldx-skeleton-region="setup-steps">
            <?php $this->renderSkeletonPanelHeading(false); ?>
            <div class="shieldx-skeleton-step-grid">
                <?php for ($step = 0; $step < 4; $step++) : ?>
                    <div class="shieldx-skeleton-step-card">
                        <span class="shieldx-skeleton-icon"></span>
                        <div>
                            <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-label"></span>
                            <span class="shieldx-skeleton-line shieldx-skeleton-line--metric-detail"></span>
                        </div>
                    </div>
                <?php endfor; ?>
            </div>
        </section>
        <?php
    }

    public function renderSetup(): void
    {
        if ($this->renderAsyncPageShell('shield-x-setup')) {
            return;
        }

        // The async page response renders the completion count and every
        // baseline card, so it must use the current site status. An empty
        // status makes every setup condition evaluate as incomplete.
        $status = $this->statusForPage();
        $setup = $this->setupAssistant($status);
        $baseline = isset($setup['steps']) && is_array($setup['steps']) ? $setup['steps'] : [];
        $advanced = isset($setup['advanced']) && is_array($setup['advanced']) ? $setup['advanced'] : [];

        $this->openPage('shield-x-setup', $status);
        ?>
        <section class="shieldx-panel shieldx-panel--full shieldx-setup-hero">
            <div>
                <div class="shieldx-section-head">
                    <div class="shieldx-setup-hero-title">
                        <h2><?php echo esc_html__('Setup Assistant', 'xerotact-shield-x-security'); ?></h2>
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <span class="shieldx-setup-count-label"><?php echo esc_html(sprintf(__('%1$d of %2$d complete', 'xerotact-shield-x-security'), (int) ($setup['complete'] ?? 0), (int) ($setup['total'] ?? 0))); ?></span>
                    </div>
                </div>
                <p><?php echo esc_html__('Follow these steps to bring SHIELD-X from installed to protected. Start with the first incomplete item, then return here until the baseline reaches 100%.', 'xerotact-shield-x-security'); ?></p>
            </div>
            <div class="shieldx-setup-score" style="<?php echo esc_attr('--shieldx-setup:' . (string) max(0, min(100, (int) ($setup['percent'] ?? 0))) . ';'); ?>">
                <strong><?php echo esc_html((string) ($setup['percent'] ?? 0)); ?>%</strong>
            </div>
        </section>

        <?php $this->renderSetupModePanel(); ?>
        <?php $this->renderAddonStatusPanel($status, 'setup'); ?>

        <section class="shieldx-panel shieldx-panel--full shieldx-setup-advanced" data-shieldx-accordion-open="1">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Advanced After Baseline', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html__('Optional hardening controls', 'xerotact-shield-x-security'); ?></span>
            </div>
            <div class="shieldx-setup-optional-grid">
                <?php foreach ($advanced as $step) : ?>
                    <?php $this->renderSetupStepCard(is_array($step) ? $step : [], true); ?>
                <?php endforeach; ?>
            </div>
        </section>

        <section class="shieldx-setup-steps" aria-label="<?php echo esc_attr__('SHIELD-X setup baseline steps', 'xerotact-shield-x-security'); ?>">
            <?php
            $setupLoginSecurity = isset($status['login_security']) && is_array($status['login_security']) ? $status['login_security'] : (new LoginSecuritySettings())->all();
            $setupUserId = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;
            $setupUserState = $setupUserId > 0 ? LoginSecurity::fromWordPress()->userState($setupUserId) : [];
            $setupAdminUsers = LoginSecurity::fromWordPress()->adminUsers();
            $this->renderLostAuthenticatorSetupDisclosure($setupLoginSecurity, $setupUserState, $setupAdminUsers);
            ?>
            <?php foreach ($baseline as $step) : ?>
                <?php $this->renderSetupStepCard(is_array($step) ? $step : []); ?>
            <?php endforeach; ?>
        </section>
        <?php
        $this->closePage();
    }

    public function renderAddons(): void
    {
        if ($this->renderAsyncPageShell('shield-x-addons')) {
            return;
        }

        $status = $this->statusForPage();
        $license = isset($status['licensing']) && is_array($status['licensing']) ? $status['licensing'] : LicenseManager::fromWordPress()->summaryForCurrentSession();
        $licenseActive = LicenseSettings::sanitizeStatus((string) ($license['status'] ?? LicenseSettings::STATUS_INACTIVE)) === LicenseSettings::STATUS_ACTIVE
            && !empty($license['has_stored_key']);
        $registry = AddonRegistry::fromWordPress();
        $modules = $registry->moduleItems();
        $enabled = 0;
        $disabled = 0;
        foreach ($modules as $module) {
            if (!empty($module['enabled'])) {
                $enabled++;
            } else {
                $disabled++;
            }
        }

        $notice = $this->queryText('shieldx_module_notice');
        $error = $this->queryText('shieldx_module_error');
        $showManagement = $licenseActive && $modules !== [];
        $managementDescription = $licenseActive
            ? __('Paid extensions are provided by the private SHIELD-X Extensions companion plugin. Enable or disable extension behavior here; the WordPress.org plugin does not upload, install, update, or delete executable extension code.', 'xerotact-shield-x-security')
            : __('Activate SHIELD-X to run private extensions. Until then, extension runtime, shortcuts, pages, and management controls stay unavailable.', 'xerotact-shield-x-security');

        $this->openPage('shield-x-addons', $status);
        ?>
        <?php if ($notice !== '') : ?>
            <div class="notice notice-success is-dismissible">
                <p><?php echo esc_html($notice); ?></p>
            </div>
        <?php endif; ?>
        <?php if ($error !== '') : ?>
            <div class="notice notice-error is-dismissible">
                <p><?php echo esc_html($error); ?></p>
            </div>
        <?php endif; ?>
        <?php if ($showManagement) : ?>
            <section class="shieldx-metrics" aria-label="<?php echo esc_attr__('SHIELD-X extension summary', 'xerotact-shield-x-security'); ?>">
                <?php $this->renderMetric(__('Extensions', 'xerotact-shield-x-security'), (string) count($modules), count($modules) > 0 ? 'info' : 'warn', 'activity'); ?>
                <?php $this->renderMetric(__('Enabled', 'xerotact-shield-x-security'), (string) $enabled, $enabled > 0 ? 'good' : 'info', 'badge-check'); ?>
                <?php $this->renderMetric(__('Disabled', 'xerotact-shield-x-security'), (string) $disabled, $disabled > 0 ? 'warn' : 'good', 'lock'); ?>
                <?php $this->renderMetric(__('API', 'xerotact-shield-x-security'), 'v' . (string) AddonRegistry::apiVersion(), 'info', 'tools'); ?>
            </section>
        <?php endif; ?>

        <section class="shieldx-panel shieldx-panel--full shieldx-addon-management">
            <div class="shieldx-section-head">
                <div>
                    <h2><?php echo esc_html__('Extension Management', 'xerotact-shield-x-security'); ?></h2>
                    <span><?php echo esc_html($managementDescription); ?></span>
                </div>
            </div>
            <?php if (!$showManagement) : ?>
                <?php $this->renderInactiveExtensionsCatalog($licenseActive); ?>
            <?php else : ?>
                <div class="shieldx-rule-table-wrap">
                    <table class="widefat striped">
                        <thead>
                            <tr>
                                <th><?php echo esc_html__('Extension', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('State', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Type', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Actions', 'xerotact-shield-x-security'); ?></th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php foreach ($modules as $module) : ?>
                                <?php
                                $id = (string) ($module['id'] ?? '');
                                $isEnabled = !empty($module['enabled']);
                                $isLoaded = !empty($module['loaded']);
                                $adminPage = (string) ($module['admin_page'] ?? '');
                                $canDisable = !empty($module['can_disable']);
                                $sourceLabel = (string) ($module['source_label'] ?? __('Private extension', 'xerotact-shield-x-security'));
                                $stateClass = $isEnabled ? 'good' : 'warn';
                                $stateLabel = $isEnabled ? __('Enabled', 'xerotact-shield-x-security') : __('Disabled', 'xerotact-shield-x-security');
                                ?>
                                <tr>
                                    <td>
                                        <strong><?php echo esc_html((string) ($module['name'] ?? $id)); ?></strong>
                                        <span class="shieldx-muted"><?php echo esc_html((string) ($module['version'] ?? '')); ?></span>
                                    </td>
                                    <td>
                                        <span class="<?php echo esc_attr('shieldx-status-pill shieldx-status-pill--' . $stateClass); ?>">
                                            <?php echo esc_html($stateLabel); ?>
                                        </span>
                                        <?php if ($isEnabled && !$isLoaded) : ?>
                                            <span class="shieldx-muted"><?php echo esc_html__('not loaded', 'xerotact-shield-x-security'); ?></span>
                                        <?php endif; ?>
                                    </td>
                                    <td>
                                        <code><?php echo esc_html($id); ?></code>
                                        <span class="shieldx-muted"><?php echo esc_html($sourceLabel); ?></span>
                                    </td>
                                    <td>
                                        <div class="shieldx-inline-actions">
                                            <?php if ($adminPage !== '' && $isEnabled) : ?>
                                                <a class="button button-small" href="<?php echo esc_url(admin_url('admin.php?page=' . $adminPage)); ?>"><?php echo esc_html__('Open', 'xerotact-shield-x-security'); ?></a>
                                            <?php endif; ?>
                                            <?php if ($canDisable) : ?>
                                                <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                                                    <input type="hidden" name="action" value="shieldx_toggle_module">
                                                    <input type="hidden" name="module_id" value="<?php echo esc_attr($id); ?>">
                                                    <input type="hidden" name="enabled" value="<?php echo esc_attr($isEnabled ? '0' : '1'); ?>">
                                                    <?php wp_nonce_field('shieldx_toggle_module'); ?>
                                                    <button type="submit" class="button button-small"><?php echo esc_html($isEnabled ? __('Disable', 'xerotact-shield-x-security') : __('Enable', 'xerotact-shield-x-security')); ?></button>
                                                </form>
                                            <?php endif; ?>
                                        </div>
                                    </td>
                                </tr>
                            <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>
            <?php endif; ?>
        </section>
        <?php
        $this->closePage();
    }

    private function renderInactiveExtensionsCatalog(bool $licenseActive = false): void
    {
        $title = $licenseActive
            ? __('Private Extensions companion plugin is not active', 'xerotact-shield-x-security')
            : __('Activate SHIELD-X to use private extensions', 'xerotact-shield-x-security');
        $description = $licenseActive
            ? __('The WordPress.org SHIELD-X package stays clean and does not upload, install, update, or delete executable extension code. Private extensions are delivered by one companion plugin and appear here after it is installed and activated.', 'xerotact-shield-x-security')
            : __('Private extensions are not loaded while SHIELD-X is not activated. Review the available extension cards below; live extension pages, shortcuts, scheduled tasks, and management actions unlock only after activation.', 'xerotact-shield-x-security');
        $pill = $licenseActive
            ? __('Companion plugin required', 'xerotact-shield-x-security')
            : __('Activation required', 'xerotact-shield-x-security');
        ?>
        <div class="shieldx-extension-catalog">
            <div class="shieldx-extension-intro">
                <div>
                    <h3><?php echo esc_html($title); ?></h3>
                    <p><?php echo esc_html($description); ?></p>
                </div>
                <span class="shieldx-status-pill shieldx-status-pill--warn"><?php echo esc_html($pill); ?></span>
            </div>

            <div class="shieldx-section-head shieldx-extension-catalog-head">
                <div>
                    <h3><?php echo esc_html__('Available private extensions', 'xerotact-shield-x-security'); ?></h3>
                    <span><?php echo esc_html($licenseActive ? __('These cards are informational until the private Extensions companion plugin is active.', 'xerotact-shield-x-security') : __('These cards are informational until SHIELD-X is activated.', 'xerotact-shield-x-security')); ?></span>
                </div>
            </div>

            <div class="shieldx-module-grid shieldx-extension-card-grid">
                <?php foreach ($this->availableExtensionCatalog() as $extension) : ?>
                    <article class="shieldx-module-card shieldx-extension-card">
                        <div class="shieldx-module-copy">
                            <strong>
                                <?php echo wp_kses(Iconoir::svg((string) $extension['icon'], 'shieldx-icon shieldx-extension-card-icon'), $this->allowedIconSvg()); ?>
                                <?php echo esc_html((string) $extension['name']); ?>
                            </strong>
                            <em><?php echo esc_html((string) $extension['summary']); ?></em>
                            <span><?php echo esc_html((string) $extension['helps']); ?></span>
                        </div>
                        <span class="shieldx-status-pill shieldx-status-pill--info"><?php echo esc_html((string) $extension['type']); ?></span>
                    </article>
                <?php endforeach; ?>
            </div>
        </div>
        <?php
    }

    /**
     * @return array<int,array{name:string,icon:string,summary:string,helps:string,type:string}>
     */
    private function availableExtensionCatalog(): array
    {
        return [
            [
                'name' => __('SHIELD-X Notifications', 'xerotact-shield-x-security'),
                'icon' => 'mail',
                'summary' => __('Email alert profiles, immediate critical alerts, and accumulated digest reports.', 'xerotact-shield-x-security'),
                'helps' => __('Helps admins receive the right security signal without noisy one-email-per-event behavior.', 'xerotact-shield-x-security'),
                'type' => __('Alerts', 'xerotact-shield-x-security'),
            ],
            [
                'name' => __('SHIELD-X Pulse', 'xerotact-shield-x-security'),
                'icon' => 'activity',
                'summary' => __('Security trend visibility and site signal summaries for the dashboard.', 'xerotact-shield-x-security'),
                'helps' => __('Helps spot changes in WAF, login, scanner, and system activity from one place.', 'xerotact-shield-x-security'),
                'type' => __('Monitoring', 'xerotact-shield-x-security'),
            ],
            [
                'name' => __('SHIELD-X Recovery', 'xerotact-shield-x-security'),
                'icon' => 'lifebelt',
                'summary' => __('Emergency recovery tooling designed to stay independent from blocking rules.', 'xerotact-shield-x-security'),
                'helps' => __('Helps recover access after a misconfiguration, lockout, or overly strict protection rule.', 'xerotact-shield-x-security'),
                'type' => __('Recovery', 'xerotact-shield-x-security'),
            ],
            [
                'name' => __('SHIELD-X Scheduler', 'xerotact-shield-x-security'),
                'icon' => 'calendar',
                'summary' => __('Central scheduling controls, missed-task visibility, and WP-Cron/system-cron guidance.', 'xerotact-shield-x-security'),
                'helps' => __('Helps scheduled scans, backups, and feed refreshes run reliably on low-traffic sites.', 'xerotact-shield-x-security'),
                'type' => __('Automation', 'xerotact-shield-x-security'),
            ],
            [
                'name' => __('SHIELD-X Updates', 'xerotact-shield-x-security'),
                'icon' => 'clock-rotate-right',
                'summary' => __('Private extension update visibility and signed package replacement checks.', 'xerotact-shield-x-security'),
                'helps' => __('Helps keep the private Extensions companion plugin current without adding updater code to the public package.', 'xerotact-shield-x-security'),
                'type' => __('Updates', 'xerotact-shield-x-security'),
            ],
        ];
    }

    private function renderSetupModePanel(): void
    {
        $profiles = SetupModeManager::profiles();
        $manager = new SetupModeManager($this->plugin);
        $previews = [];
        foreach ($profiles as $mode => $profile) {
            if (!is_array($profile)) {
                continue;
            }
            $previews[(string) $mode] = $manager->preview((string) $mode);
        }
        $lastApplied = SetupModeManager::lastApplied();
        $lastMode = (string) ($lastApplied['mode'] ?? '');
        $lastLabel = $lastMode !== '' ? (string) (SetupModeManager::profile($lastMode)['label'] ?? $lastMode) : '';
        ?>
        <section id="shieldx-auto-setup-modes" class="shieldx-panel shieldx-panel--full shieldx-setup-modes" data-shieldx-accordion-skip>
            <div class="shieldx-section-head">
                <div>
                    <h2><?php echo esc_html__('Auto Setup Mode', 'xerotact-shield-x-security'); ?></h2>
                    <span><?php echo esc_html__('Apply a safe baseline profile in one guided step', 'xerotact-shield-x-security'); ?></span>
                </div>
                <?php if ($lastLabel !== '') : ?>
                    <span>
                        <?php
                        echo esc_html(sprintf(
                            /* translators: 1: setup profile label, 2: applied time. */
                            __('Last applied: %1$s at %2$s', 'xerotact-shield-x-security'),
                            $lastLabel,
                            $this->formatTime((string) ($lastApplied['applied_at'] ?? ''))
                        ));
                        ?>
                    </span>
                <?php endif; ?>
            </div>
            <div class="shieldx-setup-mode-grid">
                <?php foreach ($profiles as $mode => $profile) : ?>
                    <?php $this->renderSetupModeCard((string) $mode, is_array($profile) ? $profile : []); ?>
                <?php endforeach; ?>
            </div>
        </section>
        <?php foreach ($profiles as $mode => $profile) : ?>
            <?php
            $this->renderSetupModeModal(
                (string) $mode,
                is_array($profile) ? $profile : [],
                isset($previews[(string) $mode]) && is_array($previews[(string) $mode]) ? $previews[(string) $mode] : []
            );
            ?>
        <?php endforeach; ?>
        <?php
    }

    /**
     * @param array<string,mixed> $profile
     */
    private function renderSetupModeCard(string $mode, array $profile): void
    {
        $tone = (string) ($profile['tone'] ?? 'info');
        ?>
        <article class="shieldx-setup-mode-card shieldx-setup-mode-card--<?php echo esc_attr($tone); ?>">
            <span class="shieldx-setup-mode-kicker"><?php echo esc_html((string) ($profile['headline'] ?? '')); ?></span>
            <h3><?php echo esc_html((string) ($profile['label'] ?? $mode)); ?></h3>
            <p><?php echo esc_html((string) ($profile['summary'] ?? '')); ?></p>
            <button
                type="button"
                class="button"
                data-shieldx-form-modal-open
                aria-controls="<?php echo esc_attr('shieldx-setup-mode-' . $mode); ?>"
            >
                <?php echo esc_html((string) ($profile['button'] ?? __('Review & apply', 'xerotact-shield-x-security'))); ?>
            </button>
        </article>
        <?php
    }

    /**
     * @param array<string,mixed> $profile
     * @param array<string,mixed> $preview
     */
    private function renderSetupModeModal(string $mode, array $profile, array $preview): void
    {
        $modalId = 'shieldx-setup-mode-' . $mode;
        $titleId = $modalId . '-title';
        $descId = $modalId . '-desc';
        $changes = isset($preview['will_change']) && is_array($preview['will_change']) ? $preview['will_change'] : [];
        $preserved = isset($preview['will_preserve']) && is_array($preview['will_preserve']) ? $preview['will_preserve'] : [];
        $warnings = isset($preview['warnings']) && is_array($preview['warnings']) ? $preview['warnings'] : [];
        ?>
        <div id="<?php echo esc_attr($modalId); ?>" class="shieldx-form-modal shieldx-setup-mode-modal" data-shieldx-form-modal hidden>
            <div class="shieldx-form-modal-backdrop" data-shieldx-form-modal-close></div>
            <section class="shieldx-form-modal-card" role="dialog" aria-modal="true" aria-labelledby="<?php echo esc_attr($titleId); ?>" aria-describedby="<?php echo esc_attr($descId); ?>">
                <button type="button" class="shieldx-modal-close" data-shieldx-form-modal-close aria-label="<?php echo esc_attr__('Close dialog', 'xerotact-shield-x-security'); ?>">
                    <span aria-hidden="true">&times;</span>
                </button>
                <p class="shieldx-modal-kicker"><?php echo esc_html__('Review before applying', 'xerotact-shield-x-security'); ?></p>
                <h2 id="<?php echo esc_attr($titleId); ?>">
                    <?php
                    echo esc_html(sprintf(
                        /* translators: %s is the setup mode label. */
                        __('Apply %s', 'xerotact-shield-x-security'),
                        (string) ($profile['label'] ?? $mode)
                    ));
                    ?>
                </h2>
                <p id="<?php echo esc_attr($descId); ?>" class="shieldx-muted">
                    <?php echo esc_html__('This preview is calculated from the current settings. SHIELD-X applies only the managed baseline and keeps the site-specific controls below unchanged.', 'xerotact-shield-x-security'); ?>
                </p>
                <div class="shieldx-setup-mode-preview">
                    <section>
                        <h3><?php echo esc_html__('Will change', 'xerotact-shield-x-security'); ?></h3>
                        <?php foreach ($changes as $group => $items) : ?>
                            <p class="shieldx-setup-mode-preview-group"><?php echo esc_html((string) $group); ?></p>
                            <ul>
                                <?php foreach ((array) $items as $item) : ?>
                                    <li><?php echo esc_html((string) $item); ?></li>
                                <?php endforeach; ?>
                            </ul>
                        <?php endforeach; ?>
                    </section>
                    <section>
                        <h3><?php echo esc_html__('Will preserve', 'xerotact-shield-x-security'); ?></h3>
                        <?php foreach ($preserved as $group => $items) : ?>
                            <p class="shieldx-setup-mode-preview-group"><?php echo esc_html((string) $group); ?></p>
                            <ul>
                                <?php foreach ((array) $items as $item) : ?>
                                    <li><?php echo esc_html((string) $item); ?></li>
                                <?php endforeach; ?>
                            </ul>
                        <?php endforeach; ?>
                    </section>
                </div>
                <?php if ($warnings !== []) : ?>
                    <ul class="shieldx-setup-mode-warnings" role="alert">
                        <?php foreach ($warnings as $warning) : ?>
                            <li><?php echo esc_html((string) $warning); ?></li>
                        <?php endforeach; ?>
                    </ul>
                <?php endif; ?>
                <form class="shieldx-modal-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                    <input type="hidden" name="action" value="shieldx_apply_setup_mode">
                    <input type="hidden" name="setup_mode" value="<?php echo esc_attr($mode); ?>">
                    <?php wp_nonce_field('shieldx_apply_setup_mode'); ?>
                    <label class="shieldx-check-row">
                        <input type="checkbox" name="create_restore_point" value="1" checked>
                        <span><?php echo esc_html__('Create a restore point before applying when possible', 'xerotact-shield-x-security'); ?></span>
                    </label>
                    <?php if ($mode === SetupModeManager::MODE_PANIC) : ?>
                        <label class="shieldx-check-row shieldx-check-row--danger">
                            <input type="checkbox" name="apply_lockdown_now" value="1">
                            <span><?php echo esc_html__('Also apply filesystem Lockdown now. Leave unchecked to keep the existing Lockdown policy unchanged.', 'xerotact-shield-x-security'); ?></span>
                        </label>
                    <?php endif; ?>
                    <div class="shieldx-modal-actions">
                        <button type="button" class="button" data-shieldx-form-modal-close><?php echo esc_html__('Cancel', 'xerotact-shield-x-security'); ?></button>
                        <button type="submit" class="button button-primary">
                            <?php
                            echo esc_html(sprintf(
                                /* translators: %s is the setup mode label. */
                                __('Apply %s', 'xerotact-shield-x-security'),
                                (string) ($profile['label'] ?? $mode)
                            ));
                            ?>
                        </button>
                    </div>
                </form>
            </section>
        </div>
        <?php
    }

    private function renderLostAuthenticatorSetupDisclosure(array $loginSecurity, array $userState, array $adminUsers = []): void
    {
        $panelId = 'shieldx-lost-authenticator-setup-panel';
        ?>
        <article class="shieldx-setup-step shieldx-setup-recovery-callout">
            <div>
                <h2><?php echo esc_html__('Lost access to Authenticator?', 'xerotact-shield-x-security'); ?></h2>
                <p><?php echo esc_html__('Open recovery paths before continuing setup if the mobile authenticator entry was deleted or the device is unavailable.', 'xerotact-shield-x-security'); ?></p>
            </div>
            <button
                type="button"
                class="button shieldx-disclosure-button"
                data-shieldx-disclosure-toggle
                aria-expanded="false"
                aria-controls="<?php echo esc_attr($panelId); ?>"
            >
                <?php echo esc_html__('Open recovery options', 'xerotact-shield-x-security'); ?>
            </button>
        </article>
        <div id="<?php echo esc_attr($panelId); ?>" class="shieldx-setup-recovery-panel" data-shieldx-disclosure-panel hidden>
            <?php $this->renderLostAuthenticatorPanel($loginSecurity, $userState, true, $adminUsers); ?>
        </div>
        <?php
    }

    public function renderScan(): void
    {
        if ($this->renderAsyncPageShell('shield-x-scan')) {
            return;
        }

        $status = $this->statusForPage();
        $scanner = isset($status['scanner']) && is_array($status['scanner']) ? $status['scanner'] : [];
        $scanRepository = ScanRepository::fromWordPress();
        $issues = $scanRepository->activeIssues(250);
        if (empty($issues) && isset($scanner['issues']) && is_array($scanner['issues'])) {
            $issues = $scanner['issues'];
        }
        $fileIssues = $this->filterScanIssuesForSection($issues, ScheduledScanSettings::TYPE_FILE);
        $databaseIssues = $this->filterScanIssuesForSection($issues, ScheduledScanSettings::TYPE_DATABASE);
        $vulnerabilityIssues = $this->filterScanIssuesForSection($issues, ScheduledScanSettings::TYPE_VULNERABILITY);
        $quarantinedIssues = isset($scanner['quarantined_issues']) && is_array($scanner['quarantined_issues']) ? $scanner['quarantined_issues'] : [];
        $ignored = isset($scanner['ignored']) && is_array($scanner['ignored']) ? $scanner['ignored'] : ['paths' => [], 'rules' => []];
        $scanRuns = isset($scanner['scan_runs']) && is_array($scanner['scan_runs']) ? $scanner['scan_runs'] : [];
        $lastScan = isset($scanner['last_scan']) && is_array($scanner['last_scan']) ? $scanner['last_scan'] : null;
        $scanScopes = isset($scanner['scopes']) && is_array($scanner['scopes']) ? $scanner['scopes'] : [];
        $scanSchedule = isset($scanner['schedule']) && is_array($scanner['schedule']) ? $scanner['schedule'] : (new ScheduledScanSettings())->all();
        $vulnerability = isset($scanner['vulnerability']) && is_array($scanner['vulnerability']) ? $scanner['vulnerability'] : [];
        $selectedScopes = $this->selectedScanScopesFromRequest($scanScopes);
        $fileScanScopes = $this->filterScanScopes($scanScopes, false);
        $shadowScanScopes = $this->defaultShadowScanScopes($fileScanScopes);
        $databaseScanScopes = $this->filterScanScopes($scanScopes, true);
        $fileSchedule = $this->scanScheduleForType($scanSchedule, ScheduledScanSettings::TYPE_FILE);
        $databaseSchedule = $this->scanScheduleForType($scanSchedule, ScheduledScanSettings::TYPE_DATABASE);
        $vulnerabilitySchedule = $this->scanScheduleForType($scanSchedule, ScheduledScanSettings::TYPE_VULNERABILITY);
        $selectedScheduledFileScopes = $this->selectedScanScopesFromSettings($fileScanScopes, isset($fileSchedule['scopes']) && is_array($fileSchedule['scopes']) ? $fileSchedule['scopes'] : []);
        $selectedFileScopes = array_values(array_filter($selectedScopes, static function (string $scope): bool {
            return $scope !== 'database';
        }));
        if (empty($selectedFileScopes)) {
            $selectedFileScopes = array_values(array_map(static function (array $scope): string {
                return (string) ($scope['id'] ?? '');
            }, $fileScanScopes));
        }
        $scanDetail = null;
        $requestedScanId = $this->requestedScanId();
        if ($requestedScanId !== '') {
            $scanDetail = $scanRepository->scanById($requestedScanId);
        }
        $coreVerifier = CoreFileVerifier::fromWordPress();
        $coreState = $coreVerifier->state();
        $coreReadyForScan = $this->officialCoreReadyForScan($coreState);
        $pluginVerifier = PluginFileVerifier::fromWordPress();
        $pluginPackageState = $pluginVerifier->state();
        $comparePath = $this->requestedCoreComparePath();
        $coreCompare = null;
        $pluginComparePath = $this->requestedPluginComparePath();
        $pluginCompare = null;
        $protectedPath = $this->requestedProtectedFilePath();
        $protectedReview = $protectedPath !== '' ? $this->buildProtectedFileRepair($protectedPath, $coreVerifier) : null;
        $reviewPath = $this->requestedReviewFilePath();
        $virtualReview = null;
        $virtualReviewKey = $this->requestedVirtualReviewKey();
        if ($virtualReviewKey !== '') {
            $virtualReview = $this->buildVirtualScannerReview($virtualReviewKey);
        }
        if ($reviewPath !== '' && $this->isVirtualScannerPath($reviewPath)) {
            $virtualReview = $virtualReview ?: $this->buildVirtualScannerReview($reviewPath);
            $reviewPath = '';
        }
        $fileReview = $reviewPath !== '' ? $this->buildFileReview($reviewPath) : null;
        $databaseRemediation = DatabaseRemediation::fromWordPress();
        $databaseReviewThreatId = $this->requestedDatabaseReviewThreatId();
        $databaseReview = $databaseReviewThreatId !== '' ? $databaseRemediation->previewIssue($databaseReviewThreatId) : null;
        $databaseBackups = $databaseRemediation->recentBackups(8);
        $scannerRemediation = ScannerRemediation::fromWordPress();
        $quarantineHistory = $scannerRemediation->quarantineHistory(12);

        if ($comparePath !== '') {
            try {
                if (empty($coreState['prepared'])) {
                    $coreState = $coreVerifier->prepareRelease();
                }

                $coreCompare = $coreVerifier->compareFile($comparePath);
                if (!empty($coreCompare['matches'])) {
                    $resolved = ScanRepository::fromWordPress()->resolveVerifiedCoreFileIssues(
                        (string) ($coreCompare['path'] ?? $comparePath),
                        function_exists('get_current_user_id') ? (int) get_current_user_id() : 0
                    );
                    $coreCompare['resolved_scanner_issues'] = $resolved;

                    if ($reviewPath !== '' && (string) ($coreCompare['path'] ?? '') === $reviewPath) {
                        $fileReview = $this->buildFileReview($reviewPath);
                    }
                }
            } catch (\Throwable $error) {
                $coreCompare = [
                    'path' => $comparePath,
                    'error' => $error->getMessage(),
                ];
            }
        }

        if ($pluginComparePath !== '') {
            try {
                $pluginCompare = $pluginVerifier->compareFile($pluginComparePath);
            } catch (\Throwable $error) {
                $pluginCompare = [
                    'path' => $pluginComparePath,
                    'error' => $error->getMessage(),
                ];
            }
        }

        $this->openPage('shield-x-scan', $status);
        ?>
        <section class="shieldx-metrics" aria-label="<?php echo esc_attr__('SHIELD-X scan summary', 'xerotact-shield-x-security'); ?>">
            <?php
            $this->renderMetric(__('Active issues', 'xerotact-shield-x-security'), (string) ($scanner['active_issues'] ?? 0), !empty($scanner['active_issues']) ? 'warn' : 'good', 'message-alert');
            $this->renderMetric(__('Tracked files', 'xerotact-shield-x-security'), (string) ($scanner['tracked_files'] ?? 0), 'info', 'database-stats');
            $lastScanLabel = $lastScan ? (string) ($lastScan['files_scanned'] ?? 0) . ' files' : __('Never', 'xerotact-shield-x-security');
            if ($lastScan && !empty($lastScan['database_selected'])) {
                $lastScanLabel .= ' + ' . (string) ($lastScan['database_items_scanned'] ?? 0) . ' DB';
            }
            $this->renderMetric(__('Last scan', 'xerotact-shield-x-security'), $lastScanLabel, $lastScan ? 'info' : 'warn', 'cube-scan');
            $this->renderMetric(__('Skipped', 'xerotact-shield-x-security'), $lastScan ? (string) ($lastScan['files_skipped'] ?? 0) : '0', 'info', 'database');
            $this->renderMetric(__('Vulnerabilities', 'xerotact-shield-x-security'), (string) ($vulnerability['issues_found'] ?? 0), !empty($vulnerability['issues_found']) ? 'warn' : 'good', 'message-alert');
            ?>
        </section>

        <?php $this->renderScanTypeSelector($scanner, $lastScan, $vulnerability); ?>

        <div class="shieldx-layout shieldx-layout--single">
            <section id="shieldx-file-scanner" class="shieldx-panel shieldx-panel--full" data-shieldx-scan-panel="file" role="tabpanel" aria-labelledby="shieldx-scan-tab-file">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('File Scanner', 'xerotact-shield-x-security'); ?></h2>
                    <span><?php echo esc_html__('Smart modified-time, hash, and malware signature checks', 'xerotact-shield-x-security'); ?></span>
                </div>
                <div class="shieldx-scan-actions">
                    <div class="shieldx-scan-summary-grid">
                        <dl class="shieldx-detail-list shieldx-file-scan-status-card">
                            <?php $this->renderDetail(__('Mode', 'xerotact-shield-x-security'), $this->formatLabel((string) ($scanner['mode'] ?? 'local_quick_scan'))); ?>
                            <?php $this->renderDetail(__('Smart scan', 'xerotact-shield-x-security'), __('Modified time, then hash, then content rules.', 'xerotact-shield-x-security')); ?>
                            <?php $this->renderDetail(__('Last started', 'xerotact-shield-x-security'), $lastScan ? $this->formatTime((string) ($lastScan['started_at'] ?? '')) : __('No scan has run yet.', 'xerotact-shield-x-security')); ?>
                            <?php $this->renderDetail(__('Last scope', 'xerotact-shield-x-security'), $lastScan ? (string) ($lastScan['scope_label'] ?? 'Full site baseline') : __('Not selected yet.', 'xerotact-shield-x-security')); ?>
                            <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                            <?php $this->renderDetail(__('Last file result', 'xerotact-shield-x-security'), $lastScan ? sprintf(__('%1$s scanned, %2$s skipped, %3$s hash checks, %4$s issues in run', 'xerotact-shield-x-security'), (string) ($lastScan['files_scanned'] ?? 0), (string) ($lastScan['files_skipped'] ?? 0), (string) ($lastScan['files_hash_checked'] ?? 0), (string) ($lastScan['threats_found'] ?? 0)) : __('Pending first run', 'xerotact-shield-x-security')); ?>
                        </dl>
                        <div id="shieldx-plugin-compare" class="shieldx-core-summary-card shieldx-core-summary-card--plugin">
                            <div class="shieldx-section-head shieldx-section-head--compact">
                                <h3><?php echo esc_html__('Official Plugin Compare', 'xerotact-shield-x-security'); ?></h3>
                                <span><?php echo esc_html__('Clean WordPress.org plugin baselines', 'xerotact-shield-x-security'); ?></span>
                            </div>
                            <?php $this->renderPluginVerifierPanel($pluginPackageState); ?>
                        </div>
                        <div id="shieldx-core-compare" class="shieldx-core-summary-card shieldx-core-summary-card--core">
                            <div class="shieldx-section-head shieldx-section-head--compact">
                                <h3><?php echo esc_html__('Official Core Compare', 'xerotact-shield-x-security'); ?></h3>
                                <span><?php echo esc_html__('Clean WordPress baseline', 'xerotact-shield-x-security'); ?></span>
                            </div>
                            <?php $this->renderCoreVerifierPanel($coreState, null, false); ?>
                        </div>
                    </div>
                    <form
                        class="shieldx-scan-form"
                        method="post"
                        action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
                        data-shieldx-scan-run-form
                        data-shieldx-progress-poll-delay="250"
                        <?php if (!$coreReadyForScan) : ?>
                            data-shieldx-official-core-prompt="1"
                            data-shieldx-official-core-prompt-key="<?php echo esc_attr('shieldx-official-core-' . (string) ($coreState['version'] ?? 'unknown')); ?>"
                        <?php endif; ?>
                    >
                        <input type="hidden" name="action" value="shieldx_run_scan">
                        <?php wp_nonce_field('shieldx_run_scan'); ?>
                        <div class="shieldx-scope-grid" aria-label="<?php echo esc_attr__('Scan directories', 'xerotact-shield-x-security'); ?>">
                            <?php foreach ($fileScanScopes as $scope) : ?>
                                <?php $this->renderScanScopeCard(is_array($scope) ? $scope : [], $selectedFileScopes); ?>
                            <?php endforeach; ?>
                        </div>
                        <button type="submit" class="button button-primary" data-shieldx-scan-run-button data-running-label="<?php echo esc_attr__('Scanning...', 'xerotact-shield-x-security'); ?>"><?php echo esc_html__('Run file scan', 'xerotact-shield-x-security'); ?></button>
                        <div class="shieldx-scan-progress" data-shieldx-scan-progress hidden aria-live="polite">
                            <span class="shieldx-scan-progress-track" role="progressbar" aria-label="<?php echo esc_attr__('File scan progress', 'xerotact-shield-x-security'); ?>">
                                <span></span>
                            </span>
                            <small><?php echo esc_html__('Scanning selected directories. Keep this tab open until results load.', 'xerotact-shield-x-security'); ?></small>
                        </div>
                    </form>
                    <?php if (!$coreReadyForScan) : ?>
                        <?php $this->renderOfficialCorePromptModal($coreState); ?>
                    <?php endif; ?>
                    <?php if ($coreCompare !== null) : ?>
                        <details id="shieldx-core-compare-result" class="shieldx-accordion-panel shieldx-accordion-panel--embedded" data-shieldx-accordion-open="1" open>
                            <summary>
                                <span><?php echo esc_html__('Official Core Compare Result', 'xerotact-shield-x-security'); ?></span>
                                <small><?php echo esc_html((string) ($coreCompare['path'] ?? __('Selected core file', 'xerotact-shield-x-security'))); ?></small>
                            </summary>
                            <div class="shieldx-accordion-body">
                                <?php $this->renderCoreCompare($coreCompare); ?>
                            </div>
                        </details>
                    <?php endif; ?>
                    <?php if ($pluginCompare !== null) : ?>
                        <details id="shieldx-plugin-compare-result" class="shieldx-accordion-panel shieldx-accordion-panel--embedded" data-shieldx-accordion-open="1" open>
                            <summary>
                                <span><?php echo esc_html__('Official Plugin Compare Result', 'xerotact-shield-x-security'); ?></span>
                                <small><?php echo esc_html((string) ($pluginCompare['path'] ?? __('Selected plugin file', 'xerotact-shield-x-security'))); ?></small>
                            </summary>
                            <div class="shieldx-accordion-body">
                                <?php $this->renderPluginCompare($pluginCompare); ?>
                            </div>
                        </details>
                    <?php endif; ?>
                </div>
            </section>

            <section id="shieldx-database-scanner" class="shieldx-panel shieldx-panel--full" data-shieldx-scan-panel="database" role="tabpanel" aria-labelledby="shieldx-scan-tab-database">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('Database Scanner', 'xerotact-shield-x-security'); ?></h2>
                    <span><?php echo esc_html__('Options, posts, widgets, cron hooks, redirects, and stored scripts', 'xerotact-shield-x-security'); ?></span>
                </div>
                <div class="shieldx-scan-actions">
                    <dl class="shieldx-detail-list">
                        <?php $this->renderDetail(__('Mode', 'xerotact-shield-x-security'), __('Database content checks', 'xerotact-shield-x-security')); ?>
                        <?php $this->renderDetail(__('Last checked', 'xerotact-shield-x-security'), ($lastScan && !empty($lastScan['database_selected'])) ? $this->formatTime((string) ($lastScan['started_at'] ?? '')) : __('No database scan has run yet.', 'xerotact-shield-x-security')); ?>
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <?php $this->renderDetail(__('Last result', 'xerotact-shield-x-security'), ($lastScan && !empty($lastScan['database_selected'])) ? sprintf(__('%1$s rows/hooks checked, %2$s database issues in run', 'xerotact-shield-x-security'), (string) ($lastScan['database_items_scanned'] ?? 0), (string) ($lastScan['database_issues_found'] ?? 0)) : __('Pending first database run', 'xerotact-shield-x-security')); ?>
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <?php $this->renderDetail(__('Backups', 'xerotact-shield-x-security'), sprintf(__('%d database repair backups stored', 'xerotact-shield-x-security'), count($databaseBackups))); ?>
                    </dl>
                    <form class="shieldx-scan-form shieldx-scan-form--compact" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" data-shieldx-scan-run-form data-shieldx-progress-poll-delay="250">
                        <input type="hidden" name="action" value="shieldx_run_scan">
                        <input type="hidden" name="scan_scopes[]" value="database">
                        <?php wp_nonce_field('shieldx_run_scan'); ?>
                        <label class="shieldx-field">
                            <span><?php echo esc_html__('Scan depth', 'xerotact-shield-x-security'); ?></span>
                            <select name="database_depth">
                                <option value="quick"><?php echo esc_html__('Quick (bounded recent and high-risk data)', 'xerotact-shield-x-security'); ?></option>
                                <option value="deep"><?php echo esc_html__('Deep (all supported database content)', 'xerotact-shield-x-security'); ?></option>
                            </select>
                        </label>
                        <?php if (!empty($databaseScanScopes)) : ?>
                            <div class="shieldx-scope-grid shieldx-scope-grid--database" aria-label="<?php echo esc_attr__('Database scan scope', 'xerotact-shield-x-security'); ?>">
                                <?php foreach ($databaseScanScopes as $scope) : ?>
                                    <?php $this->renderScanScopeCard(is_array($scope) ? $scope : [], ['database'], 'database_scope_preview'); ?>
                                <?php endforeach; ?>
                            </div>
                        <?php else : ?>
                            <p class="shieldx-empty"><?php echo esc_html__('Database checks are not available in this runtime.', 'xerotact-shield-x-security'); ?></p>
                        <?php endif; ?>
                        <button type="submit" class="button button-primary" data-shieldx-scan-run-button data-running-label="<?php echo esc_attr__('Scanning...', 'xerotact-shield-x-security'); ?>" <?php disabled(empty($databaseScanScopes)); ?>><?php echo esc_html__('Run database scan', 'xerotact-shield-x-security'); ?></button>
                        <div class="shieldx-scan-progress" data-shieldx-scan-progress hidden aria-live="polite">
                            <span class="shieldx-scan-progress-track" role="progressbar" aria-label="<?php echo esc_attr__('Database scan progress', 'xerotact-shield-x-security'); ?>">
                                <span></span>
                            </span>
                            <small><?php echo esc_html__('Checking database content and stored hooks. Keep this tab open until results load.', 'xerotact-shield-x-security'); ?></small>
                        </div>
                    </form>
                </div>
            </section>

            <section id="shieldx-vulnerability-scanner" class="shieldx-panel shieldx-panel--full" data-shieldx-scan-panel="vulnerability" role="tabpanel" aria-labelledby="shieldx-scan-tab-vulnerability">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('Vulnerability Scanner', 'xerotact-shield-x-security'); ?></h2>
                    <span><?php echo esc_html__('Core, plugin, theme update and advisory checks', 'xerotact-shield-x-security'); ?></span>
                </div>
                <?php $this->renderVulnerabilityScannerPanel($vulnerability, $vulnerabilitySchedule, $vulnerabilityIssues); ?>
            </section>

            <?php if ($virtualReview !== null) : ?>
                <section id="shieldx-vulnerability-review" class="shieldx-panel shieldx-panel--full" data-shieldx-scan-panel="vulnerability">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Vulnerability Review', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html__('Advisory details for virtual software findings', 'xerotact-shield-x-security'); ?></span>
                    </div>
                    <?php $this->renderVirtualScannerReview($virtualReview); ?>
                </section>
            <?php endif; ?>

            <?php if ($fileReview !== null) : ?>
                <section id="shieldx-file-review" class="shieldx-panel shieldx-panel--full" data-shieldx-scan-panel="file">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('File Review', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html__('Read-only local file preview', 'xerotact-shield-x-security'); ?></span>
                    </div>
                    <?php $this->renderFileReview($fileReview); ?>
                </section>
            <?php endif; ?>

            <?php if ($protectedReview !== null) : ?>
                <section id="shieldx-protected-file" class="shieldx-panel shieldx-panel--full" data-shieldx-scan-panel="file">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Protected File Repair', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html__('Safe actions for files that cannot be quarantined', 'xerotact-shield-x-security'); ?></span>
                    </div>
                    <?php $this->renderProtectedFileRepair($protectedReview); ?>
                </section>
            <?php endif; ?>

            <?php if ($databaseReview !== null) : ?>
                <section id="shieldx-db-review" class="shieldx-panel shieldx-panel--full" data-shieldx-scan-panel="database">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Database Review', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html__('Backed-up database repair actions', 'xerotact-shield-x-security'); ?></span>
                    </div>
                    <?php $this->renderDatabaseReview($databaseReview); ?>
                </section>
            <?php endif; ?>

            <?php $this->renderActiveIssuePanel(ScheduledScanSettings::TYPE_FILE, __('File Issues', 'xerotact-shield-x-security'), $fileIssues, $coreVerifier, $pluginVerifier, $scannerRemediation, 'shieldx-bulk-scan-form-file', true); ?>
            <?php $this->renderActiveIssuePanel(ScheduledScanSettings::TYPE_DATABASE, __('Database Issues', 'xerotact-shield-x-security'), $databaseIssues, $coreVerifier, $pluginVerifier, $scannerRemediation, 'shieldx-bulk-scan-form-database', false); ?>
            <?php $this->renderActiveIssuePanel(ScheduledScanSettings::TYPE_VULNERABILITY, __('Vulnerability Issues', 'xerotact-shield-x-security'), $vulnerabilityIssues, $coreVerifier, $pluginVerifier, $scannerRemediation, 'shieldx-bulk-scan-form-vulnerability', false); ?>

            <details class="shieldx-panel shieldx-panel--full shieldx-accordion-panel" data-shieldx-scan-panel="file">
                <summary>
                    <span><?php echo esc_html__('Remediation & Ignore Controls', 'xerotact-shield-x-security'); ?></span>
                    <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                    <small><?php echo esc_html(sprintf(__('%1$d quarantined, %2$d ignored items', 'xerotact-shield-x-security'), count($quarantinedIssues), count((array) ($ignored['paths'] ?? [])) + count((array) ($ignored['rules'] ?? [])))); ?></small>
                </summary>
                <div class="shieldx-accordion-body">
                    <?php $this->renderScannerRemediationPanel($quarantinedIssues, $ignored, $quarantineHistory); ?>
                </div>
            </details>

            <?php if ($requestedScanId !== '') : ?>
                <details class="shieldx-panel shieldx-panel--full shieldx-accordion-panel" data-shieldx-accordion-open="1" open>
                    <summary>
                        <span><?php echo esc_html__('Scan Run Detail', 'xerotact-shield-x-security'); ?></span>
                        <small><?php echo esc_html($requestedScanId); ?></small>
                    </summary>
                    <div class="shieldx-accordion-body">
                    <?php if ($scanDetail === null) : ?>
                        <p class="shieldx-empty"><?php echo esc_html__('Scan run was not found.', 'xerotact-shield-x-security'); ?></p>
                    <?php else : ?>
                        <?php $this->renderScanDetail($scanDetail); ?>
                    <?php endif; ?>
                    </div>
                </details>
            <?php endif; ?>

            <details class="shieldx-panel shieldx-panel--full shieldx-accordion-panel" data-shieldx-scan-panel="file">
                <summary>
                    <span><?php echo esc_html__('Recent Scan Runs', 'xerotact-shield-x-security'); ?></span>
                    <small><?php echo esc_html__('Last five quick scans', 'xerotact-shield-x-security'); ?></small>
                </summary>
                <div class="shieldx-accordion-body">
                <?php if (empty($scanRuns)) : ?>
                    <p class="shieldx-empty"><?php echo esc_html__('No scan history yet.', 'xerotact-shield-x-security'); ?></p>
                <?php else : ?>
                    <div class="shieldx-compat-table-wrap">
                        <table class="widefat striped shieldx-scan-table">
                            <thead>
                                <tr>
                                    <th><?php echo esc_html__('Started', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Status', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Scope', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Files', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Skipped', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Issues', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Scan ID', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Action', 'xerotact-shield-x-security'); ?></th>
                                </tr>
                            </thead>
                            <tbody>
                                <?php foreach ($scanRuns as $scan) : ?>
                                    <?php $this->renderScanRunRow(is_array($scan) ? $scan : []); ?>
                                <?php endforeach; ?>
                            </tbody>
                        </table>
                    </div>
                <?php endif; ?>
                </div>
            </details>
        </div>
        <?php
        $this->closePage();
    }

    public function renderBackup(): void
    {
        if ($this->renderAsyncPageShell('shield-x-backup')) {
            return;
        }

        $status = $this->statusForPage();
        $maintenance = isset($status['maintenance']) && is_array($status['maintenance']) ? $status['maintenance'] : [];
        $scanner = isset($status['scanner']) && is_array($status['scanner']) ? $status['scanner'] : [];
        $shadowManager = FileShadowCopyManager::fromWordPress();
        $scanRepository = ScanRepository::fromWordPress();
        $shadow = isset($maintenance['file_shadow_copies']) && is_array($maintenance['file_shadow_copies']) ? $maintenance['file_shadow_copies'] : $shadowManager->summary(12);
        $shadowPendingCount = (int) ($shadow['pending'] ?? 0);
        $shadowComparePath = $this->requestedShadowComparePath();
        $shadowCompare = $shadowComparePath !== '' ? $shadowManager->compareFile($shadowComparePath) : null;
        $shadowScanCompleted = $this->queryFlag('shieldx_shadow_scanned');
        $shadowScanId = $this->queryText('shieldx_shadow_scan');
        if (!$shadowScanCompleted && $shadowScanId === '') {
            $lastAdminShadowScan = AdminChangeShadowScanner::lastResult();
            if ((int) ($lastAdminShadowScan['changes'] ?? 0) > 0 && (string) ($lastAdminShadowScan['scan_id'] ?? '') !== '') {
                $shadowScanCompleted = true;
                $shadowScanId = (string) $lastAdminShadowScan['scan_id'];
            }
        }
        $shadowScan = null;
        if ($shadowScanCompleted && $shadowScanId !== '') {
            $shadowScan = $scanRepository->scanById($shadowScanId);
        }
        if (!$shadowScan && $shadowScanCompleted && isset($scanner['last_scan']) && is_array($scanner['last_scan'])) {
            $shadowScan = $scanner['last_scan'];
        }
        $shadowSessionItems = [];
        $shadowSessionPendingCount = 0;
        $shadowSessionChangedCount = 0;
        if ($shadowScanCompleted && $shadowScanId !== '') {
            $shadowSessionAllItems = $this->filterShadowChangedItems($shadowManager->allChangedForScan($shadowScanId));
            $shadowSessionPendingCount = count($shadowSessionAllItems);
            $shadowSessionItems = array_slice($shadowSessionAllItems, 0, 100);
            $shadowSessionChangedCount = $shadowSessionPendingCount;
            if (is_array($shadowScan)) {
                $shadowScanItems = isset($shadowScan['shadow_changed_items']) && is_array($shadowScan['shadow_changed_items'])
                    ? $this->filterShadowChangedItems($shadowScan['shadow_changed_items'])
                    : [];
                $shadowSessionChangedCount = max(
                    $shadowSessionChangedCount,
                    count($shadowScanItems),
                    (int) ($shadowScan['shadow_changes_pending'] ?? 0),
                    (int) ($shadowScan['shadow_changed_count'] ?? 0)
                );
            }
        } elseif ($shadowScanCompleted && is_array($shadowScan) && isset($shadowScan['shadow_changed_items']) && is_array($shadowScan['shadow_changed_items'])) {
            $shadowSessionItems = $this->filterShadowChangedItems($shadowScan['shadow_changed_items']);
            $shadowSessionPendingCount = count($shadowSessionItems);
            $shadowSessionChangedCount = $shadowSessionPendingCount;
        }
        $shadowHistorySessions = $this->shadowScanHistorySessions(
            $this->filterShadowChangedItems($shadowManager->pending(100)),
            $shadowScanCompleted ? $shadowScanId : '',
            $scanRepository->recentShadowScans(12)
        );
        $scanScopes = isset($scanner['scopes']) && is_array($scanner['scopes']) ? $scanner['scopes'] : [];
        $fileScanScopes = $this->filterScanScopes($scanScopes, false);
        $manualDatabaseBackups = isset($maintenance['database_backups']['recent']) && is_array($maintenance['database_backups']['recent']) ? $maintenance['database_backups']['recent'] : DatabaseBackupManager::fromWordPress()->recent(8);
        $backupSchedule = isset($maintenance['database_backups']['schedule']) && is_array($maintenance['database_backups']['schedule']) ? $maintenance['database_backups']['schedule'] : (new ScheduledBackupSettings())->all();
        $restorePointManager = RestorePointManager::fromWordPress();
        $restorePoints = isset($maintenance['restore_points']['recent']) && is_array($maintenance['restore_points']['recent']) ? $maintenance['restore_points']['recent'] : $restorePointManager->recent(8);
        $restorePointSchedule = isset($maintenance['restore_points']['schedule']) && is_array($maintenance['restore_points']['schedule']) ? $maintenance['restore_points']['schedule'] : (new ScheduledRestorePointSettings())->all();
        $backupStorage = isset($maintenance['backup_storage']) && is_array($maintenance['backup_storage']) ? $maintenance['backup_storage'] : BackupStorageManager::fromWordPress()->summary();
        $restorePointViewId = $this->requestedRestorePointViewId();
        if ($restorePointViewId === '') {
            $restorePointViewId = $this->sanitizeRestorePointId($this->queryText('shieldx_restore_created'));
        }
        $restorePointFileSearch = $this->requestedRestorePointFileSearch();
        $restorePointFilePage = $this->requestedRestorePointFilePage();
        $restorePointPreviewPath = $this->requestedRestorePointPreviewPath();
        $restorePointDetail = $restorePointViewId !== ''
            ? $restorePointManager->details($restorePointViewId, $restorePointFilePage, 15, $restorePointFileSearch)
            : null;
        $restorePointPreview = ($restorePointViewId !== '' && $restorePointPreviewPath !== '')
            ? $this->buildRestorePointFilePreview($restorePointManager->previewFile($restorePointViewId, $restorePointPreviewPath))
            : null;
        $databaseRepairBackups = DatabaseRemediation::fromWordPress()->recentBackups(8);
        $quarantinedIssues = isset($scanner['quarantined_issues']) && is_array($scanner['quarantined_issues']) ? $scanner['quarantined_issues'] : [];
        $quarantineHistory = ScannerRemediation::fromWordPress()->quarantineHistory(8);

        $this->openPage('shield-x-backup', $status);
        ?>
        <section class="shieldx-metrics" aria-label="<?php echo esc_attr__('SHIELD-X backup summary', 'xerotact-shield-x-security'); ?>">
            <?php
            $this->renderMetric(__('Session changes', 'xerotact-shield-x-security'), $shadowScanCompleted ? (string) $shadowSessionPendingCount : __('Run scan', 'xerotact-shield-x-security'), $shadowSessionPendingCount > 0 ? 'warn' : 'good', 'page-search');
            $this->renderMetric(__('Pending review', 'xerotact-shield-x-security'), (string) $shadowPendingCount, $shadowPendingCount > 0 ? 'warn' : 'good', 'database-stats');
            $this->renderMetric(__('Restore points', 'xerotact-shield-x-security'), (string) count($restorePoints), count($restorePoints) > 0 ? 'info' : 'warn', 'clock-rotate-right');
            $this->renderMetric(__('Database backups', 'xerotact-shield-x-security'), (string) count($manualDatabaseBackups), count($manualDatabaseBackups) > 0 ? 'info' : 'warn', 'database');
            $this->renderMetric(__('Repair backups', 'xerotact-shield-x-security'), (string) count($databaseRepairBackups), 'info', 'folder-settings');
            $this->renderMetric(__('Quarantined', 'xerotact-shield-x-security'), (string) count($quarantinedIssues), count($quarantinedIssues) > 0 ? 'warn' : 'good', 'lock');
            ?>
        </section>

        <div class="shieldx-layout shieldx-layout--single">
            <section class="shieldx-panel shieldx-panel--full" id="shieldx-file-shadow-copies">
                <div class="shieldx-section-head">
                    <div>
                        <h2><?php echo esc_html__('File Shadow Copies', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html__('Incremental recovery for changed files', 'xerotact-shield-x-security'); ?></span>
                    </div>
                    <?php $this->renderShadowCopyActions($fileScanScopes); ?>
                </div>
                <dl class="shieldx-detail-list shieldx-detail-list--compact">
                    <?php $this->renderDetail(__('Storage', 'xerotact-shield-x-security'), (string) ($shadow['directory'] ?? $shadowManager->shadowDirectory())); ?>
                    <?php $this->renderDetail(__('Behavior', 'xerotact-shield-x-security'), __('Metadata-first: unchanged file time and size are skipped before hashing; shadow review is created only for new or changed hashes.', 'xerotact-shield-x-security')); ?>
                    <?php if ($shadowScanCompleted && $shadowScanId !== '') : ?>
                        <?php $this->renderDetail(__('Session', 'xerotact-shield-x-security'), $shadowScanId); ?>
                    <?php endif; ?>
                    <?php if (is_array($shadowScan)) : ?>
                        <?php $this->renderDetail(__('Last shadow scan', 'xerotact-shield-x-security'), sprintf(
                            /* translators: 1: total files checked by metadata, 2: unchanged files skipped before hashing, 3: files hashed because metadata changed. */
                            __('%1$d metadata checks, %2$d skipped, %3$d hashed', 'xerotact-shield-x-security'),
                            (int) ($shadowScan['files_total'] ?? 0),
                            (int) ($shadowScan['files_skipped'] ?? 0),
                            (int) ($shadowScan['files_hash_checked'] ?? 0)
                        )); ?>
                    <?php endif; ?>
                </dl>
                <?php if ($shadowCompare !== null) : ?>
                    <?php $this->renderShadowCompare($shadowCompare); ?>
                <?php endif; ?>
                <?php if (!$shadowScanCompleted) : ?>
                    <p class="shieldx-empty"><?php echo esc_html__('Run a shadow scan to list changed files for that session. Existing trusted shadow copies stay hidden until a file hash changes.', 'xerotact-shield-x-security'); ?></p>
                <?php elseif ($shadowSessionChangedCount === 0) : ?>
                    <p class="shieldx-empty"><?php echo esc_html__('No file changes were detected in this shadow scan.', 'xerotact-shield-x-security'); ?></p>
                <?php else : ?>
                    <?php $this->renderShadowScanSessionCard($shadowScanId, is_array($shadowScan) ? $shadowScan : [], $shadowSessionItems, $shadowSessionPendingCount, $shadowSessionChangedCount); ?>
                <?php endif; ?>
                <?php if (!empty($shadowHistorySessions)) : ?>
                    <?php $shadowHistoryVisibleLimit = 3; ?>
                    <div class="shieldx-shadow-history" data-shieldx-shadow-history data-visible-step="<?php echo esc_attr((string) $shadowHistoryVisibleLimit); ?>">
                        <div class="shieldx-shadow-history-head">
                            <h3><?php echo esc_html__('Recent shadow scan history', 'xerotact-shield-x-security'); ?></h3>
                            <?php /* translators: %d: number of previous shadow scan sessions. */ ?>
                            <span><?php echo esc_html(sprintf(_n('%d previous session', '%d previous sessions', count($shadowHistorySessions), 'xerotact-shield-x-security'), count($shadowHistorySessions))); ?></span>
                        </div>
                        <?php foreach ($shadowHistorySessions as $historyIndex => $historySession) : ?>
                            <?php
                            $historyScanId = (string) ($historySession['scan_id'] ?? '');
                            $historyScan = isset($historySession['scan']) && is_array($historySession['scan']) ? $historySession['scan'] : [];
                            $historyItems = isset($historySession['items']) && is_array($historySession['items']) ? $historySession['items'] : [];
                            $historyChangedCount = max(count($historyItems), (int) ($historySession['changed_count'] ?? 0));
                            ?>
                            <div class="shieldx-shadow-history-item" data-shieldx-shadow-history-item<?php if ($historyIndex >= $shadowHistoryVisibleLimit) : ?> hidden<?php endif; ?>>
                                <?php $this->renderShadowScanSessionCard($historyScanId, $historyScan, $historyItems, count($historyItems), $historyChangedCount); ?>
                            </div>
                        <?php endforeach; ?>
                        <?php if (count($shadowHistorySessions) > $shadowHistoryVisibleLimit) : ?>
                            <div class="shieldx-shadow-history-actions">
                                <button type="button" class="button button-secondary button-small shieldx-shadow-history-more" data-shieldx-shadow-history-more data-label="<?php echo esc_attr__('Read more', 'xerotact-shield-x-security'); ?>" data-collapse-label="<?php echo esc_attr__('Show less', 'xerotact-shield-x-security'); ?>">
                                    <?php echo esc_html__('Read more', 'xerotact-shield-x-security'); ?>
                                </button>
                            </div>
                        <?php endif; ?>
                    </div>
                <?php endif; ?>
            </section>

            <section class="shieldx-panel shieldx-panel--full" id="shieldx-restore-points" data-shieldx-accordion-open="1">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('Restore Points', 'xerotact-shield-x-security'); ?></h2>
                    <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                    <span><?php echo esc_html(sprintf(__('%d recent restore points', 'xerotact-shield-x-security'), count($restorePoints))); ?></span>
                </div>
                <?php $this->renderRestorePointPanel($restorePoints, $restorePointDetail, $restorePointViewId, $restorePointPreview, $restorePointPreviewPath); ?>
            </section>

            <?php $this->renderBackupStoragePanel($backupStorage); ?>

            <section class="shieldx-panel shieldx-panel--full" id="shieldx-database-backups">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('Database Backups', 'xerotact-shield-x-security'); ?></h2>
                    <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                    <span><?php echo esc_html(sprintf(__('%d stored', 'xerotact-shield-x-security'), count($manualDatabaseBackups))); ?></span>
                </div>
                <?php $databaseBackupCleanupDefault = gmdate('Y-m-d', time() - (30 * 86400)); ?>
                <div class="shieldx-remediation-grid shieldx-remediation-grid--restore">
                    <div class="shieldx-remediation-box">
                        <h3><?php echo esc_html__('Create Database Backup', 'xerotact-shield-x-security'); ?></h3>
                        <p class="shieldx-muted"><?php echo esc_html__('Exports WordPress tables to SHIELD-X private storage before maintenance or risky remediation.', 'xerotact-shield-x-security'); ?></p>
                        <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="shieldx-form-actions" data-shieldx-backup-progress-form data-shieldx-progress-refresh-selector="#shieldx-database-backups" data-shieldx-progress-refresh-url="<?php echo esc_url(admin_url('admin.php?page=shield-x-backup')); ?>" data-shieldx-progress-running-title="<?php echo esc_attr__('Backup in progress', 'xerotact-shield-x-security'); ?>" data-shieldx-progress-running-intro="<?php echo esc_attr__('SHIELD-X is exporting database rows in small batches so large sites stay below timeout limits.', 'xerotact-shield-x-security'); ?>" data-shieldx-progress-complete-title="<?php echo esc_attr__('Backup complete', 'xerotact-shield-x-security'); ?>" data-shieldx-progress-complete-intro="<?php echo esc_attr__('The database backup finished. Open Backup to review it.', 'xerotact-shield-x-security'); ?>">
                            <input type="hidden" name="action" value="shieldx_create_database_backup">
                            <?php wp_nonce_field('shieldx_create_database_backup'); ?>
                            <button type="submit" class="button button-primary" data-shieldx-backup-progress-button data-running-label="<?php echo esc_attr__('Creating backup...', 'xerotact-shield-x-security'); ?>"><?php echo esc_html__('Create database backup', 'xerotact-shield-x-security'); ?></button>
                        </form>
                        <div class="shieldx-database-backup-cleanup">
                            <h3><?php echo esc_html__('Clean Up Database Backups', 'xerotact-shield-x-security'); ?></h3>
                            <p class="shieldx-muted"><?php echo esc_html__('Creates a fresh database backup first, then deletes local manual database backup files created before the selected date. Remote backup catalog entries are retained.', 'xerotact-shield-x-security'); ?></p>
                            <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="shieldx-form-actions shieldx-database-backup-cleanup-form" onsubmit="return confirm('<?php echo esc_js(__('SHIELD-X will create a fresh database backup first, then delete local database backup files older than the selected date. Continue?', 'xerotact-shield-x-security')); ?>');">
                                <input type="hidden" name="action" value="shieldx_delete_database_backups_before">
                                <?php wp_nonce_field('shieldx_delete_database_backups_before'); ?>
                                <label class="shieldx-database-backup-cleanup-date">
                                    <span class="shieldx-database-backup-cleanup-label"><?php echo esc_html__('Delete backups before', 'xerotact-shield-x-security'); ?></span>
                                    <input type="date" name="delete_before_date" value="<?php echo esc_attr($databaseBackupCleanupDefault); ?>" max="<?php echo esc_attr(gmdate('Y-m-d')); ?>" required>
                                </label>
                                <button type="submit" class="button button-secondary"><?php echo esc_html__('Clean backups', 'xerotact-shield-x-security'); ?></button>
                            </form>
                        </div>
                    </div>
                    <div class="shieldx-remediation-box shieldx-remediation-box--wide">
                        <h3><?php echo esc_html__('Recent Manual Backups', 'xerotact-shield-x-security'); ?></h3>
                        <?php if (empty($manualDatabaseBackups)) : ?>
                            <p class="shieldx-empty"><?php echo esc_html__('No manual database backups have been created yet.', 'xerotact-shield-x-security'); ?></p>
                        <?php else : ?>
                            <?php $manualBackupVisibleLimit = 2; ?>
                            <div class="shieldx-remediation-list shieldx-manual-backups-list" data-shieldx-manual-backups data-visible-step="<?php echo esc_attr((string) $manualBackupVisibleLimit); ?>">
                                <?php foreach ($manualDatabaseBackups as $backupIndex => $backup) : ?>
                                    <div data-shieldx-manual-backup-item<?php if ($backupIndex >= $manualBackupVisibleLimit) : ?> hidden<?php endif; ?>>
                                        <?php $this->renderManualDatabaseBackupItem(is_array($backup) ? $backup : []); ?>
                                    </div>
                                <?php endforeach; ?>
                                <?php if (count($manualDatabaseBackups) > $manualBackupVisibleLimit) : ?>
                                    <div class="shieldx-manual-backups-actions">
                                        <button type="button" class="button button-secondary button-small shieldx-manual-backups-more" data-shieldx-manual-backups-more data-label="<?php echo esc_attr__('Load more', 'xerotact-shield-x-security'); ?>" data-collapse-label="<?php echo esc_attr__('Show less', 'xerotact-shield-x-security'); ?>">
                                            <?php echo esc_html__('Load more', 'xerotact-shield-x-security'); ?>
                                        </button>
                                    </div>
                                <?php endif; ?>
                            </div>
                        <?php endif; ?>
                    </div>
                </div>
            </section>

            <section class="shieldx-panel shieldx-panel--full">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('Repair Backups & Quarantine', 'xerotact-shield-x-security'); ?></h2>
                    <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                    <span><?php echo esc_html(sprintf(__('%1$d DB repair backups, %2$d quarantined files', 'xerotact-shield-x-security'), count($databaseRepairBackups), count($quarantinedIssues))); ?></span>
                </div>
                <div class="shieldx-remediation-grid shieldx-remediation-grid--scanner">
                    <div class="shieldx-remediation-box">
                        <h3><?php echo esc_html__('Database Repair Backups', 'xerotact-shield-x-security'); ?></h3>
                        <?php if (empty($databaseRepairBackups)) : ?>
                            <p class="shieldx-empty"><?php echo esc_html__('No database repair backups have been created yet.', 'xerotact-shield-x-security'); ?></p>
                        <?php else : ?>
                            <div class="shieldx-remediation-list">
                                <?php foreach ($databaseRepairBackups as $backup) : ?>
                                    <?php $this->renderDatabaseBackupItem(is_array($backup) ? $backup : []); ?>
                                <?php endforeach; ?>
                            </div>
                        <?php endif; ?>
                    </div>
                    <div class="shieldx-remediation-box">
                        <h3><?php echo esc_html__('Quarantined Files', 'xerotact-shield-x-security'); ?></h3>
                        <?php if (empty($quarantinedIssues)) : ?>
                            <p class="shieldx-empty"><?php echo esc_html__('No files are currently in SHIELD-X quarantine.', 'xerotact-shield-x-security'); ?></p>
                        <?php else : ?>
                            <div class="shieldx-remediation-list">
                                <?php foreach ($quarantinedIssues as $issue) : ?>
                                    <?php $this->renderQuarantinedIssue((array) $issue); ?>
                                <?php endforeach; ?>
                            </div>
                        <?php endif; ?>
                    </div>
                    <div class="shieldx-remediation-box shieldx-remediation-box--wide">
                        <h3><?php echo esc_html__('Quarantine & Restore History', 'xerotact-shield-x-security'); ?></h3>
                        <?php if (empty($quarantineHistory)) : ?>
                            <p class="shieldx-empty"><?php echo esc_html__('No quarantine or restore attempts have been recorded yet.', 'xerotact-shield-x-security'); ?></p>
                        <?php else : ?>
                            <div class="shieldx-remediation-list">
                                <?php foreach ($quarantineHistory as $item) : ?>
                                    <?php $this->renderQuarantineHistoryItem(is_array($item) ? $item : []); ?>
                                <?php endforeach; ?>
                            </div>
                        <?php endif; ?>
                    </div>
                </div>
            </section>

        </div>
        <?php
        $this->closePage();
    }

    /**
     * @param array<int,mixed> $issues
     * @return array<int,array<string,mixed>>
     */
    private function filterScanIssuesForSection(array $issues, string $section): array
    {
        $section = ScheduledScanSettings::sanitizeType($section);
        $filtered = [];

        foreach ($issues as $issue) {
            if (!is_array($issue)) {
                continue;
            }

            $path = (string) ($issue['affected_path'] ?? '');
            $isDatabase = strpos($path, 'db:') === 0;
            $isVirtual = $this->isVirtualScannerPath($path);

            if ($section === ScheduledScanSettings::TYPE_DATABASE && $isDatabase) {
                $filtered[] = $issue;
            } elseif ($section === ScheduledScanSettings::TYPE_VULNERABILITY && $isVirtual) {
                $filtered[] = $issue;
            } elseif ($section === ScheduledScanSettings::TYPE_FILE && !$isDatabase && !$isVirtual) {
                $filtered[] = $issue;
            }
        }

        return $filtered;
    }

    /**
     * @param array<int,array<string,mixed>> $issues
     */
    private function renderActiveIssuePanel(string $section, string $title, array $issues, CoreFileVerifier $coreVerifier, PluginFileVerifier $pluginVerifier, ScannerRemediation $scannerRemediation, string $formId, bool $showBulkActions): void
    {
        $section = ScheduledScanSettings::sanitizeType($section);
        $emptyMessage = __('No active issues for this scanner section.', 'xerotact-shield-x-security');
        if ($section === ScheduledScanSettings::TYPE_DATABASE) {
            $emptyMessage = __('No active database scanner issues. Run a database scan to check options, posts, users, widgets, and cron hooks.', 'xerotact-shield-x-security');
        } elseif ($section === ScheduledScanSettings::TYPE_VULNERABILITY) {
            $emptyMessage = __('No active vulnerability advisories. Run a vulnerability scan to check installed components.', 'xerotact-shield-x-security');
        } elseif ($section === ScheduledScanSettings::TYPE_FILE) {
            $emptyMessage = __('No active file scanner issues. Run a file scan to check changed or suspicious files.', 'xerotact-shield-x-security');
        }
        ?>
        <details class="shieldx-panel shieldx-panel--full shieldx-accordion-panel" data-shieldx-scan-panel="<?php echo esc_attr($section); ?>">
            <summary>
                <span><?php echo esc_html($title); ?></span>
                <?php /* translators: %d: number of unresolved scanner issues in the current scan section. */ ?>
                <small><?php echo esc_html(sprintf(__('%d unresolved', 'xerotact-shield-x-security'), count($issues))); ?></small>
            </summary>
            <div class="shieldx-accordion-body">
            <?php if (empty($issues)) : ?>
                <p class="shieldx-empty"><?php echo esc_html($emptyMessage); ?></p>
            <?php else : ?>
                <div class="shieldx-bulk-scan-form">
                    <?php if ($showBulkActions) : ?>
                        <form id="<?php echo esc_attr($formId); ?>" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="shieldx-bulk-toolbar">
                            <input type="hidden" name="action" value="shieldx_bulk_scan_action">
                            <?php wp_nonce_field('shieldx_bulk_scan_action'); ?>
                            <label>
                                <span><?php echo esc_html__('Bulk action', 'xerotact-shield-x-security'); ?></span>
                                <select name="bulk_scan_action">
                                    <option value="recheck"><?php echo esc_html__('Recheck selected files', 'xerotact-shield-x-security'); ?></option>
                                    <option value="trust_current"><?php echo esc_html__('Validated & trusted', 'xerotact-shield-x-security'); ?></option>
                                    <option value="ignore_once"><?php echo esc_html__('Ignore selected once', 'xerotact-shield-x-security'); ?></option>
                                    <option value="ignore_file"><?php echo esc_html__('Ignore selected files', 'xerotact-shield-x-security'); ?></option>
                                    <option value="ignore_rule"><?php echo esc_html__('Ignore selected rules', 'xerotact-shield-x-security'); ?></option>
                                    <option value="quarantine"><?php echo esc_html__('Quarantine selected files', 'xerotact-shield-x-security'); ?></option>
                                </select>
                            </label>
                            <button type="submit" class="button button-secondary"><?php echo esc_html__('Apply to selected', 'xerotact-shield-x-security'); ?></button>
                            <span><?php echo esc_html__('Bulk action marks selected files as validated & trusted.', 'xerotact-shield-x-security'); ?></span>
                            <span data-shieldx-bulk-missing-selection hidden><?php echo esc_html__('Missing files can only be ignored.', 'xerotact-shield-x-security'); ?></span>
                        </form>
                    <?php endif; ?>
                    <div class="shieldx-compat-table-wrap">
                        <table class="widefat striped shieldx-issue-table">
                            <thead>
                                <tr>
                                    <?php if ($showBulkActions) : ?>
                                        <th class="shieldx-bulk-check-column"><span class="screen-reader-text"><?php echo esc_html__('Select', 'xerotact-shield-x-security'); ?></span></th>
                                    <?php endif; ?>
                                    <th><?php echo esc_html__('Severity', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Issue', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Path', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('First Seen', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Last Seen', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Action', 'xerotact-shield-x-security'); ?></th>
                                </tr>
                            </thead>
                            <tbody>
                                <?php foreach ($issues as $issue) : ?>
                                    <?php $this->renderIssueRow($issue, $coreVerifier, $pluginVerifier, $scannerRemediation, $formId, $showBulkActions); ?>
                                <?php endforeach; ?>
                            </tbody>
                        </table>
                    </div>
                </div>
            <?php endif; ?>
            </div>
        </details>
        <?php
    }

    public function renderRestorePoint(): void
    {
        if ($this->renderAsyncPageShell('shield-x-backup-restore')) {
            return;
        }

        // Restore manifests can be large on established sites. Ask WordPress for
        // its scoped admin allowance without changing php.ini or bypassing the
        // host's configured maximum for any other request.
        if (function_exists('wp_raise_memory_limit')) {
            wp_raise_memory_limit('admin');
        }

        // openPage() already has the shell status during an async content
        // request. Avoid loading the full Backup status a second time while a
        // large restore manifest is resident in memory.
        $status = $this->renderingAjaxContent ? [] : $this->statusForPage();
        $restorePointManager = RestorePointManager::fromWordPress();
        $restorePointViewId = $this->requestedRestorePointViewId();
        if ($restorePointViewId === '') {
            $restorePointViewId = $this->sanitizeRestorePointId($this->queryText('shieldx_restore_created'));
        }
        $restorePointFileSearch = $this->requestedRestorePointFileSearch();
        $restorePointFilePage = $this->requestedRestorePointFilePage();
        $restorePointPreviewPath = $this->requestedRestorePointPreviewPath();
        $restorePointDetail = $restorePointViewId !== ''
            ? $restorePointManager->details($restorePointViewId, $restorePointFilePage, 15, $restorePointFileSearch)
            : null;
        $restorePointPreview = ($restorePointViewId !== '' && $restorePointPreviewPath !== '')
            ? $this->buildRestorePointFilePreview($restorePointManager->previewFile($restorePointViewId, $restorePointPreviewPath))
            : null;

        $this->openPage('shield-x-backup-restore', $status);
        ?>
        <section class="shieldx-panel shieldx-panel--full shieldx-restore-point-page" data-shieldx-accordion-open="1">
            <?php if ($restorePointViewId === '') : ?>
                <p class="shieldx-empty"><?php echo esc_html__('Choose a restore point from Backup to view its contents.', 'xerotact-shield-x-security'); ?></p>
            <?php else : ?>
                <?php $this->renderRestorePointDetails($restorePointDetail, $restorePointViewId, $restorePointPreview, $restorePointPreviewPath, 'shield-x-backup-restore', false); ?>
            <?php endif; ?>
        </section>
        <?php
        $this->closePage();
    }

    public function renderWaf(): void
    {
        if ($this->renderAsyncPageShell('shield-x-waf')) {
            return;
        }

        $status = $this->statusForPage();
        $wafStats = isset($status['waf']['stats']) && is_array($status['waf']['stats']) ? $status['waf']['stats'] : [];
        $wafRules = isset($status['waf']['rules']) && is_array($status['waf']['rules']) ? $status['waf']['rules'] : [];
        $ruleHits = isset($wafStats['rule_hits']) && is_array($wafStats['rule_hits']) ? $wafStats['rule_hits'] : [];
        $wafSettings = isset($status['waf']['settings']) && is_array($status['waf']['settings']) ? $status['waf']['settings'] : [];
        $pluginWhitelist = isset($wafSettings['plugin_whitelist']) && is_array($wafSettings['plugin_whitelist']) ? array_map('strval', $wafSettings['plugin_whitelist']) : [];
        $availablePlugins = isset($status['waf']['available_plugins']) && is_array($status['waf']['available_plugins']) ? $status['waf']['available_plugins'] : [];
        $categoryCounts = isset($status['waf']['category_counts']) && is_array($status['waf']['category_counts']) ? $status['waf']['category_counts'] : [];
        $severityCounts = isset($status['waf']['severity_counts']) && is_array($status['waf']['severity_counts']) ? $status['waf']['severity_counts'] : [];
        $wafRuleLabels = $this->wafRuleLabels($wafRules);
        $lastEvents = isset($wafStats['last_events']) && is_array($wafStats['last_events']) ? $wafStats['last_events'] : [];
        $dismissedWafEventKeys = $this->wafAccessDismissedEventKeys();
        $visibleLastEvents = $this->filterDismissedWafEvents($lastEvents, $dismissedWafEventKeys);
        $wafHitVisibleLimit = 3;
        $wafHitTotal = count($visibleLastEvents);
        $wafHitFilterOptions = $this->wafHitFilterOptions($visibleLastEvents);
        $wafHitDefaultTotal = $wafHitTotal;
        $wafHitSummary = $wafHitTotal > 0
            ? sprintf(
                /* translators: %d: number of recent WAF hit rows available in this card. */
                _n('%d recent hit', '%d recent hits', $wafHitTotal, 'xerotact-shield-x-security'),
                $wafHitTotal
            )
            : __('No hits yet', 'xerotact-shield-x-security');
        $recentClients = isset($wafStats['recent_clients']) && is_array($wafStats['recent_clients']) ? $wafStats['recent_clients'] : [];
        $currentClient = $this->currentClientForWafPanel();
        $loopbackProxyWarning = WafSettings::fromWordPress()->shouldWarnForLoopbackProxyTraffic(array_merge([$currentClient], $recentClients));
        $blockingSuggestions = $this->wafBlockingSuggestions($currentClient, $recentClients, $visibleLastEvents);
        $wafMode = (string) ($wafSettings['mode'] ?? 'log_only');
        $automaticServerIps = $this->automaticWafServerIps();
        $temporaryRuleBlockMinutes = max(1, min(43200, (int) ($wafSettings['temporary_rule_block_minutes'] ?? WafSettings::DEFAULT_TEMPORARY_RULE_BLOCK_MINUTES)));
        $maxBodyInspectionBytes = max(WafSettings::MIN_MAX_BODY_INSPECTION_BYTES, min(WafSettings::MAX_MAX_BODY_INSPECTION_BYTES, (int) ($wafSettings['max_body_inspection_bytes'] ?? WafSettings::DEFAULT_MAX_BODY_INSPECTION_BYTES)));
        $anomalyThresholdEnabled = !empty($wafSettings['anomaly_threshold_enabled']);
        $anomalyChallengeThreshold = max(1, min(1000, (int) ($wafSettings['anomaly_challenge_threshold'] ?? WafSettings::DEFAULT_ANOMALY_CHALLENGE_THRESHOLD)));
        $anomalyBlockThreshold = max($anomalyChallengeThreshold, min(1000, (int) ($wafSettings['anomaly_block_threshold'] ?? WafSettings::DEFAULT_ANOMALY_BLOCK_THRESHOLD)));
        $temporaryBlocks = isset($wafSettings['temporary_blocks']) && is_array($wafSettings['temporary_blocks']) ? $wafSettings['temporary_blocks'] : [];
        $wafAccessRows = $this->wafAccessControlRows($wafSettings, $lastEvents, $wafRuleLabels, $dismissedWafEventKeys);
        $wafAccessStats = $this->wafAccessRowStats($wafAccessRows);
        $wafAccessUrl = admin_url('admin.php?page=shield-x-waf-access');
        $timeline = IncidentTimeline::fromWordPress();
        $wafTimeline = $timeline->summary(250);
        $wafTimeline['daily_summaries'] = $timeline->dailyActivitySummary(365);
        $wafTimeline['waf_summaries'] = $timeline->wafTrendSummary(365);

        $this->openPage('shield-x-waf', $status);
        ?>
        <section id="shieldx-waf-mode" class="shieldx-panel shieldx-panel--full shieldx-waf-mode-band" data-shieldx-accordion-skip data-shieldx-waf-mode-band data-shieldx-waf-mode="<?php echo esc_attr($wafMode); ?>">
            <details class="shieldx-waf-mode-disclosure" data-shieldx-waf-mode-disclosure>
                <summary class="shieldx-section-head shieldx-waf-mode-band-trigger">
                    <div>
                        <h2><?php echo esc_html__('Firewall Mode', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html__('Choose how SHIELD-X responds when a firewall rule matches.', 'xerotact-shield-x-security'); ?></span>
                    </div>
                    <span class="shieldx-waf-mode-band-state">
                        <span data-shieldx-waf-mode-label><?php echo esc_html($this->formatLabel($wafMode)); ?></span>
                        <span class="shieldx-waf-mode-band-caret" aria-hidden="true"></span>
                    </span>
                </summary>
                <div class="shieldx-waf-mode-band-grid">
                <label class="shieldx-waf-mode-band-option shieldx-waf-mode-band-option--good">
                    <input form="shieldx-waf-settings-form" type="radio" name="waf_mode" value="log_only" <?php checked($wafMode, 'log_only'); ?>>
                    <span>
                        <strong><?php echo esc_html__('Log only', 'xerotact-shield-x-security'); ?></strong>
                        <small><?php echo esc_html__('Record matches without interrupting requests.', 'xerotact-shield-x-security'); ?></small>
                    </span>
                </label>
                <label class="shieldx-waf-mode-band-option shieldx-waf-mode-band-option--warn shieldx-waf-mode-option--temporary">
                    <input form="shieldx-waf-settings-form" type="radio" name="waf_mode" value="temporary" <?php checked($wafMode, 'temporary'); ?>>
                    <span>
                        <strong><?php echo esc_html__('Temporary block', 'xerotact-shield-x-security'); ?></strong>
                        <small><?php echo esc_html__('Block-action rule matches return 403 and temporarily block that IP. Challenge-action rules still show the human challenge first.', 'xerotact-shield-x-security'); ?></small>
                        <span class="shieldx-waf-temporary-control">
                            <input form="shieldx-waf-settings-form" type="number" name="temporary_rule_block_minutes" value="<?php echo esc_attr((string) $temporaryRuleBlockMinutes); ?>" min="1" max="43200" step="1">
                            <em><?php echo esc_html__('minutes', 'xerotact-shield-x-security'); ?></em>
                        </span>
                    </span>
                </label>
                <label class="shieldx-waf-mode-band-option shieldx-waf-mode-band-option--danger">
                    <input form="shieldx-waf-settings-form" type="radio" name="waf_mode" value="enforce" <?php checked($wafMode, 'enforce'); ?>>
                    <span>
                        <strong><?php echo esc_html__('Enforce blocking (Permanent Block)', 'xerotact-shield-x-security'); ?></strong>
                        <small><?php echo esc_html__('Return 403 for matching rules set to block.', 'xerotact-shield-x-security'); ?></small>
                    </span>
                </label>
                </div>
            </details>
        </section>

        <?php if ($loopbackProxyWarning) : ?>
            <section class="shieldx-alert shieldx-alert--warning">
                <strong><?php echo esc_html__('Proxy traffic is arriving from loopback.', 'xerotact-shield-x-security'); ?></strong>
                <span><?php echo esc_html__('SHIELD-X received proxy headers, but every observed client IP is loopback. Configure the trusted client-IP header and reverse-proxy CIDR below. Until then, proxy requests do not use the automatic server-IP safety allowlist.', 'xerotact-shield-x-security'); ?></span>
            </section>
        <?php endif; ?>

        <section class="shieldx-metrics shieldx-waf-activity-metrics" aria-label="<?php echo esc_attr__('WAF activity summary', 'xerotact-shield-x-security'); ?>">
            <?php
            $this->renderMetric(__('Access rows', 'xerotact-shield-x-security'), (string) count($wafAccessRows), 'info', 'pc-firewall', '', $wafAccessUrl, __('Open list', 'xerotact-shield-x-security'));
            $this->renderMetric(__('Blocked', 'xerotact-shield-x-security'), (string) ($wafAccessStats['blocked'] ?? 0), !empty($wafAccessStats['blocked']) ? 'warn' : 'good', 'lock', '', add_query_arg('status', 'blocked', $wafAccessUrl), __('Manage list', 'xerotact-shield-x-security'));
            $this->renderMetric(__('Would block', 'xerotact-shield-x-security'), (string) ($wafAccessStats['would_block'] ?? 0), 'info', 'message-alert', '', add_query_arg('status', 'would_block', $wafAccessUrl), __('View list', 'xerotact-shield-x-security'));
            $this->renderMetric(__('Recent WAF', 'xerotact-shield-x-security'), (string) ($wafAccessStats['recent'] ?? 0), 'info', 'clock', '', add_query_arg('status', 'recent', $wafAccessUrl), __('View list', 'xerotact-shield-x-security'));
            ?>
        </section>

        <?php $this->renderWafActivityChart($wafTimeline); ?>

        <div class="shieldx-layout shieldx-layout--single shieldx-waf-latest-hits-layout">
            <section id="shieldx-waf-latest-hits" class="shieldx-panel shieldx-panel--full shieldx-waf-latest-hits-card" data-shieldx-accordion-open="1">
                <div class="shieldx-section-head">
                    <div>
                        <h2><?php echo esc_html__('Latest WAF Hits', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html($wafHitSummary); ?></span>
                    </div>
                    <?php if (!empty($visibleLastEvents)) : ?>
                        <div class="shieldx-section-head-actions shieldx-waf-latest-hits-head-actions">
                            <div class="shieldx-waf-hit-filter" data-shieldx-waf-hit-filter>
                                <button type="button" class="button shieldx-icon-button shieldx-waf-hit-filter-toggle" data-shieldx-waf-hit-filter-toggle aria-expanded="false" aria-haspopup="true" aria-label="<?php echo esc_attr__('Filter latest WAF hits', 'xerotact-shield-x-security'); ?>">
                                    <?php echo wp_kses(Iconoir::svg('filter', 'shieldx-icon'), $this->allowedIconSvg()); ?>
                                    <span class="shieldx-screen-reader-text"><?php echo esc_html__('Filter latest WAF hits', 'xerotact-shield-x-security'); ?></span>
                                </button>
                                <div class="shieldx-waf-hit-filter-menu" data-shieldx-waf-hit-filter-menu hidden>
                                    <strong><?php echo esc_html__('Show events', 'xerotact-shield-x-security'); ?></strong>
                                    <?php foreach ($wafHitFilterOptions as $filterOption) : ?>
                                        <label class="shieldx-waf-hit-filter-option">
                                            <input type="checkbox" value="<?php echo esc_attr((string) $filterOption['value']); ?>" data-shieldx-waf-hit-filter-option<?php checked(!empty($filterOption['checked'])); ?>>
                                            <span><?php echo esc_html((string) $filterOption['label']); ?></span>
                                            <small><?php echo esc_html((string) $filterOption['count']); ?></small>
                                        </label>
                                    <?php endforeach; ?>
                                </div>
                            </div>
                            <form class="shieldx-inline-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                                <input type="hidden" name="action" value="shieldx_clear_waf_hits">
                                <?php wp_nonce_field('shieldx_clear_waf_hits'); ?>
                                <button type="submit" class="button shieldx-button-danger">
                                    <?php echo esc_html__('Clear all', 'xerotact-shield-x-security'); ?>
                                </button>
                            </form>
                        </div>
                    <?php endif; ?>
                </div>
                <?php if (empty($visibleLastEvents)) : ?>
                    <p class="shieldx-empty"><?php echo esc_html__('No WAF hits recorded yet.', 'xerotact-shield-x-security'); ?></p>
                <?php else : ?>
                    <div class="shieldx-event-list shieldx-waf-latest-hits-list" data-shieldx-waf-latest-hits data-visible-step="<?php echo esc_attr((string) $wafHitVisibleLimit); ?>">
                        <?php $wafHitDefaultVisibleCount = 0; ?>
                        <?php foreach ($visibleLastEvents as $eventIndex => $event) : ?>
                            <?php
                            $event = is_array($event) ? $event : [];
                            $eventFilterCategoriesList = $this->wafHitFilterCategories($event);
                            $eventMatchesDefaultFilter = true;
                            $eventHidden = !$eventMatchesDefaultFilter || $wafHitDefaultVisibleCount >= $wafHitVisibleLimit;
                            if ($eventMatchesDefaultFilter) {
                                ++$wafHitDefaultVisibleCount;
                            }
                            $eventFilterCategories = implode(' ', $eventFilterCategoriesList);
                            ?>
                            <div data-shieldx-waf-latest-hit data-shieldx-waf-hit-categories="<?php echo esc_attr($eventFilterCategories); ?>"<?php if ($eventHidden) : ?> hidden<?php endif; ?>>
                                <?php $this->renderEventRow($event, $wafRuleLabels); ?>
                            </div>
                        <?php endforeach; ?>
                        <?php if ($wafHitTotal > $wafHitVisibleLimit) : ?>
                            <div class="shieldx-waf-latest-hits-actions"<?php if ($wafHitDefaultTotal <= $wafHitVisibleLimit) : ?> hidden<?php endif; ?>>
                                <button type="button" class="button button-secondary button-small shieldx-waf-latest-hits-more" data-shieldx-waf-latest-hits-more data-label="<?php echo esc_attr__('Read more', 'xerotact-shield-x-security'); ?>" data-collapse-label="<?php echo esc_attr__('Show less', 'xerotact-shield-x-security'); ?>">
                                    <?php echo esc_html__('Read more', 'xerotact-shield-x-security'); ?>
                                </button>
                            </div>
                        <?php endif; ?>
                        <p class="shieldx-empty shieldx-waf-latest-hits-empty" data-shieldx-waf-latest-hits-empty<?php if ($wafHitDefaultTotal > 0) : ?> hidden<?php endif; ?>><?php echo esc_html__('No WAF hits match the selected filters.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
            </section>
        </div>

        <form id="shieldx-waf-settings-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
            <input type="hidden" name="action" value="shieldx_save_waf_settings">
            <?php wp_nonce_field('shieldx_save_waf_settings'); ?>

            <div class="shieldx-layout shieldx-layout--single">
                <section class="shieldx-panel shieldx-panel--full">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Advanced Inspection', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html__('Redacted request surfaces', 'xerotact-shield-x-security'); ?></span>
                    </div>
                    <div class="shieldx-settings-grid shieldx-settings-grid--two">
                        <label class="shieldx-field">
                            <span><?php echo esc_html__('Body inspection cap', 'xerotact-shield-x-security'); ?></span>
                            <input type="number" name="max_body_inspection_bytes" value="<?php echo esc_attr((string) $maxBodyInspectionBytes); ?>" min="<?php echo esc_attr((string) WafSettings::MIN_MAX_BODY_INSPECTION_BYTES); ?>" max="<?php echo esc_attr((string) WafSettings::MAX_MAX_BODY_INSPECTION_BYTES); ?>" step="1024">
                            <small><?php echo esc_html__('SHIELD-X inspects redacted form fields and upload metadata up to this byte limit.', 'xerotact-shield-x-security'); ?></small>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="anomaly_threshold_enabled" value="1" <?php checked($anomalyThresholdEnabled); ?>>
                            <span>
                                <strong><?php echo esc_html__('Use score thresholds in enforce modes', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Optional. Combined weak WAF matches can challenge or block only when this is enabled and Firewall Mode is Temporary or Enforce.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <?php $this->renderNumberField('anomaly_challenge_threshold', __('Challenge score', 'xerotact-shield-x-security'), $anomalyChallengeThreshold, 1, 1000); ?>
                        <?php $this->renderNumberField('anomaly_block_threshold', __('Block score', 'xerotact-shield-x-security'), $anomalyBlockThreshold, 1, 1000); ?>
                    </div>
                </section>

                <section id="shieldx-waf-reputation-feed" class="shieldx-panel shieldx-panel--full">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Managed Firewall Rules', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html((string) ($status['waf']['engine_version'] ?? '')); ?></span>
                        <?php
                        $this->renderSectionSettingsLink(
                            admin_url('admin.php?page=shield-x-system#shieldx-human-challenge'),
                            __('Open WAF challenge provider settings in System / Configuration', 'xerotact-shield-x-security')
                        );
                        ?>
                    </div>
                    <div class="shieldx-rule-summary">
                        <?php $this->renderCountPills(__('Categories', 'xerotact-shield-x-security'), $categoryCounts); ?>
                        <?php $this->renderCountPills(__('Severities', 'xerotact-shield-x-security'), $severityCounts); ?>
                    </div>
                    <div class="shieldx-rule-table-wrap">
                        <table class="widefat striped shieldx-rule-table">
                            <thead>
                                <tr>
                                    <th><?php echo esc_html__('Enabled', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Rule', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Category', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Target', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Severity', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Source', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Action', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Hits', 'xerotact-shield-x-security'); ?></th>
                                </tr>
                            </thead>
                            <tbody>
                                <?php foreach ($wafRules as $rule) : ?>
                                    <?php $this->renderRuleRow(is_array($rule) ? $rule : [], $ruleHits); ?>
                                <?php endforeach; ?>
                            </tbody>
                        </table>
                    </div>
                </section>

                <section class="shieldx-panel shieldx-panel--full">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Trusted Proxy Headers', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html(empty($wafSettings['trusted_proxy_cidrs']) ? __('Disabled by default', 'xerotact-shield-x-security') : __('Proxy aware', 'xerotact-shield-x-security')); ?></span>
                    </div>
                    <div class="shieldx-settings-grid shieldx-settings-grid--two">
                        <label class="shieldx-field">
                            <span><?php echo esc_html__('Client IP header', 'xerotact-shield-x-security'); ?></span>
                            <select name="trusted_proxy_header">
                                <?php
                                $proxyHeader = (string) ($wafSettings['trusted_proxy_header'] ?? '');
                                foreach ([
                                    '' => __('Do not trust proxy headers', 'xerotact-shield-x-security'),
                                    'cf_connecting_ip' => 'CF-Connecting-IP',
                                    'x_real_ip' => 'X-Real-IP',
                                    'x_forwarded_for' => 'X-Forwarded-For',
                                ] as $value => $label) :
                                    ?>
                                    <option value="<?php echo esc_attr($value); ?>" <?php selected($proxyHeader, (string) $value); ?>><?php echo esc_html($label); ?></option>
                                <?php endforeach; ?>
                            </select>
                            <small><?php echo esc_html__('Only used when REMOTE_ADDR is inside a trusted proxy CIDR.', 'xerotact-shield-x-security'); ?></small>
                        </label>
                        <?php $this->renderTokenTextareaField('trusted_proxy_cidrs', __('Trusted proxy CIDRs', 'xerotact-shield-x-security'), $this->lineListValue($wafSettings['trusted_proxy_cidrs'] ?? []), __('Add Cloudflare, reverse proxy, or load balancer IP/CIDR ranges. Leave empty to use REMOTE_ADDR only.', 'xerotact-shield-x-security'), [], __('Add proxy IP or CIDR', 'xerotact-shield-x-security')); ?>
                    </div>
                </section>

                <section class="shieldx-panel shieldx-panel--full">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Whitelist IP', 'xerotact-shield-x-security'); ?></h2>
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <span><?php echo esc_html(sprintf(__('%1$d manual / %2$d automatic', 'xerotact-shield-x-security'), count((array) ($wafSettings['ip_allowlist'] ?? [])), count($automaticServerIps))); ?></span>
                    </div>
                    <div class="shieldx-settings-grid shieldx-settings-grid--two">
                        <?php $this->renderTokenTextareaField('ip_allowlist', __('Whitelisted IPs / ranges', 'xerotact-shield-x-security'), $this->lineListValue($wafSettings['ip_allowlist'] ?? []), __('Choose recent IPs or type an IP/CIDR range and press Enter.', 'xerotact-shield-x-security'), $blockingSuggestions['ips'], __('Search recent IPs or add CIDR', 'xerotact-shield-x-security')); ?>
                        <div class="shieldx-field shieldx-auto-server-ip-list">
                            <span><?php echo esc_html__('Automatically allowed server IPs', 'xerotact-shield-x-security'); ?></span>
                            <div class="shieldx-token-list shieldx-token-list--static">
                                <?php foreach ($automaticServerIps as $automaticServerIp) : ?>
                                    <span class="shieldx-token-chip"><span><?php echo esc_html($automaticServerIp); ?></span></span>
                                <?php endforeach; ?>
                            </div>
                            <small><?php echo esc_html__('Read-only safety allowlist for same-server traffic. These IPs are computed from the current server and are not saved into the manual whitelist. Requests carrying proxy headers are never covered by this allowlist.', 'xerotact-shield-x-security'); ?></small>
                        </div>
                    </div>
                </section>

                <section class="shieldx-panel shieldx-panel--full">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Advanced Blocking', 'xerotact-shield-x-security'); ?></h2>
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <span><?php echo esc_html(sprintf(__('%d temporary blocks', 'xerotact-shield-x-security'), count($temporaryBlocks))); ?></span>
                    </div>
                    <div class="shieldx-settings-grid shieldx-settings-grid--two">
                        <?php $this->renderTokenTextareaField('ip_blocklist', __('Blocked IPs / ranges', 'xerotact-shield-x-security'), $this->lineListValue($wafSettings['ip_blocklist'] ?? []), __('Exact IPs or CIDR ranges.', 'xerotact-shield-x-security'), $blockingSuggestions['ips'], __('Search recent IPs or add CIDR', 'xerotact-shield-x-security')); ?>
                        <?php $this->renderTokenTextareaField('country_blocklist', __('Blocked countries', 'xerotact-shield-x-security'), $this->lineListValue($wafSettings['country_blocklist'] ?? []), __('Two-letter country codes from proxy/CDN headers.', 'xerotact-shield-x-security'), $blockingSuggestions['countries'], __('Search country code or name', 'xerotact-shield-x-security'), false); ?>
                        <?php $this->renderTokenTextareaField('user_agent_blocklist', __('Blocked user agents', 'xerotact-shield-x-security'), $this->lineListValue($wafSettings['user_agent_blocklist'] ?? []), __('Substring matches, one per line.', 'xerotact-shield-x-security'), $blockingSuggestions['user_agents'], __('Search browser signatures or add text', 'xerotact-shield-x-security')); ?>
                        <?php $this->renderTokenTextareaField('referrer_blocklist', __('Blocked referrers', 'xerotact-shield-x-security'), $this->lineListValue($wafSettings['referrer_blocklist'] ?? []), __('Substring matches, one per line.', 'xerotact-shield-x-security'), $blockingSuggestions['referrers'], __('Search referrer patterns or add URL', 'xerotact-shield-x-security')); ?>
                        <?php $this->renderTokenTextareaField('hostname_blocklist', __('Blocked hostnames', 'xerotact-shield-x-security'), $this->lineListValue($wafSettings['hostname_blocklist'] ?? []), __('Matches REMOTE_HOST, SHIELD-X cached reverse DNS, or trusted server-provided hostnames.', 'xerotact-shield-x-security'), $blockingSuggestions['hostnames'], __('Search hostnames or add domain token', 'xerotact-shield-x-security')); ?>
                    </div>

                    <div class="shieldx-settings-grid shieldx-settings-grid--two">
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="hostname_resolution_enabled" value="1" <?php checked(!empty($wafSettings['hostname_resolution_enabled'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Resolve hostnames for blocking', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Optional reverse DNS lookup for public IPs only. Results are cached to reduce latency.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                    </div>

                    <div class="shieldx-client-actions">
                        <div class="shieldx-section-head shieldx-section-head--compact">
                            <h3><?php echo esc_html__('Client Shortcuts', 'xerotact-shield-x-security'); ?></h3>
                            <span><?php echo esc_html__('Current browser and last 10 WAF clients', 'xerotact-shield-x-security'); ?></span>
                        </div>
                        <div class="shieldx-client-list">
                            <?php $this->renderClientShortcutRow($currentClient, true); ?>
                            <?php foreach ($recentClients as $client) : ?>
                                <?php $this->renderClientShortcutRow(is_array($client) ? $client : [], false); ?>
                            <?php endforeach; ?>
                            <?php if (empty($recentClients)) : ?>
                                <p class="shieldx-empty"><?php echo esc_html__('No recent WAF clients have been recorded yet.', 'xerotact-shield-x-security'); ?></p>
                            <?php endif; ?>
                        </div>
                    </div>

                    <div class="shieldx-temp-blocks">
                        <div class="shieldx-temp-block-form">
                            <label class="shieldx-field">
                                <span><?php echo esc_html__('Temporary block type', 'xerotact-shield-x-security'); ?></span>
                                <select name="temporary_block_type">
                                    <option value=""><?php echo esc_html__('Choose type', 'xerotact-shield-x-security'); ?></option>
                                    <option value="ip"><?php echo esc_html__('IP / range', 'xerotact-shield-x-security'); ?></option>
                                    <option value="country"><?php echo esc_html__('Country', 'xerotact-shield-x-security'); ?></option>
                                    <option value="user_agent"><?php echo esc_html__('User agent', 'xerotact-shield-x-security'); ?></option>
                                    <option value="referrer"><?php echo esc_html__('Referrer', 'xerotact-shield-x-security'); ?></option>
                                </select>
                            </label>
                            <label class="shieldx-field">
                                <span><?php echo esc_html__('Temporary block value', 'xerotact-shield-x-security'); ?></span>
                                <input type="text" name="temporary_block_value" value="" maxlength="160" autocomplete="off">
                            </label>
                            <?php $this->renderNumberField('temporary_block_minutes', __('Minutes', 'xerotact-shield-x-security'), 60, 1, 43200); ?>
                        </div>

                        <?php if (empty($temporaryBlocks)) : ?>
                            <p class="shieldx-empty"><?php echo esc_html__('No temporary blocks are active.', 'xerotact-shield-x-security'); ?></p>
                        <?php else : ?>
                            <div class="shieldx-compat-table-wrap">
                                <table class="widefat striped shieldx-temp-block-table">
                                    <thead>
                                        <tr>
                                            <th><?php echo esc_html__('Remove', 'xerotact-shield-x-security'); ?></th>
                                            <th><?php echo esc_html__('Type', 'xerotact-shield-x-security'); ?></th>
                                            <th><?php echo esc_html__('Value', 'xerotact-shield-x-security'); ?></th>
                                            <th><?php echo esc_html__('Expires', 'xerotact-shield-x-security'); ?></th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        <?php foreach ($temporaryBlocks as $block) : ?>
                                            <?php $this->renderTemporaryBlockRow(is_array($block) ? $block : []); ?>
                                        <?php endforeach; ?>
                                    </tbody>
                                </table>
                            </div>
                        <?php endif; ?>
                    </div>
                </section>

                <section class="shieldx-panel shieldx-panel--full">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Reputation Feed', 'xerotact-shield-x-security'); ?></h2>
                        <?php
                        $reputationCount = count((array) ($wafSettings['reputation_ip_blocklist'] ?? []))
                            + count((array) ($wafSettings['reputation_hostname_blocklist'] ?? []))
                            + count((array) ($wafSettings['reputation_user_agent_blocklist'] ?? []));
                        ?>
                        <span><?php echo esc_html(!empty($wafSettings['reputation_enabled']) ? sprintf(
                            /* translators: %d is the number of cached reputation entries. */
                            __('Enabled / %d cached', 'xerotact-shield-x-security'),
                            $reputationCount
                        ) : __('Optional', 'xerotact-shield-x-security')); ?></span>
                    </div>
                    <div class="shieldx-settings-grid shieldx-settings-grid--two">
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="reputation_enabled" value="1" <?php checked(!empty($wafSettings['reputation_enabled'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Use cached reputation entries', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Blocks cached feed IPs, hostnames, and user-agent tokens only after you enable this control.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-field">
                            <span><?php echo esc_html__('JSON feed URL', 'xerotact-shield-x-security'); ?></span>
                            <input type="url" name="reputation_feed_url" value="<?php echo esc_attr((string) ($wafSettings['reputation_feed_url'] ?? '')); ?>" placeholder="https://example.com/shieldx-reputation.json" autocomplete="off" maxlength="<?php echo esc_attr((string) WafSettings::MAX_REPUTATION_FEED_URL_LENGTH); ?>">
                            <small><?php echo esc_html__('Public HTTPS JSON only. Redirects are disabled, responses are capped, and URLs are limited to 2048 characters.', 'xerotact-shield-x-security'); ?></small>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="reputation_feed_confirm" value="1">
                            <span>
                                <strong><?php echo esc_html__('Confirm custom feed URL change', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Required once when setting or changing a custom reputation feed URL.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="refresh_reputation_feed" value="1">
                            <span>
                                <strong><?php echo esc_html__('Queue feed refresh after saving', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Schedules one WordPress cron refresh. Existing cached entries stay active if refresh fails.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <?php $this->renderTokenTextareaField('reputation_allowlist', __('Reputation allowlist', 'xerotact-shield-x-security'), $this->lineListValue($wafSettings['reputation_allowlist'] ?? []), __('IPs, hostnames, referrer tokens, or user-agent tokens that bypass reputation feed matches.', 'xerotact-shield-x-security'), array_merge($blockingSuggestions['ips'], $blockingSuggestions['hostnames'], $blockingSuggestions['user_agents']), __('Search recent clients or add exception', 'xerotact-shield-x-security')); ?>
                    </div>
                    <dl class="shieldx-detail-list shieldx-detail-list--compact">
                        <?php $this->renderDetail(__('Cached IPs', 'xerotact-shield-x-security'), (string) count((array) ($wafSettings['reputation_ip_blocklist'] ?? []))); ?>
                        <?php $this->renderDetail(__('Cached hostnames', 'xerotact-shield-x-security'), (string) count((array) ($wafSettings['reputation_hostname_blocklist'] ?? []))); ?>
                        <?php $this->renderDetail(__('Cached user agents', 'xerotact-shield-x-security'), (string) count((array) ($wafSettings['reputation_user_agent_blocklist'] ?? []))); ?>
                        <?php $this->renderDetail(__('Last refresh', 'xerotact-shield-x-security'), !empty($wafSettings['reputation_last_refresh']) ? $this->formatTimestamp((int) $wafSettings['reputation_last_refresh']) : __('Never', 'xerotact-shield-x-security')); ?>
                        <?php $this->renderDetail(__('Last error', 'xerotact-shield-x-security'), (string) ($wafSettings['reputation_last_error'] ?? '') !== '' ? (string) $wafSettings['reputation_last_error'] : __('None', 'xerotact-shield-x-security')); ?>
                    </dl>
                </section>

                <section class="shieldx-panel shieldx-panel--full">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Plugin Whitelist', 'xerotact-shield-x-security'); ?></h2>
                        <?php /* translators: %d is the number of selected items. */ ?>
                        <span><?php echo esc_html(sprintf(__('%d selected', 'xerotact-shield-x-security'), count($pluginWhitelist))); ?></span>
                    </div>
                    <?php if (empty($availablePlugins)) : ?>
                        <p class="shieldx-empty"><?php echo esc_html__('No installed plugins were detected.', 'xerotact-shield-x-security'); ?></p>
                    <?php else : ?>
                        <div class="shieldx-plugin-list">
                            <?php foreach ($availablePlugins as $plugin) : ?>
                                <?php $this->renderPluginWhitelistRow(is_array($plugin) ? $plugin : [], $pluginWhitelist); ?>
                            <?php endforeach; ?>
                        </div>
                    <?php endif; ?>
                </section>

                <div class="shieldx-form-actions shieldx-panel--full">
                    <button type="submit" class="button button-primary"><?php echo esc_html__('Save WAF settings', 'xerotact-shield-x-security'); ?></button>
                </div>
            </div>
        </form>
        <?php
        $this->closePage();
    }

    public function renderWafAccess(): void
    {
        if ($this->renderAsyncPageShell('shield-x-waf-access')) {
            return;
        }

        $status = $this->statusForPage();
        $wafStats = isset($status['waf']['stats']) && is_array($status['waf']['stats']) ? $status['waf']['stats'] : [];
        $wafSettings = isset($status['waf']['settings']) && is_array($status['waf']['settings']) ? $status['waf']['settings'] : [];
        $wafRules = isset($status['waf']['rules']) && is_array($status['waf']['rules']) ? $status['waf']['rules'] : [];
        $lastEvents = isset($wafStats['last_events']) && is_array($wafStats['last_events']) ? $wafStats['last_events'] : [];
        $filters = $this->wafAccessFilterState();
        $rows = $this->wafAccessControlRows($wafSettings, $lastEvents, $this->wafRuleLabels($wafRules), $this->wafAccessDismissedEventKeys());
        $filteredRows = $this->filterWafAccessRows($rows, $filters);
        $stats = $this->wafAccessRowStats($rows);

        $this->openPage('shield-x-waf-access', $status);
        ?>
        <section class="shieldx-panel shieldx-panel--full shieldx-waf-access-page" data-shieldx-accordion-open="1">
            <div class="shieldx-section-head">
                <div>
                    <h2><?php echo esc_html__('WAF access control', 'xerotact-shield-x-security'); ?></h2>
                    <span><?php echo esc_html__('Review blocked, whitelisted, temporary, reputation, and recent WAF entries.', 'xerotact-shield-x-security'); ?></span>
                </div>
                <div class="shieldx-section-head-actions">
                    <form class="shieldx-inline-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                        <input type="hidden" name="action" value="shieldx_clear_waf_access_table">
                        <?php wp_nonce_field('shieldx_clear_waf_access_table'); ?>
                        <button type="submit" class="button shieldx-button-danger">
                            <?php echo esc_html__('Clear all', 'xerotact-shield-x-security'); ?>
                        </button>
                    </form>
                    <a class="button" href="<?php echo esc_url(admin_url('admin.php?page=shield-x-waf')); ?>">
                        <?php echo esc_html__('Firewall settings', 'xerotact-shield-x-security'); ?>
                    </a>
                </div>
            </div>

            <div class="shieldx-waf-access-summary-grid" aria-label="<?php echo esc_attr__('Access list summary', 'xerotact-shield-x-security'); ?>">
                <?php $this->renderWafAccessSummaryCard(__('Total entries', 'xerotact-shield-x-security'), count($rows), 'info'); ?>
                <?php $this->renderWafAccessSummaryCard(__('Blocked', 'xerotact-shield-x-security'), (int) ($stats['blocked'] ?? 0), 'blocked'); ?>
                <?php $this->renderWafAccessSummaryCard(__('Whitelisted', 'xerotact-shield-x-security'), (int) ($stats['allowed'] ?? 0), 'allowed'); ?>
                <?php $this->renderWafAccessSummaryCard(__('Recent WAF', 'xerotact-shield-x-security'), (int) ($stats['recent'] ?? 0), 'recent'); ?>
            </div>

            <?php $this->renderWafAccessFilters($filters, count($filteredRows), count($rows)); ?>
        </section>

        <?php $this->renderWafAccessControlSection($filteredRows, true); ?>
        <?php
        $this->closePage();
    }

    /**
     * @param array<string,mixed> $settings
     * @param array<int,mixed> $lastEvents
     * @param array<string,string> $ruleLabels
     * @param array<string,bool> $dismissedEvents
     * @return array<int,array<string,mixed>>
     */
    private function wafAccessControlRows(array $settings, array $lastEvents, array $ruleLabels, array $dismissedEvents): array
    {
        $rows = [];
        $listedIps = [];
        $recentReasons = $this->wafRecentBlockReasonsByIp($lastEvents, $ruleLabels);

        foreach ($this->wafAccessListValues($settings['ip_allowlist'] ?? []) as $value) {
            $listedIps[$value] = true;
            $rows[] = [
                'status' => __('Whitelisted', 'xerotact-shield-x-security'),
                'status_type' => 'allowed',
                'value' => $value,
                'source' => __('WAF allowlist', 'xerotact-shield-x-security'),
                'reason' => __('Bypasses WAF IP block rules for this IP or range.', 'xerotact-shield-x-security'),
                'expires' => '-',
                'actions' => [
                    ['action' => 'block_ip', 'value' => $value, 'label' => __('Block', 'xerotact-shield-x-security'), 'class' => 'shieldx-button-danger'],
                    ['action' => 'remove_allow', 'value' => $value, 'label' => __('Remove', 'xerotact-shield-x-security'), 'class' => ''],
                ],
            ];
        }

        foreach ($this->wafAccessListValues($settings['ip_blocklist'] ?? []) as $value) {
            $listedIps[$value] = true;
            $rows[] = [
                'status' => __('Blocked', 'xerotact-shield-x-security'),
                'status_type' => 'blocked',
                'value' => $value,
                'source' => __('Manual WAF blocklist', 'xerotact-shield-x-security'),
                'reason' => $recentReasons[$value] ?? __('Manual IP or CIDR block in WAF settings.', 'xerotact-shield-x-security'),
                'expires' => '-',
                'actions' => [
                    ['action' => 'allow_ip', 'value' => $value, 'label' => __('Whitelist', 'xerotact-shield-x-security'), 'class' => ''],
                    ['action' => 'remove_block', 'value' => $value, 'label' => __('Remove block', 'xerotact-shield-x-security'), 'class' => ''],
                ],
            ];
        }

        foreach ((array) ($settings['temporary_blocks'] ?? []) as $block) {
            if (!is_array($block)) {
                continue;
            }

            $id = trim((string) ($block['id'] ?? ''));
            $type = trim((string) ($block['type'] ?? ''));
            $value = trim((string) ($block['value'] ?? ''));

            if ($id === '' || $value === '') {
                continue;
            }

            if ($type === 'ip') {
                $listedIps[$value] = true;
            }

            $actions = [];

            if ($type === 'ip') {
                $actions[] = ['action' => 'allow_ip', 'value' => $value, 'label' => __('Whitelist', 'xerotact-shield-x-security'), 'class' => ''];
                $actions[] = ['action' => 'block_ip', 'value' => $value, 'label' => __('Block IP', 'xerotact-shield-x-security'), 'class' => 'shieldx-button-danger'];
            }

            $actions[] = ['action' => 'remove_temp', 'value' => $id, 'label' => __('Remove', 'xerotact-shield-x-security'), 'class' => ''];

            $rows[] = [
                'status' => __('Temporary block', 'xerotact-shield-x-security'),
                'status_type' => 'temporary',
                'value' => $value,
                'source' => sprintf(
                    /* translators: %s is the temporary block type. */
                    __('Temporary %s block', 'xerotact-shield-x-security'),
                    $this->formatLabel($type)
                ),
                'reason' => trim((string) ($block['reason'] ?? '')) !== '' ? (string) $block['reason'] : __('Temporary block from WAF settings.', 'xerotact-shield-x-security'),
                'expires' => !empty($block['expires_at']) ? $this->formatTimestamp((int) $block['expires_at']) : '-',
                'actions' => $actions,
            ];
        }

        foreach ($this->wafAccessListValues($settings['reputation_allowlist'] ?? []) as $value) {
            $rows[] = [
                'status' => __('Whitelisted', 'xerotact-shield-x-security'),
                'status_type' => 'allowed',
                'value' => $value,
                'source' => __('Reputation allowlist', 'xerotact-shield-x-security'),
                'reason' => __('Bypasses reputation feed matches for this token.', 'xerotact-shield-x-security'),
                'expires' => '-',
                'actions' => [
                    ['action' => 'remove_reputation_allow', 'value' => $value, 'label' => __('Remove', 'xerotact-shield-x-security'), 'class' => ''],
                ],
            ];
        }

        foreach ($this->wafAccessListValues($settings['reputation_ip_blocklist'] ?? []) as $value) {
            $listedIps[$value] = true;
            $rows[] = [
                'status' => __('Blocked', 'xerotact-shield-x-security'),
                'status_type' => 'blocked',
                'value' => $value,
                'source' => __('Reputation feed', 'xerotact-shield-x-security'),
                'reason' => __('Cached IP from the configured reputation feed.', 'xerotact-shield-x-security'),
                'expires' => '-',
                'actions' => [
                    ['action' => 'trust_reputation', 'value' => $value, 'label' => __('Trust feed entry', 'xerotact-shield-x-security'), 'class' => ''],
                    ['action' => 'allow_ip', 'value' => $value, 'label' => __('Whitelist IP', 'xerotact-shield-x-security'), 'class' => ''],
                ],
            ];
        }

        $recentRows = [];

        foreach ($lastEvents as $event) {
            if (!is_array($event)) {
                continue;
            }

            $ip = trim((string) ($event['ip'] ?? ''));

            if ($ip === '' || $ip === 'unknown') {
                continue;
            }

            $eventKey = $this->wafAccessEventKey($event);
            if ($eventKey !== '' && isset($dismissedEvents[$eventKey])) {
                continue;
            }

            $recentRows[] = [
                'status' => $this->wafAccessEventStatus($event),
                'status_type' => $this->wafAccessEventStatusType($event),
                'recent_waf' => true,
                'value' => $ip,
                'source' => $this->wafAccessEventSource($event),
                'reason' => $this->wafAccessEventReason($event, $ruleLabels),
                'expires' => !empty($event['expires_at']) ? $this->formatTimestamp((int) $event['expires_at']) : $this->formatTime((string) ($event['time'] ?? '')),
                'actions' => $this->wafAccessEventActions($event, $ip, $eventKey),
                'event_key' => $eventKey,
                'temporary_id' => trim((string) ($event['temporary_id'] ?? '')),
                'blocked_event' => $this->wafEventWasBlocked($event),
            ];
        }

        return array_merge($rows, $this->aggregateWafAccessRecentRows($recentRows));
    }

    /**
     * @param array<int,array<string,mixed>> $rows
     * @return array<int,array<string,mixed>>
     */
    private function aggregateWafAccessRecentRows(array $rows): array
    {
        $groups = [];

        foreach ($rows as $row) {
            $value = trim((string) ($row['value'] ?? ''));
            if ($value === '') {
                $groups[] = $row;
                continue;
            }

            $groupKey = strtolower($value);
            $eventKey = sanitize_key((string) ($row['event_key'] ?? ''));
            $temporaryId = trim((string) ($row['temporary_id'] ?? ''));
            $reason = trim((string) ($row['reason'] ?? ''));
            $source = trim((string) ($row['source'] ?? ''));

            if (!isset($groups[$groupKey])) {
                $row['hit_count'] = 1;
                $row['event_keys'] = $eventKey !== '' ? [$eventKey] : [];
                $row['ai_event_key'] = $eventKey;
                $row['temporary_ids'] = $temporaryId !== '' ? [$temporaryId] : [];
                $row['reason_samples'] = $reason !== '' ? [$reason] : [];
                $row['source_samples'] = $source !== '' ? [$source] : [];
                $row['blocked_event'] = !empty($row['blocked_event']);
                $groups[$groupKey] = $row;
                continue;
            }

            $groups[$groupKey]['hit_count'] = (int) ($groups[$groupKey]['hit_count'] ?? 1) + 1;
            $groups[$groupKey]['event_keys'] = $this->uniqueStringList(array_merge(
                isset($groups[$groupKey]['event_keys']) && is_array($groups[$groupKey]['event_keys']) ? $groups[$groupKey]['event_keys'] : [],
                $eventKey !== '' ? [$eventKey] : []
            ));
            $groups[$groupKey]['temporary_ids'] = $this->uniqueStringList(array_merge(
                isset($groups[$groupKey]['temporary_ids']) && is_array($groups[$groupKey]['temporary_ids']) ? $groups[$groupKey]['temporary_ids'] : [],
                $temporaryId !== '' ? [$temporaryId] : []
            ));
            $groups[$groupKey]['reason_samples'] = $this->uniqueStringList(array_merge(
                isset($groups[$groupKey]['reason_samples']) && is_array($groups[$groupKey]['reason_samples']) ? $groups[$groupKey]['reason_samples'] : [],
                $reason !== '' ? [$reason] : []
            ));
            $groups[$groupKey]['source_samples'] = $this->uniqueStringList(array_merge(
                isset($groups[$groupKey]['source_samples']) && is_array($groups[$groupKey]['source_samples']) ? $groups[$groupKey]['source_samples'] : [],
                $source !== '' ? [$source] : []
            ));
            $groups[$groupKey]['blocked_event'] = !empty($groups[$groupKey]['blocked_event']) || !empty($row['blocked_event']);

            if ($this->wafAccessStatusPriority((string) ($row['status_type'] ?? '')) > $this->wafAccessStatusPriority((string) ($groups[$groupKey]['status_type'] ?? ''))) {
                $groups[$groupKey]['status'] = (string) ($row['status'] ?? $groups[$groupKey]['status'] ?? '');
                $groups[$groupKey]['status_type'] = (string) ($row['status_type'] ?? $groups[$groupKey]['status_type'] ?? '');
            }
        }

        foreach ($groups as $key => $row) {
            $hitCount = max(1, (int) ($row['hit_count'] ?? 1));
            if ($hitCount > 1) {
                $reasonSamples = isset($row['reason_samples']) && is_array($row['reason_samples']) ? $row['reason_samples'] : [];
                $sourceSamples = isset($row['source_samples']) && is_array($row['source_samples']) ? $row['source_samples'] : [];
                $eventKeys = isset($row['event_keys']) && is_array($row['event_keys']) ? $row['event_keys'] : [];
                $temporaryIds = isset($row['temporary_ids']) && is_array($row['temporary_ids']) ? $row['temporary_ids'] : [];

                $row['reason'] = $this->formatWafAccessGroupedReason($reasonSamples, $hitCount);
                $row['source'] = $this->formatWafAccessGroupedSource($sourceSamples);

                if (!empty($row['blocked_event'])) {
                    $row['actions'] = $this->wafAccessGroupedEventActions((string) ($row['value'] ?? ''), $eventKeys, $temporaryIds);
                }
            }

            unset($row['event_key'], $row['temporary_id'], $row['event_keys'], $row['temporary_ids'], $row['reason_samples'], $row['source_samples'], $row['blocked_event']);
            $groups[$key] = $row;
        }

        return array_values($groups);
    }

    private function wafAccessStatusPriority(string $statusType): int
    {
        if ($statusType === 'blocked') {
            return 30;
        }

        if ($statusType === 'would_block') {
            return 20;
        }

        if ($statusType === 'waf_hit') {
            return 10;
        }

        return 0;
    }

    /**
     * @param array<int,mixed> $values
     * @return array<int,string>
     */
    private function uniqueStringList(array $values): array
    {
        $unique = [];

        foreach ($values as $value) {
            $value = trim((string) $value);
            if ($value !== '') {
                $unique[$value] = $value;
            }
        }

        return array_values($unique);
    }

    /**
     * @param array<int,mixed> $reasons
     */
    private function formatWafAccessGroupedReason(array $reasons, int $hitCount): string
    {
        $latest = trim((string) ($reasons[0] ?? ''));
        if ($latest === '') {
            $latest = __('Recent WAF event.', 'xerotact-shield-x-security');
        }

        return sprintf(
            /* translators: 1: number of WAF hits, 2: latest WAF hit reason. */
            _n('%1$d WAF hit from this IP. Latest: %2$s', '%1$d WAF hits from this IP. Latest: %2$s', $hitCount, 'xerotact-shield-x-security'),
            $hitCount,
            $latest
        );
    }

    /**
     * @param array<int,mixed> $sources
     */
    private function formatWafAccessGroupedSource(array $sources): string
    {
        $sources = $this->uniqueStringList($sources);
        if (count($sources) <= 1) {
            return (string) ($sources[0] ?? __('WAF', 'xerotact-shield-x-security'));
        }

        return sprintf(
            /* translators: %d is the number of source labels grouped together. */
            __('WAF / multiple (%d sources)', 'xerotact-shield-x-security'),
            count($sources)
        );
    }

    /**
     * @param array<int,mixed> $eventKeys
     * @param array<int,mixed> $temporaryIds
     * @return array<int,array<string,string>>
     */
    private function wafAccessGroupedEventActions(string $ip, array $eventKeys, array $temporaryIds): array
    {
        $actions = [
            ['action' => 'allow_ip', 'value' => $ip, 'label' => __('Whitelist', 'xerotact-shield-x-security'), 'class' => ''],
            ['action' => 'block_ip', 'value' => $ip, 'label' => __('Block IP', 'xerotact-shield-x-security'), 'class' => 'shieldx-button-danger'],
        ];

        $actions[] = [
            'action' => 'remove_waf_events',
            'value' => $this->wafAccessEventGroupRemovalPayload($eventKeys, $ip, $temporaryIds),
            'label' => __('Remove', 'xerotact-shield-x-security'),
            'class' => '',
        ];

        return $actions;
    }

    /**
     * @param array<int,array<string,mixed>> $rows
     */
    private function renderWafAccessControlSection(array $rows, bool $numbered = false): void
    {
        ?>
        <section id="shieldx-waf-access-control" class="shieldx-panel shieldx-panel--full shieldx-waf-access-panel" data-shieldx-accordion-open="1">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Access control and WAF hits', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html(sprintf(
                    /* translators: %d is the number of access control rows. */
                    _n('%d entry', '%d entries', count($rows), 'xerotact-shield-x-security'),
                    count($rows)
                )); ?></span>
            </div>
            <?php if (empty($rows)) : ?>
                <p class="shieldx-empty"><?php echo esc_html__('No blocked, whitelisted, or recent WAF entries yet.', 'xerotact-shield-x-security'); ?></p>
            <?php else : ?>
                <div class="shieldx-compat-table-wrap shieldx-waf-access-table-wrap">
                    <table class="widefat striped shieldx-waf-access-table">
                        <thead>
                            <tr>
                                <?php if ($numbered) : ?>
                                    <th><?php echo esc_html__('#', 'xerotact-shield-x-security'); ?></th>
                                <?php endif; ?>
                                <th><?php echo esc_html__('Status', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('IP / token', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Source', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Reason', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Expires / Seen', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Action', 'xerotact-shield-x-security'); ?></th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php foreach ($rows as $index => $row) : ?>
                                <?php $this->renderWafAccessControlRow($row, $numbered ? $index + 1 : 0); ?>
                            <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>
            <?php endif; ?>
        </section>
        <?php
    }

    /**
     * @param array<string,mixed> $row
     */
    private function renderWafAccessControlRow(array $row, int $number = 0): void
    {
        $statusType = sanitize_html_class((string) ($row['status_type'] ?? 'recent'));
        $actions = isset($row['actions']) && is_array($row['actions']) ? $row['actions'] : [];
        $actions = apply_filters('shieldx_waf_access_row_actions', $actions, $row, 'waf_access_row');
        ?>
        <tr>
            <?php if ($number > 0) : ?>
                <td class="shieldx-row-number"><?php echo esc_html((string) $number); ?></td>
            <?php endif; ?>
            <td><span class="<?php echo esc_attr('shieldx-access-status shieldx-access-status--' . $statusType); ?>"><?php echo esc_html((string) ($row['status'] ?? '')); ?></span></td>
            <td>
                <code><?php echo esc_html((string) ($row['value'] ?? '')); ?></code>
                <?php $hitCount = max(1, (int) ($row['hit_count'] ?? 1)); ?>
                <?php if ($hitCount > 1) : ?>
                    <span class="shieldx-status-pill shieldx-status-pill--info">
                        <?php
                        echo esc_html(sprintf(
                            /* translators: %s is the number of grouped WAF hits. */
                            _n('%s hit', '%s hits', $hitCount, 'xerotact-shield-x-security'),
                            number_format_i18n($hitCount)
                        ));
                        ?>
                    </span>
                <?php endif; ?>
            </td>
            <td><?php echo esc_html((string) ($row['source'] ?? '')); ?></td>
            <td><?php echo esc_html((string) ($row['reason'] ?? '')); ?></td>
            <td><?php echo esc_html((string) ($row['expires'] ?? '-')); ?></td>
            <td>
                <div class="shieldx-access-actions">
                    <?php foreach ($actions as $action) : ?>
                        <?php
                        if (!is_array($action)) {
                            continue;
                        }

                        $actionName = (string) ($action['action'] ?? '');
                        $actionValue = (string) ($action['value'] ?? '');
                        $class = trim('button button-small shieldx-access-action ' . (string) ($action['class'] ?? ''));
                        $url = trim((string) ($action['url'] ?? ''));
                        if ($url !== '') {
                            ?>
                            <a class="<?php echo esc_attr($class); ?>" href="<?php echo esc_url($url); ?>">
                                <?php echo esc_html((string) ($action['label'] ?? __('Review', 'xerotact-shield-x-security'))); ?>
                            </a>
                            <?php
                            continue;
                        }
                        if ($actionName === '' || $actionValue === '') {
                            continue;
                        }
                        ?>
                        <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                            <input type="hidden" name="action" value="shieldx_waf_access_action">
                            <input type="hidden" name="waf_access_action" value="<?php echo esc_attr($this->wafAccessActionValue($actionName, $actionValue)); ?>">
                            <?php foreach ($this->wafAccessFilterState() as $filterKey => $filterValue) : ?>
                                <?php if ($filterValue !== '') : ?>
                                    <input type="hidden" name="<?php echo esc_attr('return_' . $filterKey); ?>" value="<?php echo esc_attr((string) $filterValue); ?>">
                                <?php endif; ?>
                            <?php endforeach; ?>
                            <?php wp_nonce_field('shieldx_waf_access_action'); ?>
                            <button type="submit" class="<?php echo esc_attr($class); ?>">
                                <?php echo esc_html((string) ($action['label'] ?? __('Apply', 'xerotact-shield-x-security'))); ?>
                            </button>
                        </form>
                    <?php endforeach; ?>
                </div>
            </td>
        </tr>
        <?php
    }

    private function renderWafAccessSummaryCard(string $label, int $value, string $tone): void
    {
        ?>
        <article class="<?php echo esc_attr('shieldx-waf-access-summary shieldx-waf-access-summary--' . sanitize_html_class($tone)); ?>">
            <span><?php echo esc_html($label); ?></span>
            <strong><?php echo esc_html(number_format_i18n($value)); ?></strong>
        </article>
        <?php
    }

    /**
     * @param array{s:string,status:string,source:string} $filters
     */
    private function renderWafAccessFilters(array $filters, int $shown, int $total): void
    {
        $statusOptions = [
            'all' => __('All statuses', 'xerotact-shield-x-security'),
            'blocked' => __('Blocked', 'xerotact-shield-x-security'),
            'would_block' => __('Would block', 'xerotact-shield-x-security'),
            'waf_hit' => __('Logged WAF hit', 'xerotact-shield-x-security'),
            'allowed' => __('Whitelisted', 'xerotact-shield-x-security'),
            'temporary' => __('Temporary', 'xerotact-shield-x-security'),
            'recent' => __('Recent WAF', 'xerotact-shield-x-security'),
        ];
        $sourceOptions = [
            'all' => __('All sources', 'xerotact-shield-x-security'),
            'manual' => __('Manual WAF blocklist', 'xerotact-shield-x-security'),
            'allowlist' => __('WAF allowlist', 'xerotact-shield-x-security'),
            'waf' => __('WAF', 'xerotact-shield-x-security'),
            'login' => __('WAF / login', 'xerotact-shield-x-security'),
            'xmlrpc' => __('WAF / XML-RPC', 'xerotact-shield-x-security'),
            'reputation' => __('Reputation feed', 'xerotact-shield-x-security'),
            'temporary' => __('Temporary blocks', 'xerotact-shield-x-security'),
        ];
        ?>
        <form class="shieldx-waf-access-filters" method="get" action="<?php echo esc_url(admin_url('admin.php')); ?>">
            <input type="hidden" name="page" value="shield-x-waf-access">
            <label class="shieldx-field shieldx-waf-access-search">
                <span><?php echo esc_html__('Search', 'xerotact-shield-x-security'); ?></span>
                <input type="search" name="s" value="<?php echo esc_attr($filters['s']); ?>" placeholder="<?php echo esc_attr__('IP, rule, reason, source, URL...', 'xerotact-shield-x-security'); ?>">
            </label>
            <label class="shieldx-field">
                <span><?php echo esc_html__('Status', 'xerotact-shield-x-security'); ?></span>
                <select name="status">
                    <?php foreach ($statusOptions as $value => $label) : ?>
                        <option value="<?php echo esc_attr($value); ?>" <?php selected($filters['status'], $value); ?>><?php echo esc_html($label); ?></option>
                    <?php endforeach; ?>
                </select>
            </label>
            <label class="shieldx-field">
                <span><?php echo esc_html__('Source', 'xerotact-shield-x-security'); ?></span>
                <select name="source">
                    <?php foreach ($sourceOptions as $value => $label) : ?>
                        <option value="<?php echo esc_attr($value); ?>" <?php selected($filters['source'], $value); ?>><?php echo esc_html($label); ?></option>
                    <?php endforeach; ?>
                </select>
            </label>
            <div class="shieldx-waf-access-filter-actions">
                <button type="submit" class="button button-primary"><?php echo esc_html__('Filter', 'xerotact-shield-x-security'); ?></button>
                <a class="button" href="<?php echo esc_url(admin_url('admin.php?page=shield-x-waf-access')); ?>"><?php echo esc_html__('Reset', 'xerotact-shield-x-security'); ?></a>
            </div>
            <p class="shieldx-waf-access-filter-count">
                <?php
                echo esc_html(sprintf(
                    /* translators: 1: visible row count, 2: total row count. */
                    __('Showing %1$d of %2$d entries', 'xerotact-shield-x-security'),
                    $shown,
                    $total
                ));
                ?>
            </p>
        </form>
        <?php
    }

    /**
     * @return array{s:string,status:string,source:string}
     */
    private function wafAccessFilterState(): array
    {
        $status = $this->queryKey('status', 'all');
        $source = $this->queryKey('source', 'all');

        if (!in_array($status, ['all', 'blocked', 'would_block', 'waf_hit', 'allowed', 'temporary', 'recent'], true)) {
            $status = 'all';
        }

        if (!in_array($source, ['all', 'manual', 'allowlist', 'waf', 'login', 'xmlrpc', 'reputation', 'temporary'], true)) {
            $source = 'all';
        }

        return [
            's' => trim($this->queryText('s')),
            'status' => $status,
            'source' => $source,
        ];
    }

    /**
     * @param array<int,array<string,mixed>> $rows
     * @param array{s:string,status:string,source:string} $filters
     * @return array<int,array<string,mixed>>
     */
    private function filterWafAccessRows(array $rows, array $filters): array
    {
        $filtered = [];
        $search = strtolower($filters['s']);

        foreach ($rows as $row) {
            $status = (string) ($row['status_type'] ?? '');
            if ($filters['status'] !== 'all' && !($filters['status'] === 'recent' && !empty($row['recent_waf'])) && $status !== $filters['status']) {
                continue;
            }

            if ($filters['source'] !== 'all' && $this->wafAccessRowSourceType($row) !== $filters['source']) {
                continue;
            }

            if ($search !== '' && strpos(strtolower(implode(' ', [
                (string) ($row['status'] ?? ''),
                (string) ($row['value'] ?? ''),
                (string) ($row['source'] ?? ''),
                (string) ($row['reason'] ?? ''),
                (string) ($row['expires'] ?? ''),
            ])), $search) === false) {
                continue;
            }

            $filtered[] = $row;
        }

        return $filtered;
    }

    /**
     * @param array<int,array<string,mixed>> $rows
     * @return array<string,int>
     */
    private function wafAccessRowStats(array $rows): array
    {
        $stats = [
            'blocked' => 0,
            'allowed' => 0,
            'temporary' => 0,
            'recent' => 0,
            'would_block' => 0,
            'waf_hit' => 0,
        ];

        foreach ($rows as $row) {
            $type = (string) ($row['status_type'] ?? '');
            if (isset($stats[$type])) {
                $stats[$type]++;
            }

            if (!empty($row['recent_waf'])) {
                $stats['recent']++;
            }
        }

        return $stats;
    }

    /**
     * @param array<string,mixed> $row
     */
    private function wafAccessRowSourceType(array $row): string
    {
        $source = strtolower((string) ($row['source'] ?? ''));

        if (strpos($source, 'login') !== false) {
            return 'login';
        }

        if (strpos($source, 'xml-rpc') !== false || strpos($source, 'xmlrpc') !== false) {
            return 'xmlrpc';
        }

        if (strpos($source, 'reputation') !== false) {
            return 'reputation';
        }

        if (strpos($source, 'temporary') !== false) {
            return 'temporary';
        }

        if (strpos($source, 'allowlist') !== false) {
            return 'allowlist';
        }

        if (strpos($source, 'manual') !== false) {
            return 'manual';
        }

        return strpos($source, 'waf') !== false ? 'waf' : 'all';
    }

    /**
     * @return array<string,string>
     */
    private function wafAccessRedirectQuery(): array
    {
        $query = [
            'page' => 'shield-x-waf-access',
            'shieldx_waf_access_updated' => '1',
        ];
        $search = trim($this->postedText('return_s', ''));
        $status = sanitize_key($this->postedText('return_status', 'all'));
        $source = sanitize_key($this->postedText('return_source', 'all'));

        if ($search !== '') {
            $query['s'] = $search;
        }

        if (in_array($status, ['blocked', 'would_block', 'waf_hit', 'allowed', 'temporary', 'recent'], true)) {
            $query['status'] = $status;
        }

        if (in_array($source, ['manual', 'allowlist', 'waf', 'login', 'xmlrpc', 'reputation', 'temporary'], true)) {
            $query['source'] = $source;
        }

        return $query;
    }

    /**
     * @param mixed $value
     * @return array<int,string>
     */
    private function wafAccessListValues($value): array
    {
        if (is_array($value)) {
            $items = [];

            foreach ($value as $item) {
                $item = trim((string) $item);

                if ($item !== '') {
                    $items[$item] = $item;
                }
            }

            return array_values($items);
        }

        return $this->lineListArray($this->lineListValue($value));
    }

    private function wafAccessActionValue(string $action, string $value): string
    {
        return $action . '|' . $this->wafAccessEncodeActionValue($value);
    }

    private function wafAccessEncodeActionValue(string $value): string
    {
        return rtrim(strtr(base64_encode($value), '+/', '-_'), '=');
    }

    private function wafAccessDecodeActionValue(string $value): string
    {
        $normalized = strtr($value, '-_', '+/');
        $padding = strlen($normalized) % 4;
        if ($padding > 0) {
            $normalized .= str_repeat('=', 4 - $padding);
        }

        $decoded = base64_decode($normalized, true);

        return is_string($decoded) ? $decoded : rawurldecode($value);
    }

    /**
     * @param array<string,mixed> $event
     * @return array<int,array<string,string>>
     */
    private function wafAccessEventActions(array $event, string $ip, string $eventKey): array
    {
        $actions = [
            ['action' => 'allow_ip', 'value' => $ip, 'label' => __('Whitelist', 'xerotact-shield-x-security'), 'class' => ''],
            ['action' => 'block_ip', 'value' => $ip, 'label' => __('Block IP', 'xerotact-shield-x-security'), 'class' => 'shieldx-button-danger'],
        ];

        if ($this->wafEventWasBlocked($event)) {
            $removeAction = $eventKey !== '' ? 'remove_waf_event' : 'remove_ip_blocks';
            $removeValue = $eventKey !== '' ? $this->wafAccessEventRemovalPayload($eventKey, $ip, (string) ($event['temporary_id'] ?? '')) : $ip;
            $actions[] = ['action' => $removeAction, 'value' => $removeValue, 'label' => __('Remove', 'xerotact-shield-x-security'), 'class' => ''];

            return $actions;
        }

        return $actions;
    }

    /**
     * @param array<int,mixed> $blocks
     */
    private function applyWafAccessAction(string $encodedAction, string &$ipAllowlist, string &$ipBlocklist, string &$reputationAllowlist, array &$blocks): void
    {
        if ($encodedAction === '') {
            return;
        }

        $parts = explode('|', $encodedAction, 2);
        $action = (string) ($parts[0] ?? '');
        $value = trim($this->wafAccessDecodeActionValue((string) ($parts[1] ?? '')));

        if ($action === '' || $value === '') {
            return;
        }

        switch ($action) {
            case 'allow_ip':
                $ipBlocklist = $this->removeLineListValue($ipBlocklist, $value);
                $ipAllowlist = $this->appendLineListValue($ipAllowlist, $value);
                $blocks = $this->removeTemporaryIpBlocks($blocks, $value);
                break;

            case 'block_ip':
                if ($this->isCurrentWafClientIp($value)) {
                    break;
                }

                $ipAllowlist = $this->removeLineListValue($ipAllowlist, $value);
                $ipBlocklist = $this->appendLineListValue($ipBlocklist, $value);
                $blocks = $this->removeTemporaryIpBlocks($blocks, $value);
                break;

            case 'remove_block':
                $ipBlocklist = $this->removeLineListValue($ipBlocklist, $value);
                break;

            case 'remove_ip_blocks':
                $ipBlocklist = $this->removeLineListValue($ipBlocklist, $value);
                $blocks = $this->removeTemporaryIpBlocks($blocks, $value);
                break;

            case 'remove_waf_events':
                $payload = $this->parseWafAccessEventGroupRemovalPayload($value);
                $this->dismissWafAccessEventKeys($payload['event_keys']);

                if (!empty($payload['temporary_ids'])) {
                    foreach ($payload['temporary_ids'] as $temporaryId) {
                        $blocks = $this->removeTemporaryBlockById($blocks, $temporaryId);
                    }
                } elseif ($payload['ip'] !== '') {
                    $blocks = $this->removeTemporaryIpBlocks($blocks, $payload['ip']);
                }
                break;

            case 'remove_waf_event':
                $payload = $this->parseWafAccessEventRemovalPayload($value);
                if ($payload['event_key'] !== '') {
                    $this->dismissWafAccessEvent($payload['event_key']);
                }

                if ($payload['temporary_id'] !== '') {
                    $blocks = $this->removeTemporaryBlockById($blocks, $payload['temporary_id']);
                } elseif ($payload['ip'] !== '') {
                    $blocks = $this->removeTemporaryIpBlocks($blocks, $payload['ip']);
                }
                break;

            case 'remove_allow':
                $ipAllowlist = $this->removeLineListValue($ipAllowlist, $value);
                break;

            case 'remove_temp':
                $blocks = $this->removeTemporaryBlockById($blocks, $value);
                break;

            case 'trust_reputation':
                $reputationAllowlist = $this->appendLineListValue($reputationAllowlist, $value);
                break;

            case 'remove_reputation_allow':
                $reputationAllowlist = $this->removeLineListValue($reputationAllowlist, $value);
                break;
        }
    }

    /**
     * @return array<string,bool>
     */
    private function wafAccessDismissedEventKeys(): array
    {
        $stored = get_option(self::WAF_ACCESS_DISMISSED_EVENTS_OPTION, []);
        if (!is_array($stored)) {
            return [];
        }

        $keys = [];
        foreach ($stored as $key) {
            $key = sanitize_key((string) $key);
            if ($key !== '') {
                $keys[$key] = true;
            }
        }

        return $keys;
    }

    private function dismissWafAccessEvent(string $eventKey): void
    {
        $this->dismissWafAccessEventKeys([$eventKey]);
    }

    /**
     * @param array<int,mixed> $eventKeys
     */
    private function dismissWafAccessEventKeys(array $eventKeys): void
    {
        if (empty($eventKeys)) {
            return;
        }

        $keys = $this->wafAccessDismissedEventKeys();

        foreach ($eventKeys as $eventKey) {
            $eventKey = sanitize_key((string) $eventKey);
            if ($eventKey !== '') {
                $keys[$eventKey] = true;
            }
        }

        $stored = array_slice(array_keys($keys), -self::WAF_ACCESS_DISMISSED_EVENTS_LIMIT);

        update_option(self::WAF_ACCESS_DISMISSED_EVENTS_OPTION, $stored, false);
    }

    /**
     * @param array<int,mixed> $events
     * @return array<int,mixed>
     */
    private function filterDismissedWafEvents(array $events, array $dismissedEvents): array
    {
        $visible = [];

        foreach ($events as $event) {
            if (!is_array($event)) {
                continue;
            }

            $eventKey = $this->wafAccessEventKey($event);
            if ($eventKey !== '' && isset($dismissedEvents[$eventKey])) {
                continue;
            }

            $visible[] = $event;
        }

        return $visible;
    }

    /**
     * @param array<int,mixed> $events
     */
    private function dismissWafEvents(array $events): void
    {
        $dismissed = $this->wafAccessDismissedEventKeys();

        foreach ($events as $event) {
            if (!is_array($event)) {
                continue;
            }

            $eventKey = $this->wafAccessEventKey($event);
            if ($eventKey !== '') {
                $dismissed[$eventKey] = true;
            }
        }

        update_option(
            self::WAF_ACCESS_DISMISSED_EVENTS_OPTION,
            array_slice(array_keys($dismissed), -self::WAF_ACCESS_DISMISSED_EVENTS_LIMIT),
            false
        );
    }

    /**
     * @return array<int,mixed>
     */
    private function recentWafEventsForDismissal(): array
    {
        $summary = (new WafStats(FileStorage::fromWordPress()))->summary(86400, 30);

        return isset($summary['last_events']) && is_array($summary['last_events']) ? $summary['last_events'] : [];
    }

    /**
     * @param array<string,mixed> $event
     */
    private function wafAccessEventKey(array $event): string
    {
        $fingerprint = sanitize_key((string) ($event['fingerprint'] ?? ''));
        if ($fingerprint !== '') {
            return $fingerprint;
        }

        return hash('sha256', (string) wp_json_encode([
            'time' => (string) ($event['time'] ?? ''),
            'ip' => (string) ($event['ip'] ?? ''),
            'event' => (string) ($event['event'] ?? ''),
            'action' => (string) ($event['action'] ?? ''),
            'method' => (string) ($event['method'] ?? ''),
            'uri' => (string) ($event['uri'] ?? ''),
            'score' => (int) ($event['score'] ?? 0),
            'rules' => isset($event['rules']) && is_array($event['rules']) ? array_values(array_map('strval', $event['rules'])) : [],
            'reason' => (string) ($event['reason'] ?? ''),
            'matched_value' => (string) ($event['matched_value'] ?? ''),
            'temporary_id' => (string) ($event['temporary_id'] ?? ''),
            'expires_at' => (int) ($event['expires_at'] ?? 0),
        ]));
    }

    private function wafAccessEventRemovalPayload(string $eventKey, string $ip, string $temporaryId): string
    {
        return sanitize_key($eventKey) . '|' . rawurlencode($ip) . '|' . rawurlencode(trim($temporaryId));
    }

    /**
     * @param array<int,mixed> $eventKeys
     * @param array<int,mixed> $temporaryIds
     */
    private function wafAccessEventGroupRemovalPayload(array $eventKeys, string $ip, array $temporaryIds): string
    {
        $keys = [];
        foreach ($eventKeys as $eventKey) {
            $eventKey = sanitize_key((string) $eventKey);
            if ($eventKey !== '') {
                $keys[$eventKey] = $eventKey;
            }
        }

        $blockIds = [];
        foreach ($temporaryIds as $temporaryId) {
            $temporaryId = trim((string) $temporaryId);
            if ($temporaryId !== '') {
                $blockIds[$temporaryId] = $temporaryId;
            }
        }

        return implode(',', array_values($keys)) . '|' . rawurlencode($ip) . '|' . rawurlencode(implode(',', array_values($blockIds)));
    }

    /**
     * @return array{event_key:string,ip:string,temporary_id:string}
     */
    private function parseWafAccessEventRemovalPayload(string $payload): array
    {
        $parts = explode('|', $payload, 3);

        return [
            'event_key' => sanitize_key((string) ($parts[0] ?? '')),
            'ip' => trim(rawurldecode((string) ($parts[1] ?? ''))),
            'temporary_id' => trim(rawurldecode((string) ($parts[2] ?? ''))),
        ];
    }

    /**
     * @return array{event_keys:array<int,string>,ip:string,temporary_ids:array<int,string>}
     */
    private function parseWafAccessEventGroupRemovalPayload(string $payload): array
    {
        $parts = explode('|', $payload, 3);
        $eventKeys = [];
        $temporaryIds = [];

        foreach (explode(',', (string) ($parts[0] ?? '')) as $eventKey) {
            $eventKey = sanitize_key($eventKey);
            if ($eventKey !== '') {
                $eventKeys[$eventKey] = $eventKey;
            }
        }

        foreach (explode(',', rawurldecode((string) ($parts[2] ?? ''))) as $temporaryId) {
            $temporaryId = trim($temporaryId);
            if ($temporaryId !== '') {
                $temporaryIds[$temporaryId] = $temporaryId;
            }
        }

        return [
            'event_keys' => array_values($eventKeys),
            'ip' => trim(rawurldecode((string) ($parts[1] ?? ''))),
            'temporary_ids' => array_values($temporaryIds),
        ];
    }

    /**
     * @param array<int,mixed> $events
     * @param array<string,string> $ruleLabels
     * @return array<string,string>
     */
    private function wafRecentBlockReasonsByIp(array $events, array $ruleLabels): array
    {
        $reasons = [];

        foreach ($events as $event) {
            if (!is_array($event) || !$this->wafEventWasBlocked($event)) {
                continue;
            }

            $ip = trim((string) ($event['ip'] ?? ''));
            if ($ip === '' || $ip === 'unknown' || isset($reasons[$ip])) {
                continue;
            }

            $reasons[$ip] = $this->wafAccessEventReason($event, $ruleLabels);
        }

        return $reasons;
    }

    /**
     * @param array<string,mixed> $event
     */
    private function wafEventWasBlocked(array $event): bool
    {
        return (string) ($event['action'] ?? '') === 'block' || (string) ($event['event'] ?? '') === 'waf.block';
    }

    /**
     * @param array<string,mixed> $event
     */
    private function wafAccessEventStatus(array $event): string
    {
        if ($this->wafEventWasBlocked($event)) {
            return __('Blocked', 'xerotact-shield-x-security');
        }

        if (!empty($event['would_block'])) {
            return __('Would block', 'xerotact-shield-x-security');
        }

        return __('Logged WAF hit', 'xerotact-shield-x-security');
    }

    /**
     * @param array<string,mixed> $event
     */
    private function wafAccessEventStatusType(array $event): string
    {
        if ($this->wafEventWasBlocked($event)) {
            return 'blocked';
        }

        return !empty($event['would_block']) ? 'would_block' : 'waf_hit';
    }

    /**
     * @param array<string,mixed> $event
     * @param array<string,string> $ruleLabels
     */
    private function wafAccessEventReason(array $event, array $ruleLabels): string
    {
        $parts = [];
        $reason = trim((string) ($event['reason'] ?? ''));

        if ($reason !== '') {
            $parts[] = $reason;
        }

        $rules = isset($event['rules']) && is_array($event['rules']) ? array_values(array_filter(array_map('strval', $event['rules']))) : [];
        if (!empty($rules)) {
            $labels = [];

            foreach ($rules as $ruleId) {
                $labels[] = $ruleLabels[$ruleId] ?? $ruleId;
            }

            $parts[] = sprintf(
                /* translators: %s is a comma-separated list of WAF rules. */
                __('Rules: %s', 'xerotact-shield-x-security'),
                implode(', ', array_slice($labels, 0, 3))
            );
        }

        $request = trim((string) ($event['method'] ?? '') . ' ' . (string) ($event['uri'] ?? ''));
        if ($request !== '') {
            $parts[] = sprintf(
                /* translators: %s is the HTTP request method and URI. */
                __('Request: %s', 'xerotact-shield-x-security'),
                $request
            );
        }

        $matched = trim((string) ($event['matched_value'] ?? ''));
        if ($matched !== '') {
            $parts[] = sprintf(
                /* translators: %s is the matched WAF value. */
                __('Matched: %s', 'xerotact-shield-x-security'),
                $matched
            );
        }

        return !empty($parts) ? implode(' / ', $parts) : __('Recent WAF event.', 'xerotact-shield-x-security');
    }

    /**
     * @param array<string,mixed> $event
     */
    private function wafAccessEventSource(array $event): string
    {
        $rules = isset($event['rules']) && is_array($event['rules']) ? implode(' ', array_map('strval', $event['rules'])) : '';
        $haystack = strtolower(trim((string) ($event['reason'] ?? '') . ' ' . (string) ($event['uri'] ?? '') . ' ' . $rules));

        if (strpos($haystack, 'wp-login') !== false || strpos($haystack, 'login') !== false) {
            return __('WAF / login', 'xerotact-shield-x-security');
        }

        if (strpos($haystack, 'xmlrpc') !== false || strpos($haystack, 'xml-rpc') !== false) {
            return __('WAF / XML-RPC', 'xerotact-shield-x-security');
        }

        return __('WAF', 'xerotact-shield-x-security');
    }

    public function renderLoginSecurity(): void
    {
        if ($this->renderAsyncPageShell('shield-x-login-security')) {
            return;
        }

        $status = $this->statusForPage();
        $loginSecurity = isset($status['login_security']) && is_array($status['login_security']) ? $status['login_security'] : (new LoginSecuritySettings())->all();
        $humanChallenge = HumanChallengeSettings::fromWordPress()->all();
        $hardening = isset($status['hardening']) && is_array($status['hardening']) ? $status['hardening'] : HardeningSettings::fromWordPress()->all();
        $rateLimit = isset($status['rate_limit']) && is_array($status['rate_limit']) ? $status['rate_limit'] : [];
        $rateSettings = isset($rateLimit['settings']) && is_array($rateLimit['settings']) ? $rateLimit['settings'] : (new RateLimitSettings())->all();
        $security = LoginSecurity::fromWordPress();
        $adminUsers = $security->adminUsers();
        $sessionReview = $security->sessionReview();
        $rolesObject = function_exists('wp_roles') ? wp_roles() : null;
        $availableRoles = is_object($rolesObject) && isset($rolesObject->roles) && is_array($rolesObject->roles) ? $rolesObject->roles : [];
        $requiredTwoFactorRoles = isset($loginSecurity['two_factor_required_roles']) && is_array($loginSecurity['two_factor_required_roles']) ? array_map('strval', $loginSecurity['two_factor_required_roles']) : [];
        $protectedSessionRoles = isset($loginSecurity['session_protected_roles']) && is_array($loginSecurity['session_protected_roles']) ? array_map('strval', $loginSecurity['session_protected_roles']) : [];
        $userId = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;
        $userState = $userId > 0 ? $security->userState($userId) : [];
        $codesId = $this->queryKey('shieldx_codes');
        $recoveryCodes = $userId > 0 ? $security->consumeCodesForDisplay($userId, $codesId) : [];
        $setupError = $this->queryText('shieldx_2fa_error');
        $loginSecurityError = $this->queryText('shieldx_login_security_error');
        $pendingSecret = '';
        $otpauthUri = '';
        $setupCryptoError = '';

        if ($userId > 0 && empty($userState['enabled'])) {
            try {
                $pendingSecret = $security->pendingSecret($userId);
                $otpauthUri = $security->otpauthUriForUser($userId, $pendingSecret);
            } catch (\Throwable $error) {
                $setupCryptoError = $error->getMessage();
            }
        }

        $this->openPage('shield-x-login-security', $status);
        ?>
        <?php if ($setupError !== '') : ?>
            <div class="notice notice-error is-dismissible">
                <p><?php echo esc_html($setupError); ?></p>
            </div>
        <?php endif; ?>

        <?php if ($loginSecurityError !== '') : ?>
            <div class="notice notice-error is-dismissible">
                <p><?php echo esc_html($loginSecurityError); ?></p>
            </div>
        <?php endif; ?>

        <?php if ($this->queryKey('shieldx_setup') === 'required') : ?>
            <section class="shieldx-alert shieldx-alert--info">
                <strong><?php echo esc_html__('Authenticator setup required', 'xerotact-shield-x-security'); ?></strong>
                <span><?php echo esc_html__('Set up SHIELD-X 2FA for this administrator account to continue.', 'xerotact-shield-x-security'); ?></span>
            </section>
        <?php endif; ?>

        <section class="shieldx-metrics" aria-label="<?php echo esc_attr__('SHIELD-X login security summary', 'xerotact-shield-x-security'); ?>">
            <?php
            $this->renderMetric(__('2FA status', 'xerotact-shield-x-security'), !empty($userState['enabled']) ? __('Enabled', 'xerotact-shield-x-security') : __('Setup needed', 'xerotact-shield-x-security'), !empty($userState['enabled']) ? 'good' : 'warn', 'fingerprint-scan');
            $this->renderMetric(__('Recovery codes', 'xerotact-shield-x-security'), (string) ($userState['recovery_codes_remaining'] ?? 0), !empty($userState['recovery_codes_remaining']) ? 'info' : 'warn', 'lock');
            $this->renderMetric(__('Required users pending', 'xerotact-shield-x-security'), (string) ($loginSecurity['required_users_pending'] ?? 0), !empty($loginSecurity['required_users_pending']) ? 'warn' : 'good', 'group');
            $this->renderMetric(__('Recovery email', 'xerotact-shield-x-security'), (string) ($loginSecurity['recovery_email_status'] ?? 'pending'), (string) ($loginSecurity['recovery_email_status'] ?? '') === 'sent' ? 'good' : 'warn', 'mail');
            $this->renderMetric(__('Magic Login', 'xerotact-shield-x-security'), !empty($loginSecurity['magic_login_enabled']) ? __('Enabled', 'xerotact-shield-x-security') : __('Off', 'xerotact-shield-x-security'), !empty($loginSecurity['magic_login_enabled']) ? 'good' : 'warn', 'mail-open');
            ?>
        </section>

        <div id="shieldx-lost-authenticator-panel" class="shieldx-login-recovery-panel" data-shieldx-disclosure-panel hidden>
            <?php $this->renderLostAuthenticatorPanel($loginSecurity, $userState, false, $adminUsers); ?>
        </div>

        <?php if (!empty($recoveryCodes)) : ?>
            <section class="shieldx-panel shieldx-panel--full shieldx-recovery-codes">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('Save Recovery Codes', 'xerotact-shield-x-security'); ?></h2>
                    <div class="shieldx-section-head-actions">
                        <span><?php echo esc_html__('Shown once', 'xerotact-shield-x-security'); ?></span>
                        <button
                            type="button"
                            class="button"
                            data-shieldx-download-recovery-codes
                            data-shieldx-download-label="<?php echo esc_attr__('Download codes', 'xerotact-shield-x-security'); ?>"
                            data-shieldx-downloaded-label="<?php echo esc_attr__('Downloaded', 'xerotact-shield-x-security'); ?>"
                            data-shieldx-download-title="<?php echo esc_attr__('SHIELD-X Recovery Codes', 'xerotact-shield-x-security'); ?>"
                            data-shieldx-download-note="<?php echo esc_attr__('Keep these codes private. Each code can be used once if authenticator access is lost.', 'xerotact-shield-x-security'); ?>"
                        >
                            <?php echo esc_html__('Download codes', 'xerotact-shield-x-security'); ?>
                        </button>
                    </div>
                </div>
                <div class="shieldx-code-grid">
                    <?php foreach ($recoveryCodes as $code) : ?>
                        <code><?php echo esc_html($code); ?></code>
                    <?php endforeach; ?>
                </div>
            </section>
        <?php endif; ?>

        <div class="shieldx-layout shieldx-layout--single">
            <section id="shieldx-authenticator-2fa" class="shieldx-panel shieldx-panel--full" data-shieldx-accordion-open="1">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('Authenticator 2FA', 'xerotact-shield-x-security'); ?></h2>
                    <span><?php echo esc_html(!empty($userState['enabled']) ? __('Protected', 'xerotact-shield-x-security') : __('Enrollment pending', 'xerotact-shield-x-security')); ?></span>
                </div>

                <?php if (!empty($userState['enabled'])) : ?>
                    <dl class="shieldx-detail-list">
                        <?php $this->renderDetail(__('Account', 'xerotact-shield-x-security'), (string) ($userState['account_label'] ?? '')); ?>
                        <?php $this->renderDetail(__('Enabled at', 'xerotact-shield-x-security'), !empty($userState['enabled_at']) ? $this->formatTime((string) $userState['enabled_at']) : __('Unknown', 'xerotact-shield-x-security')); ?>
                        <?php $this->renderDetail(__('Recovery codes left', 'xerotact-shield-x-security'), (string) ($userState['recovery_codes_remaining'] ?? 0)); ?>
                    </dl>
                    <div class="shieldx-inline-actions shieldx-auth-actions">
                        <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                            <input type="hidden" name="action" value="shieldx_regenerate_2fa_codes">
                            <?php wp_nonce_field('shieldx_regenerate_2fa_codes'); ?>
                            <button type="submit" class="button"><?php echo esc_html__('Regenerate recovery codes', 'xerotact-shield-x-security'); ?></button>
                        </form>
                        <button
                            type="button"
                            class="button shieldx-button-danger"
                            data-shieldx-form-modal-open
                            aria-controls="shieldx-reset-2fa-modal"
                        >
                            <?php echo esc_html__('Reset 2FA', 'xerotact-shield-x-security'); ?>
                        </button>
                    </div>
                    <div id="shieldx-reset-2fa-modal" class="shieldx-form-modal" data-shieldx-form-modal hidden>
                        <div class="shieldx-form-modal-backdrop" data-shieldx-form-modal-close></div>
                        <section class="shieldx-form-modal-card" role="dialog" aria-modal="true" aria-labelledby="shieldx-reset-2fa-title" aria-describedby="shieldx-reset-2fa-desc">
                            <button type="button" class="shieldx-modal-close" data-shieldx-form-modal-close aria-label="<?php echo esc_attr__('Close dialog', 'xerotact-shield-x-security'); ?>">
                                <span aria-hidden="true">&times;</span>
                            </button>
                            <p class="shieldx-modal-kicker"><?php echo esc_html__('Protected action', 'xerotact-shield-x-security'); ?></p>
                            <h2 id="shieldx-reset-2fa-title"><?php echo esc_html__('Reset 2FA', 'xerotact-shield-x-security'); ?></h2>
                            <p id="shieldx-reset-2fa-desc" class="shieldx-muted"><?php echo esc_html__('Enter the current administrator password to disable authenticator 2FA for this account. After access is restored, enroll a fresh authenticator and save new recovery codes.', 'xerotact-shield-x-security'); ?></p>
                            <form class="shieldx-modal-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                                <input type="hidden" name="action" value="shieldx_disable_2fa">
                                <?php wp_nonce_field('shieldx_disable_2fa'); ?>
                                <label class="shieldx-field">
                                    <span><?php echo esc_html__('Current administrator password', 'xerotact-shield-x-security'); ?></span>
                                    <input type="password" name="shieldx_current_password" autocomplete="current-password" required>
                                </label>
                                <p class="description"><?php echo esc_html__('Required even when this admin browser session is already open.', 'xerotact-shield-x-security'); ?></p>
                                <div class="shieldx-modal-actions">
                                    <button type="submit" class="button shieldx-button-danger"><?php echo esc_html__('Confirm and reset 2FA', 'xerotact-shield-x-security'); ?></button>
                                    <button type="button" class="button" data-shieldx-form-modal-close><?php echo esc_html__('Cancel', 'xerotact-shield-x-security'); ?></button>
                                </div>
                            </form>
                        </section>
                    </div>
                    <div class="shieldx-reset-2fa-card">
                        <div class="shieldx-section-head shieldx-section-head--compact">
                            <h3><?php echo esc_html__('Reset 2FA by email', 'xerotact-shield-x-security'); ?></h3>
                            <span><?php echo esc_html__('15 minute code', 'xerotact-shield-x-security'); ?></span>
                        </div>
                        <p class="shieldx-muted"><?php echo esc_html__('Confirm the current administrator password, then SHIELD-X emails a one-time reset code to the configured recovery email. Enter that code here to disable authenticator 2FA for this account.', 'xerotact-shield-x-security'); ?></p>
                        <div class="shieldx-reset-2fa-grid">
                            <form class="shieldx-sensitive-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                                <input type="hidden" name="action" value="shieldx_request_2fa_reset">
                                <?php wp_nonce_field('shieldx_request_2fa_reset'); ?>
                                <label class="shieldx-field shieldx-field--compact">
                                    <span><?php echo esc_html__('Current administrator password', 'xerotact-shield-x-security'); ?></span>
                                    <input type="password" name="shieldx_current_password" autocomplete="current-password" required>
                                </label>
                                <button type="submit" class="button"><?php echo esc_html__('Email reset code', 'xerotact-shield-x-security'); ?></button>
                            </form>
                            <form class="shieldx-sensitive-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                                <input type="hidden" name="action" value="shieldx_confirm_2fa_reset">
                                <?php wp_nonce_field('shieldx_confirm_2fa_reset'); ?>
                                <label class="shieldx-field shieldx-field--compact">
                                    <span><?php echo esc_html__('Email reset code', 'xerotact-shield-x-security'); ?></span>
                                    <input type="text" name="shieldx_reset_code" inputmode="text" autocomplete="one-time-code" pattern="[A-Za-z0-9 ]{6,16}" required>
                                </label>
                                <button type="submit" class="button shieldx-button-danger"><?php echo esc_html__('Reset and disable 2FA', 'xerotact-shield-x-security'); ?></button>
                            </form>
                        </div>
                    </div>
                <?php elseif ($setupCryptoError !== '') : ?>
                    <section class="shieldx-alert shieldx-alert--warning">
                        <strong><?php echo esc_html__('2FA setup is blocked by host crypto configuration.', 'xerotact-shield-x-security'); ?></strong>
                        <span><?php echo esc_html($setupCryptoError); ?></span>
                    </section>
                <?php else : ?>
                    <div class="shieldx-setup-grid">
                        <div class="shieldx-qr-card">
                            <span><?php echo esc_html__('Scan QR code', 'xerotact-shield-x-security'); ?></span>
                            <div class="shieldx-qr-wrap">
                                <?php echo wp_kses(QrCode::svg($otpauthUri), $this->allowedQrSvg()); ?>
                            </div>
                        </div>
                        <div class="shieldx-secret-card">
                            <span><?php echo esc_html__('Setup key', 'xerotact-shield-x-security'); ?></span>
                            <code class="shieldx-setup-key-value"><?php echo esc_html($security->formattedSecret($pendingSecret)); ?></code>
                            <div class="shieldx-secret-actions">
                                <button type="button" class="button" data-shieldx-copy="<?php echo esc_attr($pendingSecret); ?>"><?php echo esc_html__('Copy setup key', 'xerotact-shield-x-security'); ?></button>
                                <button type="button" class="button" data-shieldx-copy="<?php echo esc_attr($otpauthUri); ?>"><?php echo esc_html__('Copy authenticator URI', 'xerotact-shield-x-security'); ?></button>
                            </div>
                        </div>
                        <form class="shieldx-verify-card" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                            <input type="hidden" name="action" value="shieldx_enable_2fa">
                            <?php wp_nonce_field('shieldx_enable_2fa'); ?>
                            <label class="shieldx-field">
                                <span><?php echo esc_html__('Authenticator code', 'xerotact-shield-x-security'); ?></span>
                                <input type="text" name="totp_code" inputmode="numeric" autocomplete="one-time-code" pattern="[0-9 ]{6,12}" required>
                            </label>
                            <button type="submit" class="button button-primary"><?php echo esc_html__('Verify and enable 2FA', 'xerotact-shield-x-security'); ?></button>
                        </form>
                    </div>
                <?php endif; ?>
            </section>

            <section id="shieldx-login-entry-gate" class="shieldx-panel shieldx-panel--full" data-shieldx-accordion-open="1">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('Login Entry Gate', 'xerotact-shield-x-security'); ?></h2>
                    <span><?php echo esc_html(!empty($hardening['admin_gate_enabled']) ? __('Active', 'xerotact-shield-x-security') : __('Optional', 'xerotact-shield-x-security')); ?></span>
                </div>
                <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                    <input type="hidden" name="action" value="shieldx_save_login_security">
                    <input type="hidden" name="shieldx_login_settings_section" value="gate">
                    <?php wp_nonce_field('shieldx_save_login_security'); ?>
                    <div class="shieldx-hardening-option shieldx-admin-gate-card">
                        <label class="shieldx-option-toggle">
                            <input type="checkbox" name="admin_gate_enabled" value="1" <?php checked(!empty($hardening['admin_gate_enabled'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Gate wp-admin and wp-login.php', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Requests without the keyword cookie receive a 404 response. REST, WordPress AJAX/post handlers, cron, recovery, and already authenticated WordPress sessions stay reachable.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <div class="shieldx-admin-gate-controls">
                            <label class="shieldx-keyword-field">
                                <span><?php echo esc_html__('Admin keyword', 'xerotact-shield-x-security'); ?></span>
                                <input type="text" name="admin_gate_key" value="<?php echo esc_attr((string) ($hardening['admin_gate_key'] ?? '')); ?>" pattern="[a-z0-9_-]{3,48}" maxlength="48" autocomplete="off">
                            </label>
                            <div class="shieldx-entry-url">
                                <span><?php echo esc_html__('Entry URL', 'xerotact-shield-x-security'); ?></span>
                                <code><?php echo esc_html((string) ($hardening['admin_entry_url'] ?? '/wp-admin/')); ?></code>
                            </div>
                        </div>
                        <label class="shieldx-option-toggle">
                            <input type="checkbox" name="admin_gate_strict_cookie" value="1" <?php checked(!empty($hardening['admin_gate_strict_cookie'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Use a Strict admin-gate cookie on HTTPS', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Uses SameSite=Strict only when HTTPS is active; HTTP keeps the compatibility-safe Lax setting.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                    </div>
                    <div class="shieldx-form-actions">
                        <button type="submit" class="button button-primary"><?php echo esc_html__('Save login gate', 'xerotact-shield-x-security'); ?></button>
                    </div>
                </form>
            </section>

            <section id="shieldx-login-bruteforce" class="shieldx-panel shieldx-panel--full" data-shieldx-accordion-open="1">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('Login Brute-Force Protection', 'xerotact-shield-x-security'); ?></h2>
                    <?php /* translators: %d is the number of active login lockouts. */ ?>
                    <span><?php echo esc_html(sprintf(__('%d active lockouts', 'xerotact-shield-x-security'), (int) ($rateLimit['login']['locked_identities'] ?? 0))); ?></span>
                </div>
                <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                    <input type="hidden" name="action" value="shieldx_save_login_security">
                    <input type="hidden" name="shieldx_login_settings_section" value="lockout">
                    <?php wp_nonce_field('shieldx_save_login_security'); ?>
                    <div class="shieldx-settings-grid shieldx-toggle-group" data-shieldx-toggle-group data-shieldx-toggle-input="login_protection_enabled">
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="login_protection_enabled" value="1" <?php checked(!empty($rateSettings['login_protection_enabled'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Failed-login lockout', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Locks repeated failed authentication attempts with IP and username-aware counters.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <div class="shieldx-toggle-fields" data-shieldx-toggle-fields>
                            <?php $this->renderNumberField('login_failure_limit', __('Failure limit', 'xerotact-shield-x-security'), (int) ($rateSettings['login_failure_limit'] ?? 5), 2, 50); ?>
                            <?php $this->renderNumberField('login_window_seconds', __('Failure window', 'xerotact-shield-x-security'), (int) ($rateSettings['login_window_seconds'] ?? 900), 60, 86400); ?>
                            <?php $this->renderNumberField('login_lockout_seconds', __('Lockout length', 'xerotact-shield-x-security'), (int) ($rateSettings['login_lockout_seconds'] ?? 900), 60, 86400); ?>
                        </div>
                    </div>
                    <div class="shieldx-form-actions">
                        <button type="submit" class="button button-primary"><?php echo esc_html__('Save brute-force protection', 'xerotact-shield-x-security'); ?></button>
                    </div>
                </form>
            </section>

            <?php $this->renderLoginSecurityAdminUsers($adminUsers); ?>

            <?php
            $challengeLoginRequested = !empty($humanChallenge['login_enabled'])
                || !empty($humanChallenge['login_fullscreen_enabled'])
                || !empty($humanChallenge['password_reset_enabled']);
            $challengeNeedsSetup = $challengeLoginRequested && empty($humanChallenge['configured']);
            ?>
            <section id="shieldx-recovery-email-policy" class="shieldx-panel shieldx-panel--full shieldx-login-policy-panel">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('Recovery Email & Policy', 'xerotact-shield-x-security'); ?></h2>
                    <span><?php echo esc_html((string) ($loginSecurity['recovery_email'] ?? '')); ?></span>
                </div>
                <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                    <input type="hidden" name="action" value="shieldx_save_login_security">
                    <input type="hidden" name="shieldx_login_settings_section" value="policy">
                    <?php wp_nonce_field('shieldx_save_login_security'); ?>

                    <div class="shieldx-settings-grid">
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="login_security_enabled" value="1" <?php checked(!empty($loginSecurity['enabled'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Enable login security', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Adds authenticator 2FA and owner recovery controls.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <fieldset class="shieldx-field">
                            <legend><?php echo esc_html__('Require 2FA for roles', 'xerotact-shield-x-security'); ?></legend>
                            <?php foreach ($availableRoles as $roleKey => $roleDetails) : ?>
                                <label class="shieldx-inline-check">
                                    <input type="checkbox" name="two_factor_required_roles[]" value="<?php echo esc_attr((string) $roleKey); ?>" <?php checked(in_array((string) $roleKey, $requiredTwoFactorRoles, true)); ?>>
                                    <span><?php echo esc_html(translate_user_role((string) ($roleDetails['name'] ?? $roleKey))); ?></span>
                                </label>
                            <?php endforeach; ?>
                        </fieldset>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="session_policy_enabled" value="1" <?php checked(!empty($loginSecurity['session_policy_enabled'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Enforce protected session limits', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Applies maximum-age and idle expiration only to the selected roles.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <fieldset class="shieldx-field">
                            <legend><?php echo esc_html__('Protect sessions for roles', 'xerotact-shield-x-security'); ?></legend>
                            <?php foreach ($availableRoles as $roleKey => $roleDetails) : ?>
                                <label class="shieldx-inline-check">
                                    <input type="checkbox" name="session_protected_roles[]" value="<?php echo esc_attr((string) $roleKey); ?>" <?php checked(in_array((string) $roleKey, $protectedSessionRoles, true)); ?>>
                                    <span><?php echo esc_html(translate_user_role((string) ($roleDetails['name'] ?? $roleKey))); ?></span>
                                </label>
                            <?php endforeach; ?>
                        </fieldset>
                        <?php $this->renderNumberField('session_maximum_age_days', __('Maximum session age (days)', 'xerotact-shield-x-security'), max(1, (int) round(((int) ($loginSecurity['session_maximum_age'] ?? 604800)) / 86400)), 1, 30); ?>
                        <?php $this->renderNumberField('session_idle_timeout_minutes', __('Idle timeout (minutes)', 'xerotact-shield-x-security'), max(15, (int) round(((int) ($loginSecurity['session_idle_timeout'] ?? 7200)) / 60)), 15, 10080); ?>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="magic_login_enabled" value="1" <?php checked(!empty($loginSecurity['magic_login_enabled'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Magic Login ★', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Passwordless admin login via one-time email links, QR scan, or 6-digit codes that expire after 15 minutes.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-field">
                            <span><?php echo esc_html__('Separate recovery email', 'xerotact-shield-x-security'); ?></span>
                            <input type="email" name="recovery_email" value="<?php echo esc_attr((string) ($loginSecurity['recovery_email'] ?? '')); ?>" placeholder="<?php echo esc_attr__('security-owner@example.com', 'xerotact-shield-x-security'); ?>">
                        </label>
                    </div>
                    <?php if (empty($loginSecurity['recovery_email'])) : ?>
                        <div class="shieldx-alert shieldx-alert--warning">
                            <strong><?php echo esc_html__('Recovery email is not configured', 'xerotact-shield-x-security'); ?></strong>
                            <span><?php echo esc_html__('Use a separate mailbox from the WordPress admin email so owner recovery and 2FA reset mail have an independent destination.', 'xerotact-shield-x-security'); ?></span>
                        </div>
                    <?php endif; ?>

                    <div class="shieldx-login-policy-grid">
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="send_recovery_email" value="1">
                            <span>
                                <strong><?php echo esc_html__('Send new recovery email', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Rotates the recovery token and sends it to the separate recovery mailbox.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="send_2fa_setup_emails" value="1">
                            <span>
                                <strong><?php echo esc_html__('Email 2FA setup to required users', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Sends each unenrolled user in a required role a link to the authenticated setup screen. QR codes and setup keys are only shown inside WordPress admin.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <dl class="shieldx-detail-list">
                            <?php $this->renderDetail(__('Last recovery email', 'xerotact-shield-x-security'), !empty($loginSecurity['last_recovery_email_sent_at']) ? $this->formatTime((string) $loginSecurity['last_recovery_email_sent_at']) : __('Not sent', 'xerotact-shield-x-security')); ?>
                            <?php $this->renderDetail(__('Activation email', 'xerotact-shield-x-security'), !empty($loginSecurity['activation_email_sent_at']) ? $this->formatTime((string) $loginSecurity['activation_email_sent_at']) : __('Not sent', 'xerotact-shield-x-security')); ?>
                            <?php $this->renderDetail(__('Recovery token', 'xerotact-shield-x-security'), !empty($loginSecurity['owner_recovery_available']) ? __('Available', 'xerotact-shield-x-security') : __('Not generated', 'xerotact-shield-x-security')); ?>
                            <?php $this->renderDetail(__('Magic Login mail', 'xerotact-shield-x-security'), $this->formatLabel((string) ($loginSecurity['magic_login_email_status'] ?? 'pending'))); ?>
                            <?php $this->renderDetail(__('Last Magic Login email', 'xerotact-shield-x-security'), !empty($loginSecurity['last_magic_login_email_sent_at']) ? $this->formatTime((string) $loginSecurity['last_magic_login_email_sent_at']) : __('Not sent', 'xerotact-shield-x-security')); ?>
                            <?php if (!empty($loginSecurity['recovery_email_error'])) : ?>
                                <?php $this->renderDetail(__('Mail status', 'xerotact-shield-x-security'), (string) $loginSecurity['recovery_email_error']); ?>
                            <?php endif; ?>
                            <?php if (!empty($loginSecurity['magic_login_email_error'])) : ?>
                                <?php $this->renderDetail(__('Magic Login error', 'xerotact-shield-x-security'), (string) $loginSecurity['magic_login_email_error']); ?>
                            <?php endif; ?>
                        </dl>
                    </div>

                    <div class="shieldx-form-actions">
                        <button type="submit" class="button button-primary"><?php echo esc_html__('Save login security', 'xerotact-shield-x-security'); ?></button>
                    </div>
                </form>
            </section>

            <section id="shieldx-session-review" class="shieldx-panel shieldx-panel--full">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('Protected Sessions', 'xerotact-shield-x-security'); ?></h2>
                    <span><?php echo esc_html(sprintf(
                        /* translators: %d is the number of users covered by session or 2FA policy. */
                        _n('%d protected user', '%d protected users', count($sessionReview), 'xerotact-shield-x-security'),
                        count($sessionReview)
                    )); ?></span>
                </div>
                <div class="shieldx-table-wrap">
                    <table class="widefat striped">
                        <thead>
                            <tr>
                                <th><?php echo esc_html__('User', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Roles', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Sessions', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('2FA', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Action', 'xerotact-shield-x-security'); ?></th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php if ($sessionReview === []) : ?>
                                <tr><td colspan="5"><?php echo esc_html__('No users are currently covered by role-based 2FA or session policy.', 'xerotact-shield-x-security'); ?></td></tr>
                            <?php else : ?>
                                <?php foreach ($sessionReview as $sessionUser) : ?>
                                    <tr>
                                        <td>
                                            <strong><?php echo esc_html((string) ($sessionUser['display_name'] ?? $sessionUser['user_login'] ?? '')); ?></strong>
                                            <small><?php echo esc_html((string) ($sessionUser['user_login'] ?? '')); ?></small>
                                        </td>
                                        <td><?php echo esc_html(implode(', ', array_map('strval', (array) ($sessionUser['roles'] ?? [])))); ?></td>
                                        <td><?php echo esc_html((string) (int) ($sessionUser['sessions'] ?? 0)); ?></td>
                                        <td><?php echo esc_html(!empty($sessionUser['two_factor_enabled']) ? __('Enrolled', 'xerotact-shield-x-security') : (!empty($sessionUser['two_factor_required']) ? __('Required', 'xerotact-shield-x-security') : __('Optional', 'xerotact-shield-x-security'))); ?></td>
                                        <td>
                                            <?php if ((int) ($sessionUser['sessions'] ?? 0) > (empty($sessionUser['current_user']) ? 0 : 1)) : ?>
                                                <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                                                    <input type="hidden" name="action" value="shieldx_revoke_login_sessions">
                                                    <input type="hidden" name="user_id" value="<?php echo esc_attr((string) (int) ($sessionUser['user_id'] ?? 0)); ?>">
                                                    <?php wp_nonce_field('shieldx_revoke_login_sessions'); ?>
                                                    <button type="submit" class="button"><?php echo esc_html(!empty($sessionUser['current_user']) ? __('Revoke other sessions', 'xerotact-shield-x-security') : __('Revoke all sessions', 'xerotact-shield-x-security')); ?></button>
                                                </form>
                                            <?php else : ?>
                                                <span><?php echo esc_html__('No other sessions', 'xerotact-shield-x-security'); ?></span>
                                            <?php endif; ?>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                        </tbody>
                    </table>
                </div>
            </section>

            <section id="shieldx-challenge-captcha" class="shieldx-panel shieldx-panel--full shieldx-login-challenge-panel">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('Challenge (Captcha)', 'xerotact-shield-x-security'); ?></h2>
                    <span><?php echo esc_html(!empty($humanChallenge['configured']) ? __('Configured', 'xerotact-shield-x-security') : __('Needs setup', 'xerotact-shield-x-security')); ?></span>
                    <?php
                    $this->renderSectionSettingsLink(
                        admin_url('admin.php?page=shield-x-system#shieldx-human-challenge'),
                        __('Open Challenge (Captcha) settings in System / Configuration', 'xerotact-shield-x-security')
                    );
                    ?>
                </div>
                <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                    <input type="hidden" name="action" value="shieldx_save_login_security">
                    <input type="hidden" name="shieldx_login_settings_section" value="challenge">
                    <?php wp_nonce_field('shieldx_save_login_security'); ?>

                    <div class="shieldx-settings-grid">
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="human_challenge_login_enabled" value="1" <?php checked(!empty($humanChallenge['login_enabled'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Human challenge on login', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Shows the configured Cap.js Challenge (Captcha) on wp-login.php.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="human_challenge_login_fullscreen_enabled" value="1" <?php checked(!empty($humanChallenge['login_fullscreen_enabled'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Full-screen challenge before login', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Shows the configured challenge before the WordPress login form. The normal login form appears only after the challenge passes.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="human_challenge_password_reset_enabled" value="1" <?php checked(!empty($humanChallenge['password_reset_enabled'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Human challenge on password reset', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Adds the configured challenge to the lost-password form.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <dl class="shieldx-detail-list shieldx-detail-list--compact">
                            <?php $this->renderDetail(__('Human challenge provider', 'xerotact-shield-x-security'), $this->humanChallengeProviderLabel((string) ($humanChallenge['provider'] ?? 'none'))); ?>
                            <?php $this->renderDetail(__('Human challenge status', 'xerotact-shield-x-security'), !empty($humanChallenge['configured']) ? __('Configured', 'xerotact-shield-x-security') : __('Not configured', 'xerotact-shield-x-security')); ?>
                        </dl>
                        <?php if ($challengeNeedsSetup) : ?>
                            <section class="shieldx-alert shieldx-alert--warning shieldx-human-challenge-warning">
                                <strong><?php echo esc_html__('Cap.js Challenge (Captcha) is not configured yet.', 'xerotact-shield-x-security'); ?></strong>
                                <span><?php echo esc_html__('The login challenge toggles are saved, but no challenge will display until the Cap.js Challenge (Captcha) endpoint, site key, and secret key are configured.', 'xerotact-shield-x-security'); ?></span>
                                <a class="button" href="<?php echo esc_url(admin_url('admin.php?page=shield-x-system#shieldx-human-challenge')); ?>"><?php echo esc_html__('Configure Cap.js Challenge (Captcha)', 'xerotact-shield-x-security'); ?></a>
                            </section>
                        <?php endif; ?>
                    </div>

                    <div class="shieldx-form-actions">
                        <button type="submit" class="button button-primary"><?php echo esc_html__('Save Challenge (Captcha)', 'xerotact-shield-x-security'); ?></button>
                    </div>
                </form>
            </section>
        </div>
        <?php
        $this->closePage();
    }

    private function renderLostAuthenticatorPanel(array $loginSecurity, array $userState, bool $setupContext = false, array $adminUsers = []): void
    {
        $codesRemaining = (int) ($userState['recovery_codes_remaining'] ?? 0);
        $email = (string) ($loginSecurity['recovery_email'] ?? '');
        $emailStatus = (string) ($loginSecurity['recovery_email_status'] ?? 'pending');
        $ownerRecoveryReady = !empty($loginSecurity['owner_recovery_available']);
        $emergencyRecoveryReady = !empty($loginSecurity['emergency_recovery_available']);
        $panelId = $setupContext ? 'shieldx-lost-authenticator-setup' : 'shieldx-lost-authenticator';
        $guides = $this->lostAuthenticatorGuides($setupContext, $adminUsers);
        $codesAvailability = __('None saved for this account', 'xerotact-shield-x-security');
        if ($codesRemaining > 0) {
            /* translators: %d is the number of unused SHIELD-X recovery codes remaining for the current administrator. */
            $codesAvailability = sprintf(_n('%d available', '%d available', $codesRemaining, 'xerotact-shield-x-security'), $codesRemaining);
        }
        ?>
        <section id="<?php echo esc_attr($panelId); ?>" class="shieldx-panel shieldx-panel--full shieldx-lost-authenticator">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Lost authenticator?', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html($codesRemaining > 0 || $ownerRecoveryReady || $emergencyRecoveryReady ? __('Recovery paths ready', 'xerotact-shield-x-security') : __('Prepare recovery now', 'xerotact-shield-x-security')); ?></span>
            </div>
            <p class="shieldx-muted"><?php echo esc_html__('If the authenticator entry is deleted from the mobile app, the old QR code cannot be recovered. Use one of these recovery paths, then disable and re-enable 2FA to enroll a fresh authenticator.', 'xerotact-shield-x-security'); ?></p>
            <div class="shieldx-recovery-guide-grid">
                <?php
                foreach ($guides as $guide) {
                    $guide['meta'] = $guide['id'] === 'codes'
                        ? $codesAvailability
                        : (string) ($guide['meta'] ?? '');
                    if ($guide['id'] === 'email') {
                        $guide['meta'] = $email !== '' ? $email . ' - ' . $this->formatLabel($emailStatus) : __('No recovery email configured', 'xerotact-shield-x-security');
                    }
                    $this->renderRecoveryGuideCard($guide);
                }
                ?>
            </div>
            <div class="shieldx-inline-actions shieldx-lost-auth-actions">
                <?php if ($setupContext) : ?>
                    <a class="button" href="<?php echo esc_url(admin_url('admin.php?page=shield-x-login-security#shieldx-lost-authenticator')); ?>"><?php echo esc_html__('Review login recovery', 'xerotact-shield-x-security'); ?></a>
                <?php else : ?>
                    <a class="button" href="#shieldx-recovery-email-policy"><?php echo esc_html__('Review recovery email', 'xerotact-shield-x-security'); ?></a>
                <?php endif; ?>
                <a class="button" href="<?php echo esc_url(admin_url('admin.php?page=shield-x-login-security#shieldx-authenticator-2fa')); ?>"><?php echo esc_html__('Open 2FA controls', 'xerotact-shield-x-security'); ?></a>
            </div>
        </section>
        <?php
    }

    /**
     * @return array<int,array<string,mixed>>
     */
    private function lostAuthenticatorGuides(bool $setupContext = false, array $adminUsers = []): array
    {
        $loginSecurityUrl = admin_url('admin.php?page=shield-x-login-security');

        return [
            [
                'id' => 'codes',
                'kicker' => __('Best first option', 'xerotact-shield-x-security'),
                'title' => __('Use a recovery code', 'xerotact-shield-x-security'),
                'summary' => __('Enter any unused recovery code in the same field as the authenticator code. Each code works once.', 'xerotact-shield-x-security'),
                'modal_intro' => __('Use this when the user still has one of the downloaded SHIELD-X recovery codes.', 'xerotact-shield-x-security'),
                'steps' => [
                    __('Open the normal WordPress login page.', 'xerotact-shield-x-security'),
                    __('Enter the administrator username and password as usual.', 'xerotact-shield-x-security'),
                    __('When SHIELD-X asks for an authenticator code, paste one unused recovery code instead.', 'xerotact-shield-x-security'),
                    __('After login, open Login Security and regenerate recovery codes or enroll a fresh authenticator.', 'xerotact-shield-x-security'),
                ],
                'action_label' => __('Open 2FA controls', 'xerotact-shield-x-security'),
                'action_url' => $loginSecurityUrl . '#shieldx-authenticator-2fa',
            ],
            [
                'id' => 'email',
                'kicker' => __('Owner fallback', 'xerotact-shield-x-security'),
                'title' => __('Use the recovery email link', 'xerotact-shield-x-security'),
                'summary' => __('The private recovery link lets the owner sign in with the normal WordPress password and bypass 2FA once.', 'xerotact-shield-x-security'),
                'modal_intro' => __('Use this when the site owner still controls the configured recovery email inbox.', 'xerotact-shield-x-security'),
                'steps' => [
                    __('Open the latest SHIELD-X recovery email.', 'xerotact-shield-x-security'),
                    __('Click the private recovery link and sign in with the normal WordPress administrator password.', 'xerotact-shield-x-security'),
                    __('After access is restored, disable and re-enable 2FA for the account to create a new QR code.', 'xerotact-shield-x-security'),
                    __('Send a new recovery email so old recovery links are rotated.', 'xerotact-shield-x-security'),
                ],
                'action_label' => __('Review recovery email', 'xerotact-shield-x-security'),
                'action_url' => $loginSecurityUrl . '#shieldx-recovery-email-policy',
            ],
            [
                'id' => 'admin',
                'kicker' => __('Team fallback', 'xerotact-shield-x-security'),
                'title' => __('Ask another administrator', 'xerotact-shield-x-security'),
                'summary' => __('A trusted logged-in administrator can open Login Security and disable 2FA for the locked account.', 'xerotact-shield-x-security'),
                'meta' => __('Use only after verifying the account owner.', 'xerotact-shield-x-security'),
                'modal_intro' => __('Use this when another trusted administrator is already logged in and can confirm the locked account owner.', 'xerotact-shield-x-security'),
                'steps' => [
                    __('The logged-in administrator opens SHIELD-X Login Security.', 'xerotact-shield-x-security'),
                    __('They verify the locked account and check recent activity before making changes.', 'xerotact-shield-x-security'),
                    __('They disable 2FA for the locked account or help the owner enroll a new authenticator.', 'xerotact-shield-x-security'),
                    __('The owner logs in and immediately sets up a new authenticator and recovery codes.', 'xerotact-shield-x-security'),
                ],
                'action_label' => __('Open admin users', 'xerotact-shield-x-security'),
                'action_url' => $loginSecurityUrl . '#shieldx-admin-users',
            ],
            [
                'id' => 'server',
                'kicker' => __('Last resort', 'xerotact-shield-x-security'),
                'title' => __('Use server-level recovery', 'xerotact-shield-x-security'),
                'summary' => __('If all admin recovery paths are lost, reset SHIELD-X 2FA user meta through WP-CLI or the database after confirming ownership.', 'xerotact-shield-x-security'),
                'meta' => __('Document this before enabling enforcement.', 'xerotact-shield-x-security'),
                'commands' => [
                    [
                        'label' => __('Reset SHIELD-X 2FA for selected admin', 'xerotact-shield-x-security'),
                        'command' => $this->twoFactorRecoveryCliCommand(),
                        'user_options' => $this->twoFactorRecoveryCliUserOptions($adminUsers),
                        'note' => __('Choose the locked administrator, then copy and run this command from the WordPress root over SSH or cPanel Terminal.', 'xerotact-shield-x-security'),
                    ],
                ],
                'modal_intro' => __('Use this only when recovery codes, recovery email, and logged-in administrators are all unavailable.', 'xerotact-shield-x-security'),
                'steps' => [
                    __('Confirm site ownership and take a database backup or restore point first.', 'xerotact-shield-x-security'),
                    __('Choose the locked administrator from the dropdown so the WP-CLI command is generated with the correct user ID.', 'xerotact-shield-x-security'),
                    __('Use WP-CLI or a trusted database tool to remove only the affected administrator meta keys: shieldx_2fa_enabled, shieldx_2fa_secret, shieldx_2fa_pending_secret, and shieldx_2fa_recovery_hashes.', 'xerotact-shield-x-security'),
                    __('Have the owner log in with the normal WordPress password and immediately re-enroll 2FA.', 'xerotact-shield-x-security'),
                    __('Review Incident/Activities and Login Security afterward for suspicious access.', 'xerotact-shield-x-security'),
                ],
                'action_label' => __('Open system tools', 'xerotact-shield-x-security'),
                'action_url' => admin_url('admin.php?page=shield-x-system'),
            ],
        ];
    }

    private function twoFactorRecoveryCliCommand(int $userId = 0): string
    {
        $userId = $userId > 0 ? $userId : (function_exists('get_current_user_id') ? (int) get_current_user_id() : 0);
        $userId = $userId > 0 ? $userId : 1;
        $keys = 'array("shieldx_2fa_enabled","shieldx_2fa_secret","shieldx_2fa_pending_secret","shieldx_2fa_recovery_hashes","shieldx_2fa_enabled_at","shieldx_2fa_recovery_used_at")';

        return 'wp eval \'$user_id=' . (string) $userId . '; foreach (' . $keys . ' as $key) { delete_user_meta($user_id, $key); } WP_CLI::success("SHIELD-X 2FA disabled for user ID " . $user_id);\'';
    }

    /**
     * @param array<int,array<string,mixed>> $adminUsers
     * @return array<int,array<string,string>>
     */
    private function twoFactorRecoveryCliUserOptions(array $adminUsers): array
    {
        $options = [];

        foreach ($adminUsers as $admin) {
            $userId = (int) ($admin['user_id'] ?? 0);

            if ($userId <= 0) {
                continue;
            }

            $login = (string) ($admin['login'] ?? '');
            $email = (string) ($admin['email'] ?? '');
            $displayName = trim((string) ($admin['display_name'] ?? ''));
            $labelParts = [];

            $labelParts[] = '#' . (string) $userId;
            $labelParts[] = $displayName !== '' ? $displayName : ($login !== '' ? $login : __('Administrator', 'xerotact-shield-x-security'));

            if ($email !== '') {
                $labelParts[] = '<' . $email . '>';
            }

            if (!empty($admin['is_current'])) {
                $labelParts[] = __('current account', 'xerotact-shield-x-security');
            }

            $options[] = [
                'id' => (string) $userId,
                'label' => implode(' ', $labelParts),
                'command' => $this->twoFactorRecoveryCliCommand($userId),
            ];
        }

        return $options;
    }

    /**
     * @param array<string,mixed> $guide
     */
    private function renderRecoveryGuideCard(array $guide): void
    {
        $steps = isset($guide['steps']) && is_array($guide['steps']) ? array_values(array_map('strval', $guide['steps'])) : [];
        $stepsJson = wp_json_encode($steps);
        $stepsJson = is_string($stepsJson) ? $stepsJson : '[]';
        $commands = isset($guide['commands']) && is_array($guide['commands']) ? $guide['commands'] : [];
        if (empty($commands) && !empty($guide['command'])) {
            $commands[] = [
                'label' => (string) ($guide['command_label'] ?? __('CLI command', 'xerotact-shield-x-security')),
                'command' => (string) $guide['command'],
                'note' => (string) ($guide['command_note'] ?? ''),
            ];
        }
        $commandsJson = wp_json_encode($commands);
        $commandsJson = is_string($commandsJson) ? $commandsJson : '[]';
        ?>
        <article class="shieldx-recovery-guide-card">
            <span><?php echo esc_html((string) ($guide['kicker'] ?? '')); ?></span>
            <strong><?php echo esc_html((string) ($guide['title'] ?? '')); ?></strong>
            <p><?php echo esc_html((string) ($guide['summary'] ?? '')); ?></p>
            <em><?php echo esc_html((string) ($guide['meta'] ?? '')); ?></em>
            <button
                type="button"
                class="button"
                data-shieldx-recovery-modal
                data-shieldx-modal-kicker="<?php echo esc_attr((string) ($guide['kicker'] ?? '')); ?>"
                data-shieldx-modal-title="<?php echo esc_attr((string) ($guide['title'] ?? '')); ?>"
                data-shieldx-modal-intro="<?php echo esc_attr((string) ($guide['modal_intro'] ?? '')); ?>"
                data-shieldx-modal-steps="<?php echo esc_attr($stepsJson); ?>"
                data-shieldx-modal-commands="<?php echo esc_attr($commandsJson); ?>"
                data-shieldx-modal-action-label="<?php echo esc_attr((string) ($guide['action_label'] ?? __('Open settings', 'xerotact-shield-x-security'))); ?>"
                data-shieldx-modal-action-url="<?php echo esc_url((string) ($guide['action_url'] ?? admin_url('admin.php?page=shield-x-login-security'))); ?>"
            >
                <?php echo esc_html__('View steps', 'xerotact-shield-x-security'); ?>
            </button>
        </article>
        <?php
    }

    /**
     * @param array<int,array<string,mixed>> $adminUsers
     */
    private function renderLoginSecurityAdminUsers(array $adminUsers): void
    {
        ?>
        <section id="shieldx-admin-users" class="shieldx-panel shieldx-panel--full shieldx-admin-users-panel">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Admin Users', 'xerotact-shield-x-security'); ?></h2>
                <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                <span><?php echo esc_html(sprintf(__('%d administrator accounts', 'xerotact-shield-x-security'), count($adminUsers))); ?></span>
            </div>

            <?php if (empty($adminUsers)) : ?>
                <p class="shieldx-empty"><?php echo esc_html__('No administrator users were found.', 'xerotact-shield-x-security'); ?></p>
            <?php else : ?>
                <div class="shieldx-compat-table-wrap shieldx-admin-users-wrap">
                    <table class="widefat shieldx-admin-users-table">
                        <thead>
                            <tr>
                                <th><?php echo esc_html__('User', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Role', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('2FA', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Recovery Codes', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Enabled', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Last Login', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Actions', 'xerotact-shield-x-security'); ?></th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php foreach ($adminUsers as $admin) : ?>
                                <?php
                                $roles = isset($admin['roles']) && is_array($admin['roles']) ? array_values(array_map('strval', $admin['roles'])) : ['administrator'];
                                $roleLabels = [];
                                foreach ($roles as $role) {
                                    $roleLabels[] = $this->formatLabel($role);
                                }
                                $login = (string) ($admin['login'] ?? '');
                                $displayName = trim((string) ($admin['display_name'] ?? ''));
                                $displayName = $displayName !== '' ? $displayName : $login;
                                $email = (string) ($admin['email'] ?? '');
                                $adminUserId = (int) ($admin['user_id'] ?? 0);
                                $enabled = !empty($admin['enabled']);
                                $pending = !empty($admin['pending_secret_exists']);
                                $setupNote = $enabled
                                    ? __('Protected', 'xerotact-shield-x-security')
                                    : ($pending ? __('Setup started', 'xerotact-shield-x-security') : (!empty($admin['setup_required']) ? __('Required', 'xerotact-shield-x-security') : __('Optional', 'xerotact-shield-x-security')));
                                $enabledAt = (string) ($admin['enabled_at'] ?? '');
                                $lastLoginAt = (string) ($admin['last_login_at'] ?? '');
                                ?>
                                <tr>
                                    <td>
                                        <div class="shieldx-admin-user">
                                            <strong><?php echo esc_html($displayName); ?></strong>
                                            <span><?php echo esc_html($email !== '' ? $email : $login); ?></span>
                                            <?php if (!empty($admin['is_current'])) : ?>
                                                <em><?php echo esc_html__('Current account', 'xerotact-shield-x-security'); ?></em>
                                            <?php endif; ?>
                                        </div>
                                    </td>
                                    <td><?php echo esc_html(implode(', ', $roleLabels)); ?></td>
                                    <td>
                                        <span class="<?php echo esc_attr('shieldx-status-badge shieldx-status-badge--' . ($enabled ? 'ready' : 'warning')); ?>">
                                            <?php echo esc_html($enabled ? __('Enabled', 'xerotact-shield-x-security') : __('Pending', 'xerotact-shield-x-security')); ?>
                                        </span>
                                        <small><?php echo esc_html($setupNote); ?></small>
                                    </td>
                                    <td><?php echo esc_html((string) ((int) ($admin['recovery_codes_remaining'] ?? 0))); ?></td>
                                    <td><?php echo esc_html($enabledAt !== '' ? $this->formatTime($enabledAt) : __('Not enabled', 'xerotact-shield-x-security')); ?></td>
                                    <td><?php echo esc_html($lastLoginAt !== '' ? $this->formatTime($lastLoginAt) : __('Unknown', 'xerotact-shield-x-security')); ?></td>
                                    <td class="shieldx-admin-users-actions">
                                        <?php if ($enabled && !empty($admin['is_current'])) : ?>
                                            <a class="button button-small shieldx-row-action-button" href="#shieldx-authenticator-2fa"><?php echo esc_html__('Reset 2FA', 'xerotact-shield-x-security'); ?></a>
                                        <?php elseif ($enabled && $adminUserId > 0) : ?>
                                            <form class="shieldx-row-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                                                <input type="hidden" name="action" value="shieldx_disable_admin_2fa">
                                                <input type="hidden" name="shieldx_target_user_id" value="<?php echo esc_attr((string) $adminUserId); ?>">
                                                <?php wp_nonce_field('shieldx_disable_admin_2fa_' . (string) $adminUserId); ?>
                                                <button type="submit" class="button button-small shieldx-row-action-button shieldx-button-danger"><?php echo esc_html__('Disable 2FA', 'xerotact-shield-x-security'); ?></button>
                                            </form>
                                        <?php else : ?>
                                            <span class="shieldx-muted"><?php echo esc_html__('Not enabled', 'xerotact-shield-x-security'); ?></span>
                                        <?php endif; ?>
                                    </td>
                                </tr>
                            <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>
            <?php endif; ?>
        </section>
        <?php
    }

    public function renderHardening(): void
    {
        if ($this->renderAsyncPageShell('shield-x-hardening')) {
            return;
        }

        $status = $this->statusForPage();
        $hardening = isset($status['hardening']) && is_array($status['hardening']) ? $status['hardening'] : [];
        $responseHeaders = isset($hardening['response_headers']) && is_array($hardening['response_headers'])
            ? $hardening['response_headers']
            : HardeningSettings::defaultResponseHeaders();
        $fileEditConfig = isset($hardening['wp_config_file_edit']) && is_array($hardening['wp_config_file_edit']) ? $hardening['wp_config_file_edit'] : [];
        $fileEditConfigLabel = $this->fileEditConfigLabel($fileEditConfig);
        $phpDisabledFunctions = isset($hardening['php_disabled_functions']) && is_array($hardening['php_disabled_functions'])
            ? $hardening['php_disabled_functions']
            : Hardening::phpDisabledFunctionsStatus('');
        $webServerRules = isset($hardening['web_server_rules']) && is_array($hardening['web_server_rules'])
            ? $hardening['web_server_rules']
            : Hardening::webServerRuleStatus(HardeningSettings::fromWordPress());
        $rootPreflight = isset($webServerRules['root_preflight']) && is_array($webServerRules['root_preflight'])
            ? $webServerRules['root_preflight']
            : [];
        $securityMeasures = isset($hardening['security_measures']) && is_array($hardening['security_measures'])
            ? $hardening['security_measures']
            : Hardening::securityMeasures(HardeningSettings::fromWordPress());
        $stillCallableFunctions = isset($phpDisabledFunctions['still_callable']) && is_array($phpDisabledFunctions['still_callable'])
            ? array_values(array_map('strval', $phpDisabledFunctions['still_callable']))
            : [];
        $missingDisabledFunctions = isset($phpDisabledFunctions['missing_recommended']) && is_array($phpDisabledFunctions['missing_recommended'])
            ? array_values(array_map('strval', $phpDisabledFunctions['missing_recommended']))
            : [];
        $disabledFunctions = isset($phpDisabledFunctions['disabled']) && is_array($phpDisabledFunctions['disabled'])
            ? array_values(array_map('strval', $phpDisabledFunctions['disabled']))
            : [];
        $rateLimit = isset($status['rate_limit']) && is_array($status['rate_limit']) ? $status['rate_limit'] : [];
        $settings = isset($rateLimit['settings']) && is_array($rateLimit['settings']) ? $rateLimit['settings'] : (new RateLimitSettings())->all();
        $webServerRuleStatus = (string) ($webServerRules['root_status'] ?? 'not_applied');

        $this->openPage('shield-x-hardening', $status);
        ?>
        <section class="shieldx-metrics shieldx-hardening-metrics" aria-label="<?php echo esc_attr__('SHIELD-X hardening summary', 'xerotact-shield-x-security'); ?>">
            <?php
            $this->renderMetric(__('Signatures', 'xerotact-shield-x-security'), !empty($hardening['hide_wordpress_signature']) ? __('Hidden', 'xerotact-shield-x-security') : __('Visible', 'xerotact-shield-x-security'), !empty($hardening['hide_wordpress_signature']) ? 'good' : 'warn', 'health-shield');
            $this->renderMetric(__('File editor', 'xerotact-shield-x-security'), !empty($hardening['disable_file_edit']) ? __('Disabled', 'xerotact-shield-x-security') : __('Allowed', 'xerotact-shield-x-security'), !empty($hardening['disable_file_edit']) ? 'good' : 'warn', 'page-search');
            $this->renderMetric(__('File rules', 'xerotact-shield-x-security'), $this->formatLabel($webServerRuleStatus), in_array($webServerRuleStatus, ['active', 'applied', 'ready'], true) ? 'good' : 'warn', 'pc-firewall');
            $this->renderMetric(
                __('PHP callable', 'xerotact-shield-x-security'),
                sprintf(
                    /* translators: %d: number of risky PHP functions still callable. */
                    __('%d callable', 'xerotact-shield-x-security'),
                    count($stillCallableFunctions)
                ),
                empty($stillCallableFunctions) ? 'good' : 'warn',
                'tools'
            );
            $this->renderMetric(__('Rate mode', 'xerotact-shield-x-security'), $this->formatLabel((string) ($settings['request_rate_mode'] ?? 'log_only')), !empty($settings['request_rate_enabled']) ? 'info' : 'warn', 'dashboard-speed');
            ?>
        </section>

        <form
            method="post"
            action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
            data-shieldx-hardening-form
            data-shieldx-hardening-preflight-url="<?php echo esc_url(admin_url('admin-ajax.php')); ?>"
            data-shieldx-hardening-preflight-nonce="<?php echo esc_attr(wp_create_nonce('shieldx_prepare_hardening_preflight')); ?>"
            data-shieldx-hardening-preflight-working="<?php echo esc_attr__('Testing the selected .htaccess rules...', 'xerotact-shield-x-security'); ?>"
            data-shieldx-hardening-preflight-error="<?php echo esc_attr__('The sandbox .htaccess file was created and verified on the host, but this browser could not verify it. Any host-created sandbox .htaccess was preserved as htaccess.bak. No hardening files were changed.', 'xerotact-shield-x-security'); ?>"
            data-shieldx-hardening-preflight-cloudflare="<?php echo esc_attr__('The sandbox .htaccess file was created and verified on the host, but Cloudflare returned a challenge or block page. Any host-created sandbox .htaccess was preserved as htaccess.bak. No hardening files were changed.', 'xerotact-shield-x-security'); ?>"
        >
            <input type="hidden" name="action" value="shieldx_save_hardening_settings">
            <?php wp_nonce_field('shieldx_save_hardening_settings'); ?>

            <div class="shieldx-layout shieldx-layout--single shieldx-hardening-layout">
                <section class="shieldx-panel shieldx-panel--full" data-shieldx-accordion-open="1">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Stealth Hardening', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html__('Signature and browser protections', 'xerotact-shield-x-security'); ?></span>
                    </div>
                    <div class="shieldx-hardening-grid">
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="hide_wordpress_signature" value="1" <?php checked(!empty($hardening['hide_wordpress_signature'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Hide WordPress signatures', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Generator tags, version query strings, and common discovery links are suppressed.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="force_ssl_enabled" value="1" <?php checked(!empty($hardening['force_ssl_enabled'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Force SSL', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Redirects HTTP requests to HTTPS. Behind a proxy, configure trusted proxy CIDRs or use an HTTPS home URL before enabling.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="disable_file_edit" value="1" <?php checked(!empty($hardening['disable_file_edit'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Disable theme/plugin file editor', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Adds define( \'DISALLOW_FILE_EDIT\', true ); to wp-config.php when possible. ', 'xerotact-shield-x-security') . esc_html($fileEditConfigLabel); ?></small>
                            </span>
                        </label>
                    </div>
                </section>

                <section class="shieldx-panel shieldx-panel--full">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Response Headers', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html__('Modern browser protections', 'xerotact-shield-x-security'); ?></span>
                    </div>
                    <div class="shieldx-hardening-grid">
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="response_headers[]" value="<?php echo esc_attr(HardeningSettings::RESPONSE_HEADER_MIME_TYPE_PROTECTION); ?>" <?php checked(!empty($responseHeaders[HardeningSettings::RESPONSE_HEADER_MIME_TYPE_PROTECTION])); ?>>
                            <span>
                                <strong><?php echo esc_html__('MIME type protection', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Sends X-Content-Type-Options: nosniff.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="response_headers[]" value="<?php echo esc_attr(HardeningSettings::RESPONSE_HEADER_FRAME_EMBEDDING_PROTECTION); ?>" <?php checked(!empty($responseHeaders[HardeningSettings::RESPONSE_HEADER_FRAME_EMBEDDING_PROTECTION])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Frame embedding protection', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Sends CSP frame-ancestors self with X-Frame-Options: SAMEORIGIN for compatibility.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="response_headers[]" value="<?php echo esc_attr(HardeningSettings::RESPONSE_HEADER_REFERRER_PRIVACY); ?>" <?php checked(!empty($responseHeaders[HardeningSettings::RESPONSE_HEADER_REFERRER_PRIVACY])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Referrer privacy', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Sends Referrer-Policy: strict-origin-when-cross-origin.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="response_headers[]" value="<?php echo esc_attr(HardeningSettings::RESPONSE_HEADER_CROSS_DOMAIN_POLICY); ?>" <?php checked(!empty($responseHeaders[HardeningSettings::RESPONSE_HEADER_CROSS_DOMAIN_POLICY])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Cross-domain policy', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Sends X-Permitted-Cross-Domain-Policies: none.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="response_headers[]" value="<?php echo esc_attr(HardeningSettings::RESPONSE_HEADER_HIDE_POWERED_BY); ?>" <?php checked(!empty($responseHeaders[HardeningSettings::RESPONSE_HEADER_HIDE_POWERED_BY])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Technology disclosure', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Attempts to remove X-Powered-By before WordPress sends the response.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="response_headers[]" value="<?php echo esc_attr(HardeningSettings::RESPONSE_HEADER_HSTS); ?>" <?php checked(!empty($responseHeaders[HardeningSettings::RESPONSE_HEADER_HSTS])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Enable HSTS on HTTPS', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Sends Strict-Transport-Security with includeSubDomains only over HTTPS. Enable only after every subdomain serves HTTPS.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="response_headers[]" value="<?php echo esc_attr(HardeningSettings::RESPONSE_HEADER_PERMISSIONS_POLICY); ?>" <?php checked(!empty($responseHeaders[HardeningSettings::RESPONSE_HEADER_PERMISSIONS_POLICY])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Permissions Policy', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Disables camera, screen capture, geolocation, microphone, payment, and USB browser permissions. Keep off when a site needs any of them.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                    </div>
                    <p class="shieldx-muted"><?php echo esc_html__('These controls apply to WordPress/PHP responses. Your server, CDN, and static-file responses may use different headers.', 'xerotact-shield-x-security'); ?></p>
                </section>

                <section class="shieldx-panel shieldx-panel--full">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('WordPress Surface Controls', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html__('Reversible runtime protections', 'xerotact-shield-x-security'); ?></span>
                    </div>
                    <div class="shieldx-hardening-grid">
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="block_xmlrpc" value="1" <?php checked(!empty($hardening['block_xmlrpc'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Block xmlrpc.php', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Disables XML-RPC requests and writes a root web-server deny rule when possible.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="disable_pingbacks" value="1" <?php checked(!empty($hardening['disable_pingbacks'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Turn off pingbacks', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Removes pingback XML-RPC methods and closes pingback checks at runtime.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="disable_admin_scripts_concatenation" value="1" <?php checked(!empty($hardening['disable_admin_scripts_concatenation'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Disable admin script concatenation', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Sets WordPress admin script concatenation off at runtime to avoid bundled admin-script delivery.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="block_author_scans" value="1" <?php checked(!empty($hardening['block_author_scans'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Block author scans', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Blocks numeric ?author= requests that reveal administrator usernames.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="generic_login_errors" value="1" <?php checked(!empty($hardening['generic_login_errors'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Generic login and reset errors', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Replaces username, email, and password-reset enumeration details with neutral messages.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="bot_protection_enabled" value="1" <?php checked(!empty($hardening['bot_protection_enabled'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Basic bot protection', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Blocks common scanner user agents only on login, XML-RPC, direct-core, sensitive-file, and executable-upload surfaces.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                    </div>
                </section>

                <section class="shieldx-panel shieldx-panel--full">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Direct File Access Rules', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html($this->formatLabel((string) ($webServerRules['root_status'] ?? 'not_applied'))); ?></span>
                    </div>
                    <div class="shieldx-hardening-grid">
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="direct_core_access_protection" value="1" <?php checked(!empty($hardening['direct_core_access_protection'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Block direct core access', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Denies wp-admin/includes, wp-includes PHP entrypoints, TinyMCE language PHP, and theme-compat direct access.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="sensitive_file_protection" value="1" <?php checked(!empty($hardening['sensitive_file_protection'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Block sensitive files', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Denies wp-config.php, server dotfiles, env/log/metadata files, and backup/archive extensions outside uploads.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="directory_browsing_protection" value="1" <?php checked(!empty($hardening['directory_browsing_protection'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Block directory browsing', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Adds Options -Indexes to the managed root .htaccess block when the server supports Apache-compatible rules.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="data_php_execution_protection" value="1" <?php checked(!empty($hardening['data_php_execution_protection'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Disable PHP in data directories', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Writes deny stubs for uploads, cache, and upgrade directories when present.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                    </div>
                    <dl class="shieldx-detail-list shieldx-detail-list--compact shieldx-direct-access-summary">
                        <?php $this->renderDetail(__('Root rule block', 'xerotact-shield-x-security'), $this->formatLabel((string) ($webServerRules['root_status'] ?? 'not_applied'))); ?>
                        <?php
                        $preflightStatus = (string) ($rootPreflight['status'] ?? 'unknown');
                        $preflightCode = (int) ($rootPreflight['http_code'] ?? 0);
                        $preflightUrl = (string) ($rootPreflight['url'] ?? '');
                        $preflightLabel = $this->formatLabel($preflightStatus) . ($preflightCode > 0 ? ' / HTTP ' . (string) $preflightCode : '');
                        $this->renderDetail(__('Root sandbox test', 'xerotact-shield-x-security'), $preflightLabel);
                        if ($preflightUrl !== '') {
                            $this->renderDetailLink(__('Root sandbox URL', 'xerotact-shield-x-security'), $preflightUrl);
                        }
                        $preflightFilesystemStatus = (string) ($rootPreflight['filesystem_status'] ?? '');
                        if ($preflightFilesystemStatus !== '') {
                            $this->renderDetail(__('Sandbox .htaccess file', 'xerotact-shield-x-security'), $this->formatLabel($preflightFilesystemStatus));
                        }
                        $preflightMessage = (string) ($rootPreflight['message'] ?? '');
                        $preflightCheckedAt = (string) ($rootPreflight['checked_at'] ?? '');
                        $preflightExpiresAt = (string) ($rootPreflight['expires_at'] ?? '');
                        if ($preflightMessage !== '') {
                            $this->renderDetail(__('Sandbox diagnostic', 'xerotact-shield-x-security'), $preflightMessage);
                        }
                        if ($preflightCheckedAt !== '') {
                            $this->renderDetail(__('Sandbox checked at (UTC)', 'xerotact-shield-x-security'), $preflightCheckedAt);
                        }
                        if ($preflightExpiresAt !== '') {
                            $this->renderDetail(__('Sandbox available until (UTC)', 'xerotact-shield-x-security'), $preflightExpiresAt);
                        }
                        $preflightServer = (string) ($rootPreflight['server'] ?? '');
                        $preflightCfRay = (string) ($rootPreflight['cf_ray'] ?? '');
                        $preflightCfCache = (string) ($rootPreflight['cf_cache_status'] ?? '');
                        if ($preflightServer !== '') {
                            $this->renderDetail(__('Sandbox response server', 'xerotact-shield-x-security'), $preflightServer);
                        }
                        if ($preflightCfRay !== '') {
                            $this->renderDetail(__('Cloudflare Ray ID', 'xerotact-shield-x-security'), $preflightCfRay);
                        }
                        if ($preflightCfCache !== '') {
                            $this->renderDetail(__('Cloudflare cache status', 'xerotact-shield-x-security'), $preflightCfCache);
                        }
                        ?>
                        <?php $this->renderDetail(__('Root .htaccess', 'xerotact-shield-x-security'), (string) ($webServerRules['root_path'] ?? '')); ?>
                        <?php
                        $dataDirectoryStatuses = [];
                        foreach ((array) ($webServerRules['data_directories'] ?? []) as $directoryStatus) {
                            if (!is_array($directoryStatus)) {
                                continue;
                            }
                            $dataDirectoryStatuses[] = basename((string) ($directoryStatus['path'] ?? '')) . ': ' . $this->formatLabel((string) ($directoryStatus['status'] ?? 'not_applied'));
                        }
                        $this->renderDetail(__('Data directory rules', 'xerotact-shield-x-security'), !empty($dataDirectoryStatuses) ? implode(', ', $dataDirectoryStatuses) : __('No matching directories found yet.', 'xerotact-shield-x-security'));
                        ?>
                    </dl>
                </section>

                <section class="shieldx-panel shieldx-panel--full shieldx-hardening-sidebar-panel" data-shieldx-accordion-open="1">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Security Measures', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html__('Checklist coverage', 'xerotact-shield-x-security'); ?></span>
                    </div>
                    <div class="shieldx-measure-list" role="list">
                        <?php foreach ($securityMeasures as $measure) : ?>
                            <?php
                            if (!is_array($measure)) {
                                continue;
                            }
                            $measureStatus = (string) ($measure['status'] ?? 'warning');
                            $measureTone = $measureStatus === 'ready' ? 'good' : 'warn';
                            ?>
                            <div class="shieldx-measure-row" role="listitem">
                                <div>
                                    <strong><?php echo esc_html((string) ($measure['label'] ?? '')); ?></strong>
                                    <small><?php echo esc_html((string) ($measure['detail'] ?? '')); ?></small>
                                </div>
                                <span class="<?php echo esc_attr('shieldx-pill shieldx-pill--' . $measureTone); ?>"><?php echo esc_html($this->formatLabel($measureStatus)); ?></span>
                                <em><?php echo esc_html((string) ($measure['type'] ?? '')); ?></em>
                            </div>
                        <?php endforeach; ?>
                    </div>
                </section>

                <section class="shieldx-panel shieldx-panel--full">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('PHP Disabled Functions', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html(empty($stillCallableFunctions) ? __('No risky recommended functions callable', 'xerotact-shield-x-security') : sprintf(
                            /* translators: %d: number of risky PHP functions still callable. */
                            __('%d risky functions callable', 'xerotact-shield-x-security'),
                            count($stillCallableFunctions)
                        )); ?></span>
                    </div>
                    <dl class="shieldx-detail-list">
                        <?php $this->renderDetail(__('Directive level', 'xerotact-shield-x-security'), (string) ($phpDisabledFunctions['ini_level'] ?? 'PHP_INI_SYSTEM')); ?>
                        <?php $this->renderDetail(__('Runtime SAPI', 'xerotact-shield-x-security'), (string) ($phpDisabledFunctions['runtime_sapi'] ?? PHP_SAPI)); ?>
                        <?php $this->renderDetail(__('Can use .user.ini', 'xerotact-shield-x-security'), !empty($phpDisabledFunctions['user_ini_supported']) ? __('Yes', 'xerotact-shield-x-security') : __('No for disable_functions', 'xerotact-shield-x-security')); ?>
                        <?php $this->renderDetail(__('Configured recommended', 'xerotact-shield-x-security'), sprintf(
                            /* translators: 1: number of recommended PHP functions already disabled, 2: total recommended PHP functions. */
                            __('%1$d of %2$d disabled', 'xerotact-shield-x-security'),
                            (int) ($phpDisabledFunctions['configured_count'] ?? 0),
                            (int) ($phpDisabledFunctions['recommended_count'] ?? 0)
                        )); ?>
                        <?php $this->renderDetail(__('Disabled now', 'xerotact-shield-x-security'), !empty($disabledFunctions) ? implode(', ', $disabledFunctions) : __('None configured', 'xerotact-shield-x-security')); ?>
                        <?php $this->renderDetail(__('Still callable', 'xerotact-shield-x-security'), !empty($stillCallableFunctions) ? implode(', ', $stillCallableFunctions) : __('None detected', 'xerotact-shield-x-security')); ?>
                        <?php $this->renderDetail(__('Recommended missing', 'xerotact-shield-x-security'), !empty($missingDisabledFunctions) ? implode(', ', $missingDisabledFunctions) : __('None', 'xerotact-shield-x-security')); ?>
                    </dl>
                    <p class="shieldx-muted">
                        <?php echo esc_html__('PHP disable_functions is a server-level php.ini setting. .user.ini cannot reliably set it because PHP exposes the directive as PHP_INI_SYSTEM. SHIELD-X reports and provides the recommended line, but the host, container, or server admin must apply it and reload PHP.', 'xerotact-shield-x-security'); ?>
                    </p>
                    <div class="shieldx-system-code-grid">
                        <?php $this->renderSystemCodeCard(__('php.ini / hosting panel', 'xerotact-shield-x-security'), (string) ($phpDisabledFunctions['php_ini_line'] ?? '')); ?>
                        <?php $this->renderSystemCodeCard(__('Docker conf.d file', 'xerotact-shield-x-security'), (string) ($phpDisabledFunctions['docker_ini_file'] ?? '') . "\n\n" . (string) ($phpDisabledFunctions['docker_ini_contents'] ?? '')); ?>
                        <?php $this->renderSystemCodeCard(__('Do not use .user.ini', 'xerotact-shield-x-security'), (string) ($phpDisabledFunctions['user_ini_note'] ?? '')); ?>
                    </div>
                </section>

                <section id="shieldx-rate-limiting" class="shieldx-panel shieldx-panel--full">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Rate Limiting', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html($this->formatLabel((string) ($settings['request_rate_mode'] ?? 'log_only'))); ?></span>
                    </div>
                    <div class="shieldx-settings-grid shieldx-toggle-group" data-shieldx-toggle-group data-shieldx-toggle-input="request_rate_enabled">
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="request_rate_enabled" value="1" <?php checked(!empty($settings['request_rate_enabled'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Request rate monitor', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Tracks high request volume per client IP with burst and sustained windows.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <div class="shieldx-toggle-fields" data-shieldx-toggle-fields>
                            <label class="shieldx-field">
                                <span><?php echo esc_html__('Mode', 'xerotact-shield-x-security'); ?></span>
                                <select name="request_rate_mode">
                                    <option value="log_only" <?php selected((string) ($settings['request_rate_mode'] ?? 'log_only'), 'log_only'); ?>><?php echo esc_html__('Log only', 'xerotact-shield-x-security'); ?></option>
                                    <option value="enforce" <?php selected((string) ($settings['request_rate_mode'] ?? 'log_only'), 'enforce'); ?>><?php echo esc_html__('Enforce 429', 'xerotact-shield-x-security'); ?></option>
                                </select>
                            </label>
                            <?php $this->renderNumberField('request_limit', __('Sustained limit', 'xerotact-shield-x-security'), (int) ($settings['request_limit'] ?? 120), 10, 5000); ?>
                            <?php $this->renderNumberField('request_window_seconds', __('Sustained window', 'xerotact-shield-x-security'), (int) ($settings['request_window_seconds'] ?? 60), 10, 3600); ?>
                            <?php $this->renderNumberField('burst_limit', __('Burst limit', 'xerotact-shield-x-security'), (int) ($settings['burst_limit'] ?? 40), 5, 1000); ?>
                            <?php $this->renderNumberField('burst_window_seconds', __('Burst window', 'xerotact-shield-x-security'), (int) ($settings['burst_window_seconds'] ?? 10), 5, 300); ?>
                        </div>
                    </div>
                    <fieldset class="shieldx-field">
                        <legend><?php echo esc_html__('Endpoint profiles', 'xerotact-shield-x-security'); ?></legend>
                        <p class="shieldx-muted"><?php echo esc_html__('Each enabled profile uses an independent per-IP bucket. Profiles start disabled and log only.', 'xerotact-shield-x-security'); ?></p>
                        <div class="shieldx-settings-grid">
                            <?php
                            $endpointLabels = [
                                'login_post' => __('Login POST', 'xerotact-shield-x-security'),
                                'xmlrpc_post' => __('XML-RPC POST', 'xerotact-shield-x-security'),
                                'rest_user_enumeration' => __('REST user enumeration', 'xerotact-shield-x-security'),
                                'comment_submission' => __('Comment submission', 'xerotact-shield-x-security'),
                                'search' => __('Search requests', 'xerotact-shield-x-security'),
                            ];
                            $endpointProfiles = isset($settings['endpoint_profiles']) && is_array($settings['endpoint_profiles']) ? $settings['endpoint_profiles'] : [];
                            ?>
                            <?php foreach ($endpointLabels as $profileKey => $profileLabel) : ?>
                                <?php $profileSettings = isset($endpointProfiles[$profileKey]) && is_array($endpointProfiles[$profileKey]) ? $endpointProfiles[$profileKey] : []; ?>
                                <fieldset class="shieldx-field">
                                    <legend><?php echo esc_html($profileLabel); ?></legend>
                                    <label class="shieldx-inline-check">
                                        <input type="checkbox" name="endpoint_profiles[<?php echo esc_attr($profileKey); ?>][enabled]" value="1" <?php checked(!empty($profileSettings['enabled'])); ?>>
                                        <span><?php echo esc_html__('Enable profile', 'xerotact-shield-x-security'); ?></span>
                                    </label>
                                    <label>
                                        <span><?php echo esc_html__('Mode', 'xerotact-shield-x-security'); ?></span>
                                        <select name="endpoint_profiles[<?php echo esc_attr($profileKey); ?>][mode]">
                                            <option value="log_only" <?php selected((string) ($profileSettings['mode'] ?? 'log_only'), 'log_only'); ?>><?php echo esc_html__('Log only', 'xerotact-shield-x-security'); ?></option>
                                            <option value="enforce" <?php selected((string) ($profileSettings['mode'] ?? 'log_only'), 'enforce'); ?>><?php echo esc_html__('Enforce 429', 'xerotact-shield-x-security'); ?></option>
                                        </select>
                                    </label>
                                    <label><span><?php echo esc_html__('Sustained limit', 'xerotact-shield-x-security'); ?></span><input type="number" min="2" max="5000" name="endpoint_profiles[<?php echo esc_attr($profileKey); ?>][limit]" value="<?php echo esc_attr((string) (int) ($profileSettings['limit'] ?? 30)); ?>"></label>
                                    <label><span><?php echo esc_html__('Window seconds', 'xerotact-shield-x-security'); ?></span><input type="number" min="10" max="3600" name="endpoint_profiles[<?php echo esc_attr($profileKey); ?>][window_seconds]" value="<?php echo esc_attr((string) (int) ($profileSettings['window_seconds'] ?? 60)); ?>"></label>
                                    <label><span><?php echo esc_html__('Burst limit', 'xerotact-shield-x-security'); ?></span><input type="number" min="2" max="1000" name="endpoint_profiles[<?php echo esc_attr($profileKey); ?>][burst_limit]" value="<?php echo esc_attr((string) (int) ($profileSettings['burst_limit'] ?? 10)); ?>"></label>
                                    <label><span><?php echo esc_html__('Burst seconds', 'xerotact-shield-x-security'); ?></span><input type="number" min="5" max="300" name="endpoint_profiles[<?php echo esc_attr($profileKey); ?>][burst_window_seconds]" value="<?php echo esc_attr((string) (int) ($profileSettings['burst_window_seconds'] ?? 10)); ?>"></label>
                                </fieldset>
                            <?php endforeach; ?>
                        </div>
                    </fieldset>
                </section>

                <div class="shieldx-form-actions shieldx-panel--full">
                    <button type="submit" class="button button-primary"><?php echo esc_html__('Save hardening settings', 'xerotact-shield-x-security'); ?></button>
                    <p class="shieldx-form-status" data-shieldx-hardening-preflight-status role="status" hidden></p>
                </div>
            </div>
        </form>
        <?php
        $this->closePage();
    }

    public function renderLockdown(): void
    {
        if ($this->renderAsyncPageShell('shield-x-lockdown')) {
            return;
        }

        $status = $this->statusForPage();
        $lockdown = isset($status['lockdown']) && is_array($status['lockdown']) ? $status['lockdown'] : [];
        $policy = isset($lockdown['policy']) && is_array($lockdown['policy']) ? $lockdown['policy'] : LockdownSettings::defaultPolicy();
        $coverage = isset($lockdown['coverage']) && is_array($lockdown['coverage']) ? $lockdown['coverage'] : [];
        $preview = isset($lockdown['preview']) && is_array($lockdown['preview']) ? $lockdown['preview'] : [];
        $manifest = isset($lockdown['manifest']) && is_array($lockdown['manifest']) ? $lockdown['manifest'] : [];
        $lockdownEnabled = !empty($lockdown['enabled']);
        $manifestExists = !empty($lockdown['manifest_exists']);
        $recoveryProbe = isset($lockdown['recovery_probe']) && is_array($lockdown['recovery_probe']) ? $lockdown['recovery_probe'] : [];
        $recoveryStatus = (string) ($recoveryProbe['status'] ?? '');
        $forceRevertAvailable = $manifestExists && (
            empty($manifest['hmac_valid'])
            || in_array($recoveryStatus, ['manifest_corrupt', 'manifest_unreadable'], true)
        );
        if ($this->queryFlag('shieldx_lockdown_preview')) {
            $previewPolicy = $this->loadLockdownPreviewPolicy();
            if ($previewPolicy !== []) {
                $policy = $previewPolicy;
                $preview = $this->plugin->previewLockdown($previewPolicy);
            }
        }
        $permissionScopes = array_values(array_filter(LocalScanner::fromWordPress()->availableScopes(false), static function ($scope): bool {
            return is_array($scope) && (string) ($scope['id'] ?? '') !== 'database';
        }));
        $selectedPermissionScopes = [];
        foreach ($permissionScopes as $scope) {
            if (is_array($scope) && isset($scope['id'])) {
                $selectedPermissionScopes[] = (string) $scope['id'];
            }
        }
        $permissionReportId = $this->queryKey('shieldx_permission_report');
        $permissionReport = $permissionReportId !== '' ? $this->loadPermissionFixReport($permissionReportId) : [];

        $this->openPage('shield-x-lockdown', $status);
        ?>
        <section class="shieldx-metrics" aria-label="<?php echo esc_attr__('SHIELD-X lockdown summary', 'xerotact-shield-x-security'); ?>">
            <?php
            $this->renderMetric(__('Lockdown', 'xerotact-shield-x-security'), !empty($lockdown['enabled']) ? __('Enabled', 'xerotact-shield-x-security') : __('Off', 'xerotact-shield-x-security'), !empty($lockdown['enabled']) ? 'good' : 'warn', 'lock');
            $this->renderMetric(__('Exec block', 'xerotact-shield-x-security'), $this->formatLabel((string) ($coverage['exec_block'] ?? 'not_applied')), !empty($lockdown['enabled']) ? 'info' : 'warn', 'pc-firewall');
            $this->renderMetric(__('Read-only', 'xerotact-shield-x-security'), $this->formatLabel((string) ($coverage['readonly'] ?? 'not_applied')), (string) ($coverage['readonly'] ?? '') === 'enforced' ? 'good' : 'info', 'folder-settings');
            $this->renderMetric(__('Manifest', 'xerotact-shield-x-security'), !empty($lockdown['manifest_exists']) ? __('Ready', 'xerotact-shield-x-security') : __('None', 'xerotact-shield-x-security'), !empty($lockdown['manifest_exists']) ? 'good' : 'warn', 'database');
            $this->renderMetric(__('Killswitch', 'xerotact-shield-x-security'), !empty($lockdown['killswitch_active']) ? __('Active', 'xerotact-shield-x-security') : __('Inactive', 'xerotact-shield-x-security'), !empty($lockdown['killswitch_active']) ? 'warn' : 'good', 'message-alert');
            ?>
        </section>

        <form
            method="post"
            action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
            data-shieldx-permission-fix-form
            data-shieldx-progress-poll-delay="250"
            data-shieldx-progress-refresh-selector="#shieldx-permission-fix"
            data-shieldx-progress-running-title="<?php echo esc_attr__('Permission Fix in progress', 'xerotact-shield-x-security'); ?>"
            data-shieldx-progress-running-intro="<?php echo esc_attr__('SHIELD-X is checking selected WordPress paths in small batches so large sites stay below server timeout limits.', 'xerotact-shield-x-security'); ?>"
            data-shieldx-progress-failed-title="<?php echo esc_attr__('Permission Fix stopped', 'xerotact-shield-x-security'); ?>"
            data-shieldx-progress-failed-intro="<?php echo esc_attr__('SHIELD-X could not continue this Permission Fix session. Go back and start Permission Fix again.', 'xerotact-shield-x-security'); ?>"
            data-shieldx-progress-complete-title="<?php echo esc_attr__('Permission Fix complete', 'xerotact-shield-x-security'); ?>"
            data-shieldx-progress-complete-intro="<?php echo esc_attr__('Selected permissions were checked. Review the summary and any host-owned paths below.', 'xerotact-shield-x-security'); ?>"
            data-shieldx-progress-result-label="<?php echo esc_attr__('Review Permission Fix', 'xerotact-shield-x-security'); ?>"
            data-shieldx-progress-continue-label="<?php echo esc_attr__('Continue now', 'xerotact-shield-x-security'); ?>"
            data-shieldx-progress-back-label="<?php echo esc_attr__('Back to Permission Fix', 'xerotact-shield-x-security'); ?>"
            data-shieldx-progress-auto-only="1"
        >
            <input type="hidden" name="action" value="shieldx_save_lockdown_settings">
            <?php wp_nonce_field('shieldx_save_lockdown_settings'); ?>

            <div class="shieldx-layout shieldx-lockdown-layout">
                <section class="shieldx-panel shieldx-panel--full shieldx-lockdown-policy-panel" data-shieldx-accordion-open="1">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Lockdown Policy', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html((string) ($coverage['message'] ?? __('Dry-run first, apply only after confirmation.', 'xerotact-shield-x-security'))); ?></span>
                    </div>
                    <div class="shieldx-settings-grid">
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="lockdown_read_only_enabled" value="1" <?php checked(!empty($policy['read_only_enabled'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Read-only core, plugins, and themes', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Records original permissions, then attempts chmod hardening when the host permits it.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="lockdown_no_exec_enabled" value="1" <?php checked(!empty($policy['no_exec_enabled'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('No-exec data directories', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Writes safe .htaccess deny stubs for uploads, cache, upgrade, and SHIELD-X storage.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="lockdown_disable_file_mods" value="1" <?php checked(!empty($policy['disable_file_mods'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Aggressive update/install lock', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Applies DISALLOW_FILE_MODS. Use only during incident response or maintenance freezes.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                        <label class="shieldx-hardening-option">
                            <input type="checkbox" name="lockdown_directory_index_off" value="1" <?php checked(!empty($policy['directory_index_off'])); ?>>
                            <span>
                                <strong><?php echo esc_html__('Block sensitive direct access', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Adds wp-admin/includes, wp-includes PHP, root sensitive file, backup, and SQL dump protections.', 'xerotact-shield-x-security'); ?></small>
                            </span>
                        </label>
                    </div>

                    <div class="shieldx-settings-grid shieldx-settings-grid--two">
                        <?php $this->renderTextareaField('lockdown_allowlist', __('Excluded subpaths', 'xerotact-shield-x-security'), $this->lineListValue($policy['allowlist'] ?? []), __('One relative path per line. Use only for rare legitimate executable files under data directories.', 'xerotact-shield-x-security')); ?>
                        <div class="shieldx-field shieldx-field--textarea">
                            <span><?php echo esc_html__('Recovery', 'xerotact-shield-x-security'); ?></span>
                            <code><?php echo esc_html((string) ($lockdown['killswitch_path'] ?? 'uploads/.shield-x/lockdown/disable.json')); ?></code>
                            <small><?php echo esc_html__('Create this signed JSON marker by FTP/SFTP to suspend enforcement and revert from the manifest on the next SHIELD-X load.', 'xerotact-shield-x-security'); ?></small>
                            <?php if (!empty($lockdown['killswitch_marker'])) : ?>
                                <textarea class="large-text code" rows="6" readonly><?php echo esc_textarea((string) $lockdown['killswitch_marker']); ?></textarea>
                            <?php endif; ?>
                            <code><?php echo esc_html((string) ($lockdown['manifest_path'] ?? 'uploads/.shield-x/lockdown/manifest.json')); ?></code>
                            <small><?php echo esc_html__('The manifest stores original permissions and SHIELD-X-created files for precise rollback.', 'xerotact-shield-x-security'); ?></small>
                        </div>
                    </div>

                    <?php if (!$lockdownEnabled) : ?>
                        <label class="shieldx-inline-check shieldx-lockdown-confirm">
                            <input type="checkbox" name="lockdown_confirm" value="1">
                            <span><?php echo esc_html__('I understand enabling Lockdown changes filesystem protections and may pause normal updates until reverted or suspended.', 'xerotact-shield-x-security'); ?></span>
                        </label>
                    <?php endif; ?>

                    <div class="shieldx-form-actions shieldx-lockdown-actions">
                        <button type="submit" class="button" name="lockdown_submit" value="save"><?php echo esc_html__('Save policy', 'xerotact-shield-x-security'); ?></button>
                        <button type="submit" class="button" name="lockdown_submit" value="preview"><?php echo esc_html__('Dry-run preview', 'xerotact-shield-x-security'); ?></button>
                        <?php if ($lockdownEnabled) : ?>
                            <button type="submit" class="button button-primary shieldx-button-danger" name="lockdown_submit" value="disable"><?php echo esc_html__('Disable Lockdown', 'xerotact-shield-x-security'); ?></button>
                            <button type="submit" class="button" name="lockdown_submit" value="reapply"><?php echo esc_html__('Reapply protections', 'xerotact-shield-x-security'); ?></button>
                        <?php else : ?>
                            <button type="submit" class="button button-primary" name="lockdown_submit" value="enable"><?php echo esc_html__('Enable Lockdown', 'xerotact-shield-x-security'); ?></button>
                            <?php if ($manifestExists) : ?>
                                <button type="submit" class="button shieldx-button-danger" name="lockdown_submit" value="disable"><?php echo esc_html__('Disable / revert', 'xerotact-shield-x-security'); ?></button>
                            <?php endif; ?>
                        <?php endif; ?>
                    </div>

                    <?php if ($forceRevertAvailable) : ?>
                        <div class="shieldx-inline-alert shieldx-inline-alert--warning">
                            <div class="shieldx-section-head">
                                <h3><?php echo esc_html__('Corrupt rollback manifest', 'xerotact-shield-x-security'); ?></h3>
                                <span><?php echo esc_html__('Manual confirmation required', 'xerotact-shield-x-security'); ?></span>
                            </div>
                            <p class="shieldx-muted"><?php echo esc_html((string) ($recoveryProbe['message'] ?? __('The rollback manifest cannot be trusted.', 'xerotact-shield-x-security'))); ?></p>
                            <p class="shieldx-muted"><?php echo esc_html__('Force revert removes only known SHIELD-X Lockdown markers and exact orphaned read-only modes. It does not trust paths or permissions recorded in the corrupt manifest.', 'xerotact-shield-x-security'); ?></p>
                            <label class="shieldx-inline-check shieldx-lockdown-confirm">
                                <input type="checkbox" name="lockdown_force_revert_confirm" value="1">
                                <span><?php echo esc_html__('I understand this bypasses the corrupt manifest and may require manual recovery for any remaining paths.', 'xerotact-shield-x-security'); ?></span>
                            </label>
                            <div class="shieldx-form-actions shieldx-lockdown-actions">
                                <button type="submit" class="button shieldx-button-danger" name="lockdown_submit" value="force_revert"><?php echo esc_html__('Force revert known SHIELD-X changes', 'xerotact-shield-x-security'); ?></button>
                            </div>
                        </div>
                    <?php endif; ?>
                </section>

                <section class="shieldx-panel shieldx-panel--full shieldx-lockdown-preview-panel" data-shieldx-accordion-open="1">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Dry-run Diff', 'xerotact-shield-x-security'); ?></h2>
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <span><?php echo esc_html(sprintf(__('%d planned actions', 'xerotact-shield-x-security'), (int) ($preview['summary']['actions'] ?? 0))); ?></span>
                    </div>
                    <?php $this->renderLockdownPreview($preview); ?>
                </section>

                <section id="shieldx-permission-fix" class="shieldx-panel shieldx-panel--full shieldx-permission-fix-panel" data-shieldx-accordion-open="1">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Permission Fix', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html__('Folders 0755, files 0644', 'xerotact-shield-x-security'); ?></span>
                    </div>
                    <p class="shieldx-muted"><?php echo esc_html__('Use this after failed plugin/theme updates or a reverted lockdown leaves WordPress files too restrictive. SHIELD-X only touches selected WordPress paths and skips symbolic links.', 'xerotact-shield-x-security'); ?></p>
                    <div class="shieldx-scope-grid shieldx-scope-grid--permission-fix">
                        <?php foreach ($permissionScopes as $scope) : ?>
                            <?php $this->renderScanScopeCard(is_array($scope) ? $scope : [], $selectedPermissionScopes, 'permission_scopes'); ?>
                        <?php endforeach; ?>
                    </div>
                    <div class="shieldx-form-actions shieldx-lockdown-actions">
                        <button type="submit" class="button button-primary" name="lockdown_submit" value="permission_fix" data-shieldx-permission-fix-button data-running-label="<?php echo esc_attr__('Fixing permissions...', 'xerotact-shield-x-security'); ?>"><?php echo esc_html__('Fix selected permissions', 'xerotact-shield-x-security'); ?></button>
                    </div>
                    <?php if (!empty($permissionReport['failed']) && is_array($permissionReport['failed'])) : ?>
                        <div class="shieldx-inline-alert shieldx-inline-alert--warning shieldx-permission-report">
                            <div class="shieldx-section-head">
                                <h3><?php echo esc_html__('Paths that still need host action', 'xerotact-shield-x-security'); ?></h3>
                                <span>
                                    <?php
                                    echo esc_html(sprintf(
                                        /* translators: 1: number of shown failed permission paths, 2: total failed permission paths. */
                                        __('Showing %1$d of %2$d failures', 'xerotact-shield-x-security'),
                                        count((array) $permissionReport['failed']),
                                        (int) ($permissionReport['failed_total'] ?? count((array) $permissionReport['failed']))
                                    ));
                                    ?>
                                </span>
                            </div>
                            <p class="shieldx-muted"><?php echo esc_html__('These paths could not be changed through the WordPress filesystem layer. Fix ownership or permissions in your hosting panel, FTP/SFTP, or SSH, then run Permission Fix again.', 'xerotact-shield-x-security'); ?></p>
                            <div class="shieldx-table-wrap">
                                <table class="shieldx-table shieldx-table--compact">
                                    <thead>
                                        <tr>
                                            <th><?php echo esc_html__('Path', 'xerotact-shield-x-security'); ?></th>
                                            <th><?php echo esc_html__('Type', 'xerotact-shield-x-security'); ?></th>
                                            <th><?php echo esc_html__('Target', 'xerotact-shield-x-security'); ?></th>
                                            <th><?php echo esc_html__('Reason', 'xerotact-shield-x-security'); ?></th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        <?php foreach ((array) $permissionReport['failed'] as $failure) : ?>
                                            <?php if (!is_array($failure)) { continue; } ?>
                                            <tr>
                                                <td><?php echo esc_html((string) ($failure['path'] ?? '')); ?></td>
                                                <td><?php echo esc_html($this->formatLabel((string) ($failure['type'] ?? 'path'))); ?></td>
                                                <td><?php echo esc_html((string) ($failure['target_mode'] ?? '')); ?></td>
                                                <td><?php echo esc_html((string) ($failure['reason'] ?? __('Permission change failed.', 'xerotact-shield-x-security'))); ?></td>
                                            </tr>
                                        <?php endforeach; ?>
                                    </tbody>
                                </table>
                            </div>
                        </div>
                    <?php endif; ?>
                </section>

                <section class="shieldx-panel shieldx-panel--full">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Current Manifest', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html(!empty($lockdown['manifest_exists']) ? __('Rollback available', 'xerotact-shield-x-security') : __('Not applied', 'xerotact-shield-x-security')); ?></span>
                    </div>
                    <dl class="shieldx-detail-list">
                        <?php $this->renderDetail(__('Applied at', 'xerotact-shield-x-security'), !empty($manifest['applied_at_utc']) ? (string) $manifest['applied_at_utc'] : __('Not applied', 'xerotact-shield-x-security')); ?>
                        <?php $this->renderDetail(__('HMAC', 'xerotact-shield-x-security'), !empty($manifest['hmac_valid']) ? __('Valid', 'xerotact-shield-x-security') : (!empty($lockdown['manifest_exists']) ? __('Needs review', 'xerotact-shield-x-security') : __('Not present', 'xerotact-shield-x-security'))); ?>
                        <?php $this->renderDetail(__('Created files', 'xerotact-shield-x-security'), (string) count((array) ($manifest['files_created'] ?? []))); ?>
                        <?php $this->renderDetail(__('Modified files', 'xerotact-shield-x-security'), (string) count((array) ($manifest['files_modified'] ?? []))); ?>
                        <?php $this->renderDetail(__('Permission snapshots', 'xerotact-shield-x-security'), (string) count((array) ($manifest['perms'] ?? []))); ?>
                    </dl>
                </section>
            </div>
        </form>
        <?php
        $this->closePage();
    }

    public function renderActivity(): void
    {
        if ($this->renderAsyncPageShell('shield-x-activity')) {
            return;
        }

        $status = $this->statusForPage();
        $wafStats = isset($status['waf']['stats']) && is_array($status['waf']['stats']) ? $status['waf']['stats'] : [];
        $rateRequest = isset($status['rate_limit']['request']) && is_array($status['rate_limit']['request']) ? $status['rate_limit']['request'] : [];
        $rateLogin = isset($status['rate_limit']['login']) && is_array($status['rate_limit']['login']) ? $status['rate_limit']['login'] : [];
        $userProfiler = isset($status['user_profiler']) && is_array($status['user_profiler']) ? $status['user_profiler'] : [];
        $profilerSampling = isset($userProfiler['sampling']) && is_array($userProfiler['sampling']) ? $userProfiler['sampling'] : [];
        $profiles = isset($userProfiler['profiles']) && is_array($userProfiler['profiles']) ? $userProfiler['profiles'] : [];
        $recentVisits = isset($userProfiler['recent_events']) && is_array($userProfiler['recent_events']) ? $userProfiler['recent_events'] : [];
        $wafSettings = WafSettings::fromWordPress()->all();
        $blockedIps = isset($wafSettings['ip_blocklist']) && is_array($wafSettings['ip_blocklist']) ? $wafSettings['ip_blocklist'] : [];
        $timeline = IncidentTimeline::fromWordPress();
        $incidentTimeline = $timeline->summary(40);
        $activityEvents = $this->activityLogEvents($timeline->summary(250));
        $activityStats = $this->activityLogStats($activityEvents);
        $incidentSeverityCounts = isset($incidentTimeline['severity_counts']) && is_array($incidentTimeline['severity_counts']) ? $incidentTimeline['severity_counts'] : [];
        $incidentTotal = (int) ($incidentTimeline['total'] ?? 0);
        $highSeverityCount = (int) ($incidentSeverityCounts['critical'] ?? 0) + (int) ($incidentSeverityCounts['high'] ?? 0);
        $trackedProfiles = (int) ($userProfiler['tracked_profiles'] ?? count($profiles));
        $profilerRetentionDays = max(1, (int) ($userProfiler['retention_days'] ?? 30));
        $profilerCookieTtlDays = max(1, (int) ($userProfiler['cookie_ttl_days'] ?? $profilerRetentionDays));
        $profilerSampleRate = max(1, (int) ($profilerSampling['anonymous_frontend_get_rate'] ?? 1));
        $profilerSchemaWarningKey = trim((string) ($userProfiler['schema_warning'] ?? ''));
        $profilerSchemaWarning = '';
        if ($profilerSchemaWarningKey === 'user_profiler_table_missing') {
            $profilerSchemaWarning = __('The User Profiler database table is missing. Run the SHIELD-X schema repair before using this feature.', 'xerotact-shield-x-security');
        } elseif ($profilerSchemaWarningKey === 'user_profiler_database_unavailable') {
            $profilerSchemaWarning = __('The User Profiler database connection is unavailable. Request recording is paused until the database is available.', 'xerotact-shield-x-security');
        }
        $wafHitCount = (int) ($wafStats['total_hits'] ?? 0);
        $blockedIpCount = count($blockedIps);

        $this->openPage('shield-x-activity', $status);
        ?>
        <section class="shieldx-metrics shieldx-activity-metrics" aria-label="<?php echo esc_attr__('Incident and activity statistics', 'xerotact-shield-x-security'); ?>">
            <?php
            $this->renderMetric(__('Incidents', 'xerotact-shield-x-security'), number_format_i18n($incidentTotal), $incidentTotal > 0 ? 'warn' : 'good', 'activity');
            $this->renderMetric(__('High severity', 'xerotact-shield-x-security'), number_format_i18n($highSeverityCount), $highSeverityCount > 0 ? 'warn' : 'good', 'warning-triangle');
            $this->renderMetric(__('Visitors', 'xerotact-shield-x-security'), number_format_i18n($trackedProfiles), $trackedProfiles > 0 ? 'info' : 'warn', 'group');
            $this->renderMetric(__('WAF hits', 'xerotact-shield-x-security'), number_format_i18n($wafHitCount), $wafHitCount > 0 ? 'warn' : 'good', 'pc-firewall');
            $this->renderMetric(__('Activity log', 'xerotact-shield-x-security'), number_format_i18n((int) ($activityStats['total'] ?? 0)), (int) ($activityStats['total'] ?? 0) > 0 ? 'info' : 'warn', 'clock-rotate-right');
            $this->renderMetric(__('IP blocks', 'xerotact-shield-x-security'), number_format_i18n($blockedIpCount), $blockedIpCount > 0 ? 'warn' : 'good', 'lock');
            ?>
        </section>
        <div class="shieldx-layout">
            <?php $this->renderIncidentTimeline($incidentTimeline); ?>
            <?php $this->renderActivityLogCard($activityEvents); ?>

            <section class="shieldx-panel shieldx-panel--full">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('User Profiler', 'xerotact-shield-x-security'); ?></h2>
                    <span><?php echo esc_html(!empty($userProfiler['enabled']) ? __('Opt-in collection enabled', 'xerotact-shield-x-security') : __('Opt-in collection disabled', 'xerotact-shield-x-security')); ?></span>
                </div>
                <form class="shieldx-profiler-settings" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                    <input type="hidden" name="action" value="shieldx_save_user_profiler">
                    <?php wp_nonce_field('shieldx_save_user_profiler'); ?>
                    <label class="shieldx-hardening-option">
                        <input type="checkbox" name="user_profiler_enabled" value="1" <?php checked(!empty($userProfiler['enabled'])); ?>>
                        <span>
                            <strong><?php echo esc_html__('Enable visitor profiler', 'xerotact-shield-x-security'); ?></strong>
                            <small><?php echo esc_html__('Sets a first-party visitor cookie and stores recent URL/query/referrer metadata locally for security investigations. Anonymous front-end GET requests are sampled; authenticated front-end requests and non-GET requests are recorded at the full rate with their WordPress user ID when available. Automated user agents are not filtered. URL and referrer query values are redacted. Country indicators are informational proxy/CDN headers when available. Keep disabled until your site policy covers this collection.', 'xerotact-shield-x-security'); ?></small>
                        </span>
                    </label>
                    <button type="submit" class="button button-primary"><?php echo esc_html__('Save profiler setting', 'xerotact-shield-x-security'); ?></button>
                </form>
                <?php if ($profilerSchemaWarning !== '') : ?>
                    <p class="shieldx-alert shieldx-alert--warning"><?php echo esc_html($profilerSchemaWarning); ?></p>
                <?php endif; ?>
                <dl class="shieldx-detail-list shieldx-detail-list--compact">
                    <?php $this->renderDetail(__('Collection', 'xerotact-shield-x-security'), !empty($userProfiler['enabled']) ? __('Enabled', 'xerotact-shield-x-security') : __('Disabled', 'xerotact-shield-x-security')); ?>
                    <?php /* translators: %d: number of days. */ ?>
                    <?php $this->renderDetail(__('Retention', 'xerotact-shield-x-security'), sprintf(__('%d days', 'xerotact-shield-x-security'), $profilerRetentionDays)); ?>
                    <?php /* translators: %d: number of days. */ ?>
                    <?php $this->renderDetail(__('Visitor cookie lifetime', 'xerotact-shield-x-security'), sprintf(__('%d days', 'xerotact-shield-x-security'), $profilerCookieTtlDays)); ?>
                    <?php /* translators: %d: one-in-N anonymous GET sampling rate. */ ?>
                    <?php $this->renderDetail(__('Anonymous GET sampling', 'xerotact-shield-x-security'), $profilerSampleRate > 1 ? sprintf(__('1 in %d requests', 'xerotact-shield-x-security'), $profilerSampleRate) : __('All requests recorded', 'xerotact-shield-x-security')); ?>
                    <?php $this->renderDetail(__('Tracked profiles', 'xerotact-shield-x-security'), (string) ($userProfiler['tracked_profiles'] ?? 0)); ?>
                    <?php $this->renderDetail(__('Tracked IPs', 'xerotact-shield-x-security'), (string) ($userProfiler['tracked_ips'] ?? 0)); ?>
                    <?php $this->renderDetail(__('Recorded visits', 'xerotact-shield-x-security'), (string) ($userProfiler['total_events'] ?? 0)); ?>
                </dl>

                <?php if (empty($profiles)) : ?>
                    <p class="shieldx-empty"><?php echo esc_html__('No visitor profiles have been recorded yet.', 'xerotact-shield-x-security'); ?></p>
                <?php else : ?>
                    <div class="shieldx-profiler-shell" data-shieldx-profiler>
                        <div class="shieldx-user-list-wrap">
                            <table class="widefat shieldx-user-list-table">
                                <thead>
                                    <tr>
                                        <th><?php echo esc_html__('Visitor', 'xerotact-shield-x-security'); ?></th>
                                        <th><?php echo esc_html__('Recorded visits', 'xerotact-shield-x-security'); ?></th>
                                        <th><?php echo esc_html__('Recorded URLs', 'xerotact-shield-x-security'); ?></th>
                                        <th><?php echo esc_html__('First seen', 'xerotact-shield-x-security'); ?></th>
                                        <th><?php echo esc_html__('Last seen', 'xerotact-shield-x-security'); ?></th>
                                        <th><?php echo esc_html__('Status', 'xerotact-shield-x-security'); ?></th>
                                        <th><?php echo esc_html__('Action', 'xerotact-shield-x-security'); ?></th>
                                    </tr>
                                </thead>
                                <tbody>
                                    <?php foreach ($profiles as $profile) : ?>
                                        <?php $profile = is_array($profile) ? $profile : []; ?>
                                        <?php $this->renderUserProfileListRow($profile, false); ?>
                                        <?php $this->renderUserProfileDetailRow($profile, false); ?>
                                    <?php endforeach; ?>
                                </tbody>
                            </table>
                        </div>
                    </div>
                    <p class="shieldx-help-text"><?php echo esc_html__('The URL trail starts with all recorded visitors. Anonymous front-end GET requests are sampled, so displayed counts reflect recorded activity rather than total site traffic. Click a visitor row to open profile details inline and focus its recorded requests.', 'xerotact-shield-x-security'); ?></p>
                <?php endif; ?>
            </section>

            <section class="shieldx-panel shieldx-panel--full">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('Visitor URL & Query Trail', 'xerotact-shield-x-security'); ?></h2>
                    <span>
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <span data-shieldx-profiler-count><?php echo esc_html(sprintf(__('%d recent recorded requests', 'xerotact-shield-x-security'), count($recentVisits))); ?></span>
                        <?php if (!empty($recentVisits)) : ?>
                            <button type="button" class="button-link shieldx-profile-show-all" data-shieldx-profiler-show-all><?php echo esc_html__('Show all recorded requests', 'xerotact-shield-x-security'); ?></button>
                        <?php endif; ?>
                    </span>
                </div>
                <?php if (empty($recentVisits)) : ?>
                    <p class="shieldx-empty"><?php echo esc_html__('No URL trail is available yet.', 'xerotact-shield-x-security'); ?></p>
                <?php else : ?>
                    <div class="shieldx-compat-table-wrap">
                        <table class="widefat striped shieldx-profiler-table">
                            <thead>
                                <tr>
                                    <th><?php echo esc_html__('Time', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('IP / Browser', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Visitor', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('URL', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Query', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Referer', 'xerotact-shield-x-security'); ?></th>
                                    <th><?php echo esc_html__('Action', 'xerotact-shield-x-security'); ?></th>
                                </tr>
                            </thead>
                            <tbody>
                                <?php foreach ($recentVisits as $visit) : ?>
                                    <?php $this->renderUserProfilerVisitRow(is_array($visit) ? $visit : [], $blockedIps); ?>
                                <?php endforeach; ?>
                            </tbody>
                        </table>
                        <p class="shieldx-empty" data-shieldx-profiler-empty hidden><?php echo esc_html__('No recent URL trail is available for the selected visitor.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
            </section>

            <section class="shieldx-panel">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('Rate State', 'xerotact-shield-x-security'); ?></h2>
                    <span><?php echo esc_html($this->formatLabel((string) ($rateRequest['mode'] ?? 'log_only'))); ?></span>
                    <?php
                    $this->renderSectionSettingsLink(
                        admin_url('admin.php?page=shield-x-hardening#shieldx-rate-limiting'),
                        __('Open request rate-limit settings in Hardening', 'xerotact-shield-x-security')
                    );
                    ?>
                </div>
                <dl class="shieldx-detail-list">
                    <?php $this->renderDetail(__('Tracked clients', 'xerotact-shield-x-security'), (string) ($rateRequest['tracked_clients'] ?? 0)); ?>
                    <?php $this->renderDetail(__('Recent limited clients', 'xerotact-shield-x-security'), (string) ($rateRequest['recent_limited_clients'] ?? 0)); ?>
                    <?php $this->renderDetail(__('Sustained window', 'xerotact-shield-x-security'), (string) ($rateRequest['limit'] ?? 0) . ' / ' . (string) ($rateRequest['window_seconds'] ?? 0) . 's'); ?>
                    <?php $this->renderDetail(__('Burst window', 'xerotact-shield-x-security'), (string) ($rateRequest['burst_limit'] ?? 0) . ' / ' . (string) ($rateRequest['burst_window_seconds'] ?? 0) . 's'); ?>
                </dl>
            </section>

            <section class="shieldx-panel">
                <div class="shieldx-section-head">
                    <h2><?php echo esc_html__('Login State', 'xerotact-shield-x-security'); ?></h2>
                    <span><?php echo esc_html(!empty($rateLogin['enabled']) ? __('Enabled', 'xerotact-shield-x-security') : __('Disabled', 'xerotact-shield-x-security')); ?></span>
                    <?php
                    $this->renderSectionSettingsLink(
                        admin_url('admin.php?page=shield-x-login-security#shieldx-login-bruteforce'),
                        __('Open login brute-force protection settings', 'xerotact-shield-x-security')
                    );
                    ?>
                </div>
                <dl class="shieldx-detail-list">
                    <?php $this->renderDetail(__('Tracked identities', 'xerotact-shield-x-security'), (string) ($rateLogin['tracked_identities'] ?? 0)); ?>
                    <?php $this->renderDetail(__('Active lockouts', 'xerotact-shield-x-security'), (string) ($rateLogin['locked_identities'] ?? 0)); ?>
                    <?php $this->renderDetail(__('Failure window', 'xerotact-shield-x-security'), (string) ($rateLogin['failure_limit'] ?? 0) . ' / ' . (string) ($rateLogin['window_seconds'] ?? 0) . 's'); ?>
                    <?php $this->renderDetail(__('Lockout length', 'xerotact-shield-x-security'), (string) ($rateLogin['lockout_seconds'] ?? 0) . 's'); ?>
                </dl>
            </section>
        </div>
        <?php
        $this->closePage();
    }

    public function activityQuickBlock(): void
    {
        $this->assertCanSave('shieldx_activity_quick_block');

        $type = $this->postedText('block_type', 'ip') === 'user_agent' ? 'user_agent' : 'ip';
        $value = trim($this->postedText('block_value', ''));

        if ($value === '') {
            $this->redirectActivityBlock(['shieldx_block_error' => 'empty']);
        }

        $wafSettings = WafSettings::fromWordPress();
        $current = $wafSettings->all();

        if ($type === 'ip') {
            if (!filter_var($value, FILTER_VALIDATE_IP)) {
                $this->redirectActivityBlock(['shieldx_block_error' => 'invalid_ip']);
            }

            if ($this->isCurrentWafClientIp($value)) {
                $this->redirectActivityBlock(['shieldx_block_error' => 'current_client']);
            }

            $current['ip_blocklist'] = $this->appendLineListValue($this->lineListValue($current['ip_blocklist'] ?? []), $value);
        } else {
            if ($this->isCurrentWafClientUserAgent($value)) {
                $this->redirectActivityBlock(['shieldx_block_error' => 'current_client']);
            }

            $current['user_agent_blocklist'] = $this->appendLineListValue($this->lineListValue($current['user_agent_blocklist'] ?? []), $value);
        }

        $wafSettings->save(
            isset($current['disabled_rules']) && is_array($current['disabled_rules']) ? $current['disabled_rules'] : [],
            isset($current['plugin_whitelist']) && is_array($current['plugin_whitelist']) ? $current['plugin_whitelist'] : [],
            $current
        );

        $this->redirectActivityBlock([
            'shieldx_blocked' => $type === 'ip' ? 'ip' : 'browser',
        ]);
    }

    public function wafHitAction(): void
    {
        $this->assertCanSave('shieldx_waf_hit_action');

        $ip = trim($this->postedText('hit_ip', ''));
        $hitAction = $this->postedText('hit_action', '');

        if (!in_array($hitAction, ['allow', 'block'], true)) {
            $this->redirectWafHitAction(['shieldx_saved' => '1']);
        }

        if (!filter_var($ip, FILTER_VALIDATE_IP)) {
            $this->redirectWafHitAction(['shieldx_saved' => '1']);
        }

        $wafSettings = WafSettings::fromWordPress();
        $current = $wafSettings->all();

        if ($hitAction === 'block') {
            if ($this->isCurrentWafClientIp($ip)) {
                $this->redirectWafHitAction(['shieldx_saved' => '1']);
            }

            $current['ip_allowlist'] = $this->removeLineListValue($this->lineListValue($current['ip_allowlist'] ?? []), $ip);
            $current['ip_blocklist'] = $this->appendLineListValue($this->lineListValue($current['ip_blocklist'] ?? []), $ip);
        } else {
            $current['ip_allowlist'] = $this->appendLineListValue($this->lineListValue($current['ip_allowlist'] ?? []), $ip);
            $current['ip_blocklist'] = $this->removeLineListValue($this->lineListValue($current['ip_blocklist'] ?? []), $ip);
            $current['temporary_blocks'] = $this->removeTemporaryIpBlocks((array) ($current['temporary_blocks'] ?? []), $ip);
        }

        $wafSettings->save(
            isset($current['disabled_rules']) && is_array($current['disabled_rules']) ? $current['disabled_rules'] : [],
            isset($current['plugin_whitelist']) && is_array($current['plugin_whitelist']) ? $current['plugin_whitelist'] : [],
            $current
        );

        $this->redirectWafHitAction(['shieldx_saved' => '1']);
    }

    public function renderSystem(): void
    {
        if ($this->renderAsyncPageShell('shield-x-system')) {
            return;
        }

        $status = $this->statusForPage();
        $maintenance = isset($status['maintenance']) && is_array($status['maintenance']) ? $status['maintenance'] : [];
        $databaseReport = isset($maintenance['database_tables']) && is_array($maintenance['database_tables']) ? $maintenance['database_tables'] : [];
        $updateCoordination = isset($maintenance['update_coordination']) && is_array($maintenance['update_coordination']) ? $maintenance['update_coordination'] : [];
        $humanChallenge = HumanChallengeSettings::fromWordPress()->all();
        $fileTypes = FileTypeSettings::fromWordPress();
        $postureReport = \ShieldX\Core\SecurityPosture::fromWordPress()->report();
        $provenanceEntries = \ShieldX\Scanner\ComponentProvenance::fromWordPress()->all();
        $themePackageUploaded = $this->queryText('shieldx_theme_package_uploaded');
        $themePackageUploadError = $this->queryText('shieldx_theme_package_upload_error');
        $themePackageTrusted = $this->queryText('shieldx_theme_package_trusted');
        $themePackageTrustError = $this->queryText('shieldx_theme_package_trust_error');

        $this->openPage('shield-x-system', $status);
        ?>
        <?php if ($themePackageUploaded !== '') : ?>
            <div class="notice notice-success is-dismissible">
                <p>
                    <?php
                    /* translators: %s: theme slug accepted from an uploaded package. */
                    echo esc_html(sprintf(__('Uploaded theme package accepted for %s.', 'xerotact-shield-x-security'), $themePackageUploaded));
                    ?>
                </p>
            </div>
        <?php endif; ?>
        <?php if ($themePackageUploadError !== '') : ?>
            <div class="notice notice-error is-dismissible"><p><?php echo esc_html($themePackageUploadError); ?></p></div>
        <?php endif; ?>
        <?php if ($themePackageTrusted !== '') : ?>
            <div class="notice notice-warning is-dismissible">
                <p>
                    <?php
                    /* translators: %s: theme slug trusted from the installed copy. */
                    echo esc_html(sprintf(__('Installed theme files are now trusted for %s.', 'xerotact-shield-x-security'), $themePackageTrusted));
                    ?>
                </p>
            </div>
        <?php endif; ?>
        <?php if ($themePackageTrustError !== '') : ?>
            <div class="notice notice-error is-dismissible"><p><?php echo esc_html($themePackageTrustError); ?></p></div>
        <?php endif; ?>
        <?php $this->renderSystemToolSelector($humanChallenge, $fileTypes); ?>

        <div class="shieldx-layout shieldx-layout--single">
            <?php $this->renderHumanChallengeSettingsPanel($humanChallenge); ?>
            <?php $this->renderFileTypePolicyPanel($fileTypes); ?>
            <?php $this->renderSettingsPortability(); ?>
        </div>

        <section id="shieldx-system-diagnostics" data-shieldx-system-panel="diagnostics" role="tabpanel" aria-labelledby="shieldx-system-tab-diagnostics">
            <div class="shieldx-layout shieldx-layout--single shieldx-system-audit-layout">
                <section id="shieldx-security-posture" class="shieldx-panel shieldx-panel--full">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Security Posture', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html__('Read-only audit', 'xerotact-shield-x-security'); ?></span>
                    </div>
                    <div class="shieldx-table-wrap shieldx-system-audit-table-wrap">
                        <table class="widefat striped">
                            <thead><tr><th><?php echo esc_html__('Check', 'xerotact-shield-x-security'); ?></th><th><?php echo esc_html__('Severity', 'xerotact-shield-x-security'); ?></th><th><?php echo esc_html__('Status', 'xerotact-shield-x-security'); ?></th><th><?php echo esc_html__('Evidence', 'xerotact-shield-x-security'); ?></th><th><?php echo esc_html__('Action', 'xerotact-shield-x-security'); ?></th></tr></thead>
                            <tbody>
                                <?php foreach ($postureReport as $finding) : ?>
                                    <?php $action = $this->securityPostureAction(is_array($finding) ? $finding : []); ?>
                                    <tr data-shieldx-posture-status="<?php echo esc_attr((string) ($finding['status'] ?? 'unavailable')); ?>">
                                        <td><strong><?php echo esc_html((string) $finding['title']); ?></strong></td>
                                        <td><?php echo esc_html($this->formatLabel((string) $finding['severity'])); ?></td>
                                        <td><?php echo esc_html($this->formatLabel((string) $finding['status'])); ?></td>
                                        <td><?php echo esc_html((string) $finding['evidence']); ?></td>
                                        <td>
                                            <?php if ($action !== []) : ?>
                                                <a class="button button-small" data-shieldx-posture-action href="<?php echo esc_url($action['url']); ?>"><?php echo esc_html($action['label']); ?></a>
                                            <?php else : ?>
                                                <span class="shieldx-muted"><?php echo esc_html__('No action', 'xerotact-shield-x-security'); ?></span>
                                            <?php endif; ?>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                            </tbody>
                        </table>
                    </div>
                </section>

                <section id="shieldx-component-provenance" class="shieldx-panel shieldx-panel--full">
                    <div class="shieldx-section-head">
                        <h2><?php echo esc_html__('Component Provenance', 'xerotact-shield-x-security'); ?></h2>
                        <span><?php echo esc_html__('Warning only', 'xerotact-shield-x-security'); ?></span>
                    </div>
                    <div class="shieldx-table-wrap shieldx-system-audit-table-wrap">
                        <table class="widefat striped">
                            <thead><tr><th><?php echo esc_html__('Component', 'xerotact-shield-x-security'); ?></th><th><?php echo esc_html__('Type', 'xerotact-shield-x-security'); ?></th><th><?php echo esc_html__('Version', 'xerotact-shield-x-security'); ?></th><th><?php echo esc_html__('Provenance', 'xerotact-shield-x-security'); ?></th><th><?php echo esc_html__('Action', 'xerotact-shield-x-security'); ?></th></tr></thead>
                            <tbody>
                                <?php foreach ($provenanceEntries as $component) : ?>
                                    <tr data-shieldx-component-status="<?php echo esc_attr((string) ($component['status'] ?? 'unverified')); ?>" data-shieldx-component-type="<?php echo esc_attr((string) ($component['type'] ?? '')); ?>">
                                        <td><strong><?php echo esc_html((string) $component['name']); ?></strong><small><?php echo esc_html((string) $component['slug']); ?></small></td>
                                        <td><?php echo esc_html($this->formatLabel((string) $component['type'])); ?></td>
                                        <td><?php echo esc_html((string) $component['version']); ?></td>
                                        <td><?php echo esc_html($this->formatLabel((string) $component['status'])); ?></td>
                                        <td><?php $this->renderComponentProvenanceAction(is_array($component) ? $component : []); ?></td>
                                    </tr>
                                <?php endforeach; ?>
                            </tbody>
                        </table>
                    </div>
                </section>
            </div>

            <div class="shieldx-layout shieldx-layout--single">
                <?php $this->renderUpdateCoordinationPanel($updateCoordination); ?>
                <?php $this->renderEnvironmentPanel($status); ?>
                <?php $this->renderSystemHealth($status); ?>
                <?php $this->renderDataStoresPanel($status); ?>
            </div>

            <div class="shieldx-layout shieldx-layout--single shieldx-system-full-layout">
                <?php $this->renderCompatibilityChecks($status); ?>
                <?php $this->renderModuleCompatibility($status); ?>
                <?php $this->renderPhpExtensionInventory($status); ?>
                <?php $this->renderPhpFunctionInventory($status); ?>
                <?php $this->renderDatabaseTableSizeReport($databaseReport); ?>
            </div>
        </section>
        <?php
        $this->closePage();
    }

    /**
     * @param array<string,mixed> $finding
     * @return array{label:string,url:string}|array{}
     */
    private function securityPostureAction(array $finding): array
    {
        $status = (string) ($finding['status'] ?? '');
        $url = (string) ($finding['remediation_url'] ?? '');
        if (!in_array($status, ['warning', 'unavailable'], true) || $url === '') {
            return [];
        }

        $labels = [
            'production_debug' => __('Review configuration', 'xerotact-shield-x-security'),
            'php_error_display' => __('Review PHP configuration', 'xerotact-shield-x-security'),
            'https_urls' => __('Open settings', 'xerotact-shield-x-security'),
            'default_admin_username' => __('Review users', 'xerotact-shield-x-security'),
            'stale_privileged_accounts' => __('Review users', 'xerotact-shield-x-security'),
            'inactive_components' => __('Manage plugins', 'xerotact-shield-x-security'),
            'excessive_sessions' => __('Manage sessions', 'xerotact-shield-x-security'),
            'missing_required_2fa' => __('Configure 2FA', 'xerotact-shield-x-security'),
            'component_provenance' => __('Review components', 'xerotact-shield-x-security'),
        ];
        $id = (string) ($finding['id'] ?? '');

        return [
            'label' => isset($labels[$id]) ? $labels[$id] : __('Review', 'xerotact-shield-x-security'),
            'url' => $url,
        ];
    }

    /**
     * @param array<string,mixed> $component
     */
    private function renderComponentProvenanceAction(array $component): void
    {
        $type = (string) ($component['type'] ?? '');
        $slug = sanitize_key((string) ($component['slug'] ?? ''));
        $status = (string) ($component['status'] ?? '');
        if ($status !== 'unverified' || $slug === '' || !in_array($type, ['plugin', 'theme'], true)) {
            ?>
            <span class="shieldx-muted"><?php echo esc_html__('No action', 'xerotact-shield-x-security'); ?></span>
            <?php
            return;
        }

        $isPlugin = $type === 'plugin';
        $objectKey = $isPlugin ? 'plugin' : 'theme';
        $trustAction = $isPlugin ? 'shieldx_trust_installed_plugin' : 'shieldx_trust_installed_theme';
        $uploadAction = $isPlugin ? 'shieldx_upload_plugin_package' : 'shieldx_upload_theme_package';
        $trustNonce = $isPlugin ? 'shieldx_trust_installed_plugin' : 'shieldx_trust_installed_theme';
        $uploadNonce = $isPlugin ? 'shieldx_upload_plugin_package' : 'shieldx_upload_theme_package';
        $uploadField = $isPlugin ? 'plugin_package' : 'theme_package';
        $objectLabel = $isPlugin ? __('plugin', 'xerotact-shield-x-security') : __('theme', 'xerotact-shield-x-security');
        $uploadId = 'shieldx-provenance-zip-' . sanitize_key($type . '-' . $slug);
        $confirm = sprintf(
            /* translators: %s: plugin or theme. */
            __('Trust the currently installed %s files as the clean baseline? Only do this after you have verified the installed files are clean.', 'xerotact-shield-x-security'),
            $objectLabel
        );
        ?>
        <div class="shieldx-component-provenance-actions">
            <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                <input type="hidden" name="action" value="<?php echo esc_attr($trustAction); ?>">
                <input type="hidden" name="<?php echo esc_attr($objectKey); ?>_slug" value="<?php echo esc_attr($slug); ?>">
                <input type="hidden" name="provenance_return" value="system">
                <?php wp_nonce_field($trustNonce); ?>
                <button
                    type="submit"
                    class="button button-small shieldx-component-provenance-icon-button"
                    data-shieldx-confirm="<?php echo esc_attr($confirm); ?>"
                    aria-label="<?php echo esc_attr__('Trust installed files', 'xerotact-shield-x-security'); ?>"
                    title="<?php echo esc_attr__('Trust installed files', 'xerotact-shield-x-security'); ?>"
                >
                    <?php echo wp_kses(Iconoir::svg('badge-check', 'shieldx-icon shieldx-component-provenance-action-icon'), $this->allowedIconSvg()); ?>
                </button>
            </form>
            <form class="shieldx-component-provenance-upload-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" enctype="multipart/form-data">
                <input type="hidden" name="action" value="<?php echo esc_attr($uploadAction); ?>">
                <input type="hidden" name="<?php echo esc_attr($objectKey); ?>_slug" value="<?php echo esc_attr($slug); ?>">
                <input type="hidden" name="provenance_return" value="system">
                <?php wp_nonce_field($uploadNonce); ?>
                <input id="<?php echo esc_attr($uploadId); ?>" class="shieldx-component-provenance-file-input" type="file" name="<?php echo esc_attr($uploadField); ?>" accept=".zip,application/zip,application/x-zip-compressed" required>
                <label
                    class="button button-small shieldx-component-provenance-icon-button shieldx-component-provenance-choose"
                    for="<?php echo esc_attr($uploadId); ?>"
                    title="<?php echo esc_attr__('Choose ZIP package', 'xerotact-shield-x-security'); ?>"
                >
                    <?php echo wp_kses(Iconoir::svg('folder-settings', 'shieldx-icon shieldx-component-provenance-action-icon'), $this->allowedIconSvg()); ?>
                    <span class="shieldx-screen-reader-text"><?php echo esc_html__('Choose ZIP package', 'xerotact-shield-x-security'); ?></span>
                </label>
                <button
                    type="submit"
                    class="button button-small shieldx-component-provenance-icon-button"
                    aria-label="<?php echo esc_attr__('Verify ZIP package', 'xerotact-shield-x-security'); ?>"
                    title="<?php echo esc_attr__('Verify ZIP package', 'xerotact-shield-x-security'); ?>"
                >
                    <?php echo wp_kses(Iconoir::svg('page-search', 'shieldx-icon shieldx-component-provenance-action-icon'), $this->allowedIconSvg()); ?>
                </button>
            </form>
        </div>
        <?php
    }

    public function renderLogRotation(): void
    {
        if ($this->renderAsyncPageShell('shield-x-log-rotation')) {
            return;
        }

        $status = $this->statusForPage();
        $maintenance = isset($status['maintenance']) && is_array($status['maintenance']) ? $status['maintenance'] : [];
        $logRotation = isset($maintenance['log_rotation']) && is_array($maintenance['log_rotation']) ? $maintenance['log_rotation'] : [];
        $files = isset($logRotation['files']) && is_array($logRotation['files']) ? $logRotation['files'] : [];
        $tempCleanup = isset($logRotation['temp_cleanup']) && is_array($logRotation['temp_cleanup']) ? $logRotation['temp_cleanup'] : [];
        $lastTempCleanup = isset($tempCleanup['last_result']) && is_array($tempCleanup['last_result']) ? $tempCleanup['last_result'] : [];
        $targets = isset($logRotation['target_status']) && is_array($logRotation['target_status']) ? $logRotation['target_status'] : [];
        $databaseTargets = 0;

        foreach ($targets as $target) {
            if (is_array($target) && (string) ($target['type'] ?? '') === 'database') {
                $databaseTargets++;
            }
        }

        $this->openPage('shield-x-log-rotation', $status);
        ?>
        <section class="shieldx-metrics" aria-label="<?php echo esc_attr__('SHIELD-X cleanup summary', 'xerotact-shield-x-security'); ?>">
            <?php
            $this->renderMetric(__('Rotation', 'xerotact-shield-x-security'), !empty($logRotation['enabled']) ? __('Enabled', 'xerotact-shield-x-security') : __('Disabled', 'xerotact-shield-x-security'), !empty($logRotation['enabled']) ? 'good' : 'warn', 'clock-rotate-right');
            $this->renderMetric(__('File logs', 'xerotact-shield-x-security'), (string) ($files['count'] ?? 0), 'info', 'page');
            $this->renderMetric(__('File size', 'xerotact-shield-x-security'), $this->formatBytes((int) ($files['total_bytes'] ?? 0)), 'info', 'database-stats');
            $this->renderMetric(__('Temp cleanup', 'xerotact-shield-x-security'), !empty($lastTempCleanup['ran_at']) ? $this->formatBytes((int) ($lastTempCleanup['deleted_bytes'] ?? 0)) : __('Ready', 'xerotact-shield-x-security'), 'info', 'tools');
            $this->renderMetric(__('DB targets', 'xerotact-shield-x-security'), (string) $databaseTargets, !empty($logRotation['database_available']) ? 'good' : 'warn', 'database');
            ?>
        </section>
        <div class="shieldx-layout shieldx-layout--single">
            <?php $this->renderLogRotationPanel($logRotation); ?>
            <?php $this->renderTempCleanupPanel($logRotation); ?>
            <?php $this->renderUninstallDataPanel(); ?>
        </div>
        <?php
        $this->closePage();
    }

    public function exportSettings(): void
    {
        $this->assertCanSave('shieldx_export_settings');

        $includeSiteSecrets = $this->postFlag('include_site_secrets');
        $export = SettingsPortability::export($includeSiteSecrets);
        $json = wp_json_encode($export, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

        if (!is_string($json)) {
            wp_die(esc_html__('SHIELD-X could not build the export file.', 'xerotact-shield-x-security'));
        }

        $parsedHome = function_exists('wp_parse_url') && function_exists('home_url') ? wp_parse_url((string) home_url('/')) : [];
        $site = is_array($parsedHome) ? (string) ($parsedHome['host'] ?? '') : '';
        $site = preg_replace('/[^A-Za-z0-9._-]+/', '-', $site) ?: 'site';
        $filename = 'shield-x-settings-' . strtolower(trim((string) $site, '-')) . '-' . gmdate('Ymd-His') . '.json';

        NotificationManager::fromWordPress()->notify('system.settings_changed', [
            'operation' => 'settings_exported',
            'include_site_secrets' => $includeSiteSecrets ? 'yes' : 'no',
        ], 'info');

        if (function_exists('nocache_headers')) {
            nocache_headers();
        }

        header('Content-Type: application/json; charset=utf-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('X-ShieldX-Notice: site-bound-export');
        header('X-Content-Type-Options: nosniff');
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- JSON file response, not HTML output.
        echo $json;
        exit;
    }

    public function importSettings(): void
    {
        $this->assertCanSave('shieldx_import_settings');

        $json = $this->uploadedImportJson();
        if ($json === '') {
            $json = trim($this->postedText('settings_import_json', ''));
        }

        if ($json === '') {
            $this->redirectSystemImport([
                'shieldx_import_error' => AdminFlashNotice::flag('shieldx_import_error', __('Choose a SHIELD-X settings JSON file or paste JSON.', 'xerotact-shield-x-security')),
            ]);
        }

        $mode = $this->postedText('settings_import_mode', 'merge') === 'replace' ? 'replace' : 'merge';
        $nonce = $this->postedText('_wpnonce', '');
        $result = SettingsPortability::import($json, $mode, 'shieldx_import_settings', $nonce);

        if (empty($result['ok'])) {
            $this->redirectSystemImport([
                'shieldx_import_error' => AdminFlashNotice::flag('shieldx_import_error', (string) ($result['message'] ?? __('Settings import failed.', 'xerotact-shield-x-security'))),
            ]);
        }

        $this->redirectSystemImport([
            'shieldx_imported' => '1',
            'shieldx_import_modules' => AdminFlashNotice::flag('shieldx_import_modules', implode(', ', (array) ($result['modules'] ?? []))),
        ]);
    }

    public function saveLogRotationSettings(): void
    {
        $this->assertCanSave('shieldx_save_log_rotation');

        LogRotationSettings::fromWordPress()->save([
            'enabled' => $this->postFlag('log_rotation_enabled'),
            'retention_days' => $this->postedInt('log_retention_days', 90),
            'max_files' => $this->postedInt('log_max_files', 90),
            'targets' => $this->postedArray('log_rotation_targets'),
            'excluded_file_logs' => $this->lineListArray($this->postedText('log_excluded_file_logs', '')),
        ]);

        if ($this->postFlag('rotate_now')) {
            $result = LogRotator::fromWordPress()->rotate(true);
            $status = (string) ($result['status'] ?? 'failed');
            if (!in_array($status, ['completed', 'completed_with_warnings', 'failed', 'skipped'], true)) {
                $status = 'failed';
            }

            $query = [
                'shieldx_log_rotation_run' => '1',
                'shieldx_log_rotation_status' => $status,
                'shieldx_log_rotation_failed_files' => (string) max(0, (int) ($result['failed_files'] ?? 0)),
                'shieldx_log_rotation_deleted_files' => (string) max(0, (int) ($result['deleted_files'] ?? 0)),
                'shieldx_log_rotation_deleted_rows' => (string) max(0, (int) ($result['deleted_rows'] ?? 0)),
            ];
        } else {
            $query = ['shieldx_log_rotation_saved' => '1'];
        }

        $this->redirectLogRotation($query);
    }

    public function saveUninstallSettings(): void
    {
        $this->assertCanSave('shieldx_save_uninstall_settings');

        Uninstaller::savePurgeDataOnUninstall($this->postFlag('purge_data_on_uninstall'));

        $this->redirectLogRotation(['shieldx_uninstall_settings_saved' => '1']);
    }

    public function scanLogRotationFiles(): void
    {
        $this->assertCanSave('shieldx_scan_log_rotation');

        $ajax = $this->isProgressiveScanAjax();
        $settings = LogRotationSettings::fromWordPress();
        $current = $settings->all();
        $targets = isset($current['targets']) && is_array($current['targets']) ? array_map('strval', $current['targets']) : [];
        $targets[] = 'file_daily_logs';
        $current['targets'] = array_values(array_unique($targets));
        $settings->save($current);

        $scanId = $this->queryText('scan_id', $this->postedText('scan_id', ''));
        if ($scanId !== '' && preg_match('/^[a-f0-9]{32}$/i', $scanId) !== 1) {
            $scanId = '';
        }

        $result = LogRotator::fromWordPress()->scanFileLogsForRotation($scanId);
        $status = (string) ($result['status'] ?? 'completed');

        if ($status === 'running') {
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => true,
                    'status' => 'running',
                    'scan_id' => (string) ($result['scan_id'] ?? ''),
                    'progress' => $this->logRotationScanProgressPayload($result),
                    'next_url' => $this->logRotationScanContinueUrl((string) ($result['scan_id'] ?? ''), true),
                ]);
            }

            $this->redirectLogRotation([
                'shieldx_log_scan_running' => '1',
                'shieldx_log_scan_id' => (string) ($result['scan_id'] ?? ''),
                'shieldx_log_scan_seen' => (string) (int) ($result['scanned_entries'] ?? 0),
                'shieldx_log_scan_found' => (string) (int) ($result['discovered_files'] ?? 0),
            ]);
        }

        $redirectUrl = $this->logRotationScanCompletionUrl($result);
        if ($ajax) {
            $this->sendProgressiveScanJson([
                'ok' => true,
                'status' => 'completed',
                'scan_id' => (string) ($result['scan_id'] ?? ''),
                'progress' => $this->logRotationScanProgressPayload($result),
                'redirect_url' => $redirectUrl,
            ]);
        }

        $this->redirectLogRotation([
            'shieldx_log_scan' => '1',
            'shieldx_log_scan_marked' => (string) (int) ($result['marked_files'] ?? 0),
            'shieldx_log_scan_total' => (string) (int) ($result['total_files'] ?? 0),
        ]);
    }

    public function cleanupTemporaryFiles(): void
    {
        $this->assertCanSave('shieldx_cleanup_temp_files');

        $result = LogRotator::fromWordPress()->cleanupTemporaryFiles(true);
        $query = [
            'shieldx_temp_cleanup_run' => '1',
            'shieldx_temp_cleanup_files' => (string) ((int) ($result['deleted_files'] ?? 0)),
            'shieldx_temp_cleanup_dirs' => (string) ((int) ($result['deleted_directories'] ?? 0)),
            'shieldx_temp_cleanup_bytes' => (string) ((int) ($result['deleted_bytes'] ?? 0)),
            'shieldx_temp_cleanup_failed' => (string) ((int) ($result['failed_files'] ?? 0) + (int) ($result['failed_directories'] ?? 0)),
        ];

        if (!empty($result['truncated'])) {
            $query['shieldx_temp_cleanup_truncated'] = '1';
        }

        $this->redirectLogRotation($query);
    }

    public function saveWafSettings(): void
    {
        $this->assertCanSave('shieldx_save_waf_settings');

        $wafSettings = WafSettings::fromWordPress();
        $current = $wafSettings->all();
        $allRules = $this->postedArray('all_rules');
        $enabledRules = array_fill_keys($this->postedArray('enabled_rules'), true);
        $disabledRules = [];

        if (empty($allRules)) {
            $disabledRules = isset($current['disabled_rules']) && is_array($current['disabled_rules']) ? array_map('strval', $current['disabled_rules']) : [];
        } else {
            foreach ($allRules as $ruleId) {
                if ($ruleId !== '' && !isset($enabledRules[$ruleId])) {
                    $disabledRules[] = $ruleId;
                }
            }
        }

        $temporaryBlocks = [];
        $removeTemporaryBlocks = array_fill_keys($this->postedArray('remove_temporary_blocks'), true);

        foreach ((array) ($current['temporary_blocks'] ?? []) as $block) {
            if (!is_array($block)) {
                continue;
            }

            $id = (string) ($block['id'] ?? '');
            if ($id !== '' && !isset($removeTemporaryBlocks[$id])) {
                $temporaryBlocks[] = $block;
            }
        }

        $temporaryType = $this->postedText('temporary_block_type', '');
        $temporaryValue = trim($this->postedText('temporary_block_value', ''));
        $temporaryMinutes = max(0, min(43200, $this->postedInt('temporary_block_minutes', 0)));

        if ($temporaryType !== '' && $temporaryValue !== '' && $temporaryMinutes > 0) {
            try {
                $temporaryId = bin2hex(random_bytes(8));
            } catch (\Throwable $error) {
                $temporaryId = '';
            }

            if ($temporaryId !== '') {
                $temporaryBlocks[] = [
                    'id' => $temporaryId,
                    'type' => $temporaryType,
                    'value' => $temporaryValue,
                    'expires_at' => time() + ($temporaryMinutes * 60),
                ];
            }
        }

        $ipAllowlist = $this->postedText('ip_allowlist', $this->lineListValue($current['ip_allowlist'] ?? []));
        $ipBlocklist = $this->postedText('ip_blocklist', $this->lineListValue($current['ip_blocklist'] ?? []));
        $userAgentBlocklist = $this->postedText('user_agent_blocklist', $this->lineListValue($current['user_agent_blocklist'] ?? []));
        $reputationAllowlist = $this->postedText('reputation_allowlist', $this->lineListValue($current['reputation_allowlist'] ?? []));
        $quickAllowIp = $this->postedText('quick_ip_allow', '');
        $quickBlockIp = $this->postedText('quick_ip_block', '');
        $quickBlockUa = $this->postedText('quick_ua_block', '');
        $wafAccessAction = $this->postedText('waf_access_action', '');
        $currentReputationFeedUrl = (string) ($current['reputation_feed_url'] ?? '');
        $reputationFeedUrl = $this->postedText('reputation_feed_url', $currentReputationFeedUrl);
        if (strlen($reputationFeedUrl) > WafSettings::MAX_REPUTATION_FEED_URL_LENGTH) {
            $this->redirectWafSettingsError(__('Custom reputation feed URL is too long.', 'xerotact-shield-x-security'));
        }
        if ($reputationFeedUrl !== '' && $reputationFeedUrl !== $currentReputationFeedUrl && !$this->postFlag('reputation_feed_confirm')) {
            $this->redirectWafSettingsError(__('Confirm the custom reputation feed URL before saving it.', 'xerotact-shield-x-security'));
        }

        $reputationSettings = [
            'reputation_enabled' => $this->postFlag('reputation_enabled'),
            'reputation_feed_url' => $reputationFeedUrl,
            'reputation_allowlist' => $reputationAllowlist,
            'reputation_ip_blocklist' => $reputationFeedUrl !== '' ? (array) ($current['reputation_ip_blocklist'] ?? []) : [],
            'reputation_hostname_blocklist' => $reputationFeedUrl !== '' ? (array) ($current['reputation_hostname_blocklist'] ?? []) : [],
            'reputation_user_agent_blocklist' => $reputationFeedUrl !== '' ? (array) ($current['reputation_user_agent_blocklist'] ?? []) : [],
            'reputation_last_refresh' => $reputationFeedUrl !== '' ? (int) ($current['reputation_last_refresh'] ?? 0) : 0,
            'reputation_last_attempt' => $reputationFeedUrl !== '' ? (int) ($current['reputation_last_attempt'] ?? 0) : 0,
            'reputation_last_status' => $reputationFeedUrl !== '' ? (string) ($current['reputation_last_status'] ?? '') : '',
            'reputation_last_error' => $reputationFeedUrl !== '' ? (string) ($current['reputation_last_error'] ?? '') : '',
        ];

        if ($reputationFeedUrl !== '' && $this->postFlag('refresh_reputation_feed')) {
            $queued = SchedulerBridge::active() && ReputationFeed::scheduleRefresh();
            if ($queued) {
                SchedulerBridge::refresh();
            }
            $reputationSettings['reputation_last_error'] = $queued
                ? __('Refresh queued.', 'xerotact-shield-x-security')
                : __('Install and enable SHIELD-X Scheduler to queue reputation feed refreshes.', 'xerotact-shield-x-security');
        }

        if ($quickAllowIp !== '') {
            $ipAllowlist = $this->appendLineListValue($ipAllowlist, $quickAllowIp);
        }

        $currentClient = $this->currentClientForWafPanel();

        if ($quickBlockIp !== '' && !$this->isCurrentWafClientIp($quickBlockIp, $currentClient)) {
            $ipBlocklist = $this->appendLineListValue($ipBlocklist, $quickBlockIp);
        }
        $ipBlocklist = $this->removeCurrentWafClientIpMatchesFromList($ipBlocklist, $currentClient);

        if ($quickBlockUa !== '' && !$this->isCurrentWafClientUserAgent($quickBlockUa, $currentClient)) {
            $userAgentBlocklist = $this->appendLineListValue($userAgentBlocklist, $quickBlockUa);
        }

        $this->applyWafAccessAction($wafAccessAction, $ipAllowlist, $ipBlocklist, $reputationAllowlist, $temporaryBlocks);
        $ipBlocklist = $this->removeCurrentWafClientIpMatchesFromList($ipBlocklist, $currentClient);
        $reputationSettings['reputation_allowlist'] = $reputationAllowlist;
        $postedRuleActions = $this->postedKeyedArray('rule_actions');

        $wafSettings->save($disabledRules, $this->postedArray('plugin_whitelist'), [
            'mode' => $this->postedText('waf_mode', (string) ($current['mode'] ?? 'log_only')),
            'temporary_rule_block_minutes' => max(1, min(43200, $this->postedInt('temporary_rule_block_minutes', (int) ($current['temporary_rule_block_minutes'] ?? WafSettings::DEFAULT_TEMPORARY_RULE_BLOCK_MINUTES)))),
            'max_body_inspection_bytes' => max(WafSettings::MIN_MAX_BODY_INSPECTION_BYTES, min(WafSettings::MAX_MAX_BODY_INSPECTION_BYTES, $this->postedInt('max_body_inspection_bytes', (int) ($current['max_body_inspection_bytes'] ?? WafSettings::DEFAULT_MAX_BODY_INSPECTION_BYTES)))),
            'anomaly_threshold_enabled' => $this->postFlag('anomaly_threshold_enabled'),
            'anomaly_challenge_threshold' => max(1, min(1000, $this->postedInt('anomaly_challenge_threshold', (int) ($current['anomaly_challenge_threshold'] ?? WafSettings::DEFAULT_ANOMALY_CHALLENGE_THRESHOLD)))),
            'anomaly_block_threshold' => max(1, min(1000, $this->postedInt('anomaly_block_threshold', (int) ($current['anomaly_block_threshold'] ?? WafSettings::DEFAULT_ANOMALY_BLOCK_THRESHOLD)))),
            'rule_actions' => empty($allRules) && empty($postedRuleActions) && isset($current['rule_actions']) && is_array($current['rule_actions'])
                ? $current['rule_actions']
                : $postedRuleActions,
            'ip_allowlist' => $ipAllowlist,
            'ip_blocklist' => $ipBlocklist,
            'country_blocklist' => $this->postedText('country_blocklist', $this->lineListValue($current['country_blocklist'] ?? [])),
            'user_agent_blocklist' => $userAgentBlocklist,
            'referrer_blocklist' => $this->postedText('referrer_blocklist', $this->lineListValue($current['referrer_blocklist'] ?? [])),
            'hostname_blocklist' => $this->postedText('hostname_blocklist', $this->lineListValue($current['hostname_blocklist'] ?? [])),
            'hostname_resolution_enabled' => $this->postFlag('hostname_resolution_enabled'),
            'trusted_proxy_header' => $this->postedText('trusted_proxy_header', (string) ($current['trusted_proxy_header'] ?? '')),
            'trusted_proxy_cidrs' => $this->postedText('trusted_proxy_cidrs', $this->lineListValue($current['trusted_proxy_cidrs'] ?? [])),
            'temporary_blocks' => $temporaryBlocks,
        ] + $reputationSettings);

        $this->redirectAfterSave('shield-x-waf');
    }

    public function wafAccessAction(): void
    {
        $this->assertCanSave('shieldx_waf_access_action');

        $wafSettings = WafSettings::fromWordPress();
        $current = $wafSettings->all();
        $ipAllowlist = $this->lineListValue($current['ip_allowlist'] ?? []);
        $ipBlocklist = $this->lineListValue($current['ip_blocklist'] ?? []);
        $reputationAllowlist = $this->lineListValue($current['reputation_allowlist'] ?? []);
        $temporaryBlocks = isset($current['temporary_blocks']) && is_array($current['temporary_blocks']) ? $current['temporary_blocks'] : [];

        $this->applyWafAccessAction(
            $this->postedText('waf_access_action', ''),
            $ipAllowlist,
            $ipBlocklist,
            $reputationAllowlist,
            $temporaryBlocks
        );

        $current['ip_allowlist'] = $ipAllowlist;
        $current['ip_blocklist'] = $ipBlocklist;
        $current['reputation_allowlist'] = $reputationAllowlist;
        $current['temporary_blocks'] = $temporaryBlocks;

        $wafSettings->save(
            isset($current['disabled_rules']) && is_array($current['disabled_rules']) ? $current['disabled_rules'] : [],
            isset($current['plugin_whitelist']) && is_array($current['plugin_whitelist']) ? $current['plugin_whitelist'] : [],
            $current
        );

        wp_safe_redirect(add_query_arg($this->wafAccessRedirectQuery(), admin_url('admin.php')));
        exit;
    }

    public function clearWafAccessTable(): void
    {
        $this->assertCanSave('shieldx_clear_waf_access_table');

        $this->dismissWafEvents($this->recentWafEventsForDismissal());

        $wafSettings = WafSettings::fromWordPress();
        $current = $wafSettings->all();
        $current['ip_allowlist'] = [];
        $current['ip_blocklist'] = [];
        $current['country_blocklist'] = [];
        $current['user_agent_blocklist'] = [];
        $current['referrer_blocklist'] = [];
        $current['hostname_blocklist'] = [];
        $current['reputation_allowlist'] = [];
        $current['reputation_ip_blocklist'] = [];
        $current['reputation_hostname_blocklist'] = [];
        $current['reputation_user_agent_blocklist'] = [];
        $current['temporary_blocks'] = [];

        $wafSettings->save(
            isset($current['disabled_rules']) && is_array($current['disabled_rules']) ? $current['disabled_rules'] : [],
            isset($current['plugin_whitelist']) && is_array($current['plugin_whitelist']) ? $current['plugin_whitelist'] : [],
            $current
        );

        wp_safe_redirect(add_query_arg([
            'page' => 'shield-x-waf-access',
            'shieldx_waf_access_cleared' => '1',
        ], admin_url('admin.php')));
        exit;
    }

    public function clearWafHits(): void
    {
        $this->assertCanSave('shieldx_clear_waf_hits');

        $this->dismissWafEvents($this->recentWafEventsForDismissal());

        wp_safe_redirect(add_query_arg([
            'page' => 'shield-x-waf',
            'shieldx_waf_hits_cleared' => '1',
        ], admin_url('admin.php')) . '#shieldx-waf-latest-hits');
        exit;
    }

    public function saveHumanChallengeSettings(): void
    {
        $this->assertCanSave('shieldx_save_human_challenge_settings');
        $this->saveHumanChallengeSettingsFromPost(['provider', 'waf']);

        $args = ['shieldx_saved' => '1'];
        if ($this->postFlag('human_challenge_generate_settings')) {
            try {
                HumanChallengeSettings::fromWordPress()->saveGeneratedStandaloneDefaults();
                $args['shieldx_cap_generated'] = '1';
            } catch (\RuntimeException $error) {
                $args['shieldx_cap_error'] = AdminFlashNotice::flag('shieldx_cap_error', $error->getMessage());
            }
        }

        NotificationManager::fromWordPress()->notify('system.human_challenge_changed', [
            'operation' => 'human_challenge_settings_saved',
        ], 'info');

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($args), admin_url('admin.php?page=shield-x-system')) . '#shieldx-human-challenge');
        exit;
    }

    public function toggleModule(): void
    {
        $this->assertCanSave('shieldx_toggle_module');

        $result = AddonRegistry::fromWordPress()->setModuleEnabled(
            $this->postedText('module_id', ''),
            $this->postedText('enabled', '0') === '1'
        );

        $this->redirectModuleResult($result);
    }

    public function activateLicense(): void
    {
        $this->assertCanSave('shieldx_activate_license');

        $result = LicenseManager::fromWordPress()->activate($this->postedText('license_key', ''));
        $this->redirectLicenseResult($result, 'shieldx_license_activated', $this->postedText('return_page', 'xerotact-shield-x-security'));
    }

    public function retryLicenseActivation(): void
    {
        $this->assertCanSave('shieldx_retry_license_activation');

        $result = LicenseManager::fromWordPress()->retryActivation();
        $this->redirectLicenseResult($result, 'shieldx_license_activated', $this->postedText('return_page', 'xerotact-shield-x-security'));
    }

    public function validateLicense(): void
    {
        $this->assertCanSave('shieldx_validate_license');

        $result = LicenseManager::fromWordPress()->validateForCurrentSession(true);
        $this->redirectLicenseResult($result, 'shieldx_license_checked', $this->postedText('return_page', 'xerotact-shield-x-security'));
    }

    public function deactivateLicense(): void
    {
        $this->assertCanSave('shieldx_deactivate_license');

        $result = LicenseManager::fromWordPress()->deactivate();
        $this->redirectLicenseResult($result, 'shieldx_license_deactivated', $this->postedText('return_page', 'xerotact-shield-x-security'));
    }

    public function applySetupMode(): void
    {
        $this->assertCanSave('shieldx_apply_setup_mode');

        $mode = SetupModeManager::sanitizeMode($this->postedText('setup_mode', SetupModeManager::MODE_NORMAL));
        $manager = new SetupModeManager($this->plugin);

        try {
            $state = $manager->apply($mode, [
                'create_restore_point' => $this->postFlag('create_restore_point'),
                'apply_lockdown' => $this->postFlag('apply_lockdown_now'),
            ]);
            $args = [
                'shieldx_setup_mode_applied' => $mode,
                'shieldx_setup_mode_warnings' => (string) count((array) ($state['warnings'] ?? [])),
            ];
            if (!empty($state['lockdown_applied'])) {
                $args['shieldx_lockdown_applied'] = '1';
            }
            NotificationManager::fromWordPress()->notify('system.setup_mode_applied', [
                'mode' => $mode,
                'warnings' => count((array) ($state['warnings'] ?? [])),
                'lockdown_applied' => !empty($state['lockdown_applied']) ? 'yes' : 'no',
            ], 'info');
        } catch (\Throwable $throwable) {
            $args = [
                'shieldx_setup_mode_error' => AdminFlashNotice::flag('shieldx_setup_mode_error', $throwable->getMessage()),
            ];
        }

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($args), admin_url('admin.php?page=shield-x-setup')) . '#shieldx-auto-setup-modes');
        exit;
    }

    public function saveUserProfilerSettings(): void
    {
        $this->assertCanSave('shieldx_save_user_profiler');

        UserProfiler::saveSettings($this->postFlag('user_profiler_enabled'));
        $this->redirectAfterSave('shield-x-activity');
    }

    /**
     * @param array<int,string> $sections
     */
    private function saveHumanChallengeSettingsFromPost(array $sections): void
    {
        $settings = HumanChallengeSettings::fromWordPress();
        $updates = [];

        if (in_array('provider', $sections, true)) {
            $updates['provider'] = $this->postedText('human_challenge_provider', 'none');
            $updates['endpoint_url'] = $this->postedText('human_challenge_endpoint_url', '');
            $updates['site_key'] = $this->postedText('human_challenge_site_key', '');
            $updates['timeout_seconds'] = $this->postedInt('human_challenge_timeout_seconds', 5);
            $updates['fail_behavior'] = $this->postedText('human_challenge_fail_behavior', 'fail_closed');
            $secret = $this->postedText('human_challenge_secret_key', '');
            if ($secret !== '') {
                $updates['secret_key'] = $secret;
            }
        }

        if (in_array('waf', $sections, true)) {
            $updates['waf_enabled'] = $this->postFlag('human_challenge_waf_enabled');
        }

        if (in_array('login', $sections, true)) {
            $updates['login_enabled'] = $this->postFlag('human_challenge_login_enabled');
            $updates['login_fullscreen_enabled'] = $this->postFlag('human_challenge_login_fullscreen_enabled');
            $updates['password_reset_enabled'] = $this->postFlag('human_challenge_password_reset_enabled');
        }

        if (!empty($updates)) {
            $settings->save($updates);
        }

        if (in_array('login', $sections, true) && (
            !empty($updates['login_enabled'])
            || !empty($updates['login_fullscreen_enabled'])
            || !empty($updates['password_reset_enabled'])
        )) {
            $settings = HumanChallengeSettings::fromWordPress();
            if (!$settings->isConfigured()) {
                try {
                    $settings->saveGeneratedStandaloneDefaults();
                } catch (\RuntimeException $error) {
                    unset($error);
                }
            }
        }
    }

    public function ajaxPrepareHardeningPreflight(): void
    {
        if (!self::currentUserCanManageShieldX()) {
            wp_send_json_error([
                'message' => __('You do not have permission to change SHIELD-X hardening settings.', 'xerotact-shield-x-security'),
            ], 403);
        }

        if (function_exists('check_ajax_referer')) {
            check_ajax_referer('shieldx_prepare_hardening_preflight');
        }

        $ajaxNonce = $this->postedText('_ajax_nonce', '');
        $reportJobId = $this->postedText('report_job_id', '');
        if ($reportJobId !== '') {
            $recorded = Hardening::recordBrowserHtaccessPreflightFailure(
                $reportJobId,
                [
                    'label' => $this->postedText('probe_label', ''),
                    'http_code' => (int) $this->postedText('probe_http_code', '0'),
                    'server' => $this->postedText('probe_server', ''),
                    'cf_ray' => $this->postedText('probe_cf_ray', ''),
                    'cf_cache_status' => $this->postedText('probe_cf_cache_status', ''),
                    'cf_mitigated' => $this->postedText('probe_cf_mitigated', ''),
                    'content_type' => $this->postedText('probe_content_type', ''),
                    'cloudflare' => $this->postedText('probe_cloudflare', '0'),
                ],
                'shieldx_prepare_hardening_preflight',
                $ajaxNonce
            );
            if (empty($recorded['ok'])) {
                wp_send_json_error($recorded, 400);
            }
            if ((string) ($recorded['status'] ?? '') === 'retry_ready') {
                wp_send_json_success($recorded);
            }

            $noticeMessage = (string) ($recorded['preflight']['status'] ?? '') === 'cdn_intercepted'
                ? __('The sandbox .htaccess file was created and verified on the host, but Cloudflare returned a challenge or block page. Any host-created sandbox .htaccess was preserved as htaccess.bak. No hardening files were changed.', 'xerotact-shield-x-security')
                : __('The sandbox .htaccess file was created and verified on the host, but the browser could not verify it. Any host-created sandbox .htaccess was preserved as htaccess.bak. No hardening files were changed.', 'xerotact-shield-x-security');
            AdminFlashNotice::store('shieldx_hardening_error', $noticeMessage);
            $recorded['notice'] = $noticeMessage;

            wp_send_json_success($recorded);
        }

        $cancelJobId = $this->postedText('cancel_job_id', '');
        if ($cancelJobId !== '') {
            $cancelled = Hardening::cancelBrowserHtaccessPreflight(
                $cancelJobId,
                'shieldx_prepare_hardening_preflight',
                $ajaxNonce
            );
            if (empty($cancelled['ok'])) {
                wp_send_json_error($cancelled, 400);
            }

            wp_send_json_success($cancelled);
        }

        $proposal = $this->postedHardeningProposal(HardeningSettings::fromWordPress());
        $prepared = Hardening::prepareBrowserHtaccessPreflight(
            $proposal['settings'],
            '',
            'shieldx_prepare_hardening_preflight',
            $ajaxNonce
        );
        if (empty($prepared['ok'])) {
            AdminFlashNotice::store(
                'shieldx_hardening_error',
                (string) ($prepared['message'] ?? __('SHIELD-X could not prepare the browser .htaccess test.', 'xerotact-shield-x-security'))
            );
            wp_send_json_error($prepared, 400);
        }

        wp_send_json_success($prepared);
    }

    public function saveHardeningSettings(): void
    {
        $this->assertCanSave('shieldx_save_hardening_settings');

        $hardeningSettings = HardeningSettings::fromWordPress();
        $proposal = $this->postedHardeningProposal($hardeningSettings);
        $proposedHardeningSettings = $proposal['settings'];
        $browserJobId = $this->postedText('shieldx_htaccess_browser_job', '');
        $rootPreflightRequester = null;
        if ($browserJobId !== '') {
            $browserPreflight = Hardening::consumeBrowserHtaccessPreflight(
                $browserJobId,
                $proposedHardeningSettings,
                'shieldx_save_hardening_settings'
            );
            if (empty($browserPreflight['ok'])) {
                AdminFlashNotice::store(
                    'shieldx_hardening_error',
                    (string) ($browserPreflight['message'] ?? __('The browser .htaccess verification could not be confirmed.', 'xerotact-shield-x-security'))
                );
                wp_safe_redirect(add_query_arg([
                    'shieldx_hardening_error' => '1',
                ], admin_url('admin.php?page=shield-x-hardening')));
                exit;
            }

            $rootPreflightRequester = static function (string $url, string $expectedToken): array {
                unset($url);

                return [
                    'ok' => true,
                    'status' => 'browser_passed',
                    'message' => 'The administrator browser confirmed the .htaccess sandbox token.',
                    'http_code' => 200,
                    'body' => $expectedToken,
                    'headers' => [],
                ];
            };
        }

        $this->createPreChangeRestorePoint('pre_hardening_save', [
            '.htaccess',
            'wp-config.php',
            'wp-content/uploads/.htaccess',
            'wp-content/cache/.htaccess',
            'wp-content/upgrade/.htaccess',
        ]);
        $fileEditResult = Hardening::syncFileEditConfig((bool) $proposal['disable_file_edit'], '', 'shieldx_save_hardening_settings');
        $webRulesResult = Hardening::syncWebServerRules($proposedHardeningSettings, '', '', $rootPreflightRequester, 'shieldx_save_hardening_settings');
        if (empty($fileEditResult['ok']) || empty($webRulesResult['ok'])) {
            $message = empty($fileEditResult['ok'])
                ? (string) ($fileEditResult['message'] ?? __('wp-config.php hardening could not be updated.', 'xerotact-shield-x-security'))
                : implode(' ', (array) ($webRulesResult['messages'] ?? []));
            AdminFlashNotice::store(
                'shieldx_hardening_error',
                $message !== ''
                    ? $message
                    : __('Hardening filesystem rules could not be updated.', 'xerotact-shield-x-security')
            );
            wp_safe_redirect(add_query_arg([
                'shieldx_hardening_error' => '1',
            ], admin_url('admin.php?page=shield-x-hardening')));
            exit;
        }

        $hardeningSettings->save(
            (bool) $proposal['hide_signature'],
            (bool) $proposal['admin_gate_enabled'],
            (string) $proposal['admin_gate_key'],
            (bool) $proposal['force_ssl_enabled'],
            (bool) $proposal['disable_file_edit'],
            (array) $proposal['response_headers'],
            $proposal['advanced']
        );

        NotificationManager::fromWordPress()->notify('hardening.hardening_applied', [
            'operation' => 'hardening_settings_saved',
        ], 'info');

        if ($hardeningSettings->adminGateEnabled()) {
            Hardening::setAccessCookie($hardeningSettings);
        } else {
            Hardening::clearAccessCookie();
        }

        $rateLimitSettings = RateLimitSettings::fromWordPress();
        $current = $rateLimitSettings->all();
        $rateLimitSettings->save([
            'request_rate_enabled' => $this->postFlag('request_rate_enabled'),
            'request_rate_mode' => $this->postedText('request_rate_mode', (string) $current['request_rate_mode']),
            'request_limit' => $this->postedInt('request_limit', (int) $current['request_limit']),
            'request_window_seconds' => $this->postedInt('request_window_seconds', (int) $current['request_window_seconds']),
            'burst_limit' => $this->postedInt('burst_limit', (int) $current['burst_limit']),
            'burst_window_seconds' => $this->postedInt('burst_window_seconds', (int) $current['burst_window_seconds']),
            'login_protection_enabled' => (bool) $current['login_protection_enabled'],
            'login_failure_limit' => (int) $current['login_failure_limit'],
            'login_window_seconds' => (int) $current['login_window_seconds'],
            'login_lockout_seconds' => (int) $current['login_lockout_seconds'],
            'endpoint_profiles' => $this->postedEndpointProfiles((array) ($current['endpoint_profiles'] ?? [])),
        ]);

        $this->redirectAfterSave('shield-x-hardening');
    }

    /**
     * @return array{
     *     settings:HardeningSettings,
     *     hide_signature:bool,
     *     admin_gate_enabled:bool,
     *     admin_gate_key:string,
     *     force_ssl_enabled:bool,
     *     disable_file_edit:bool,
     *     response_headers:array<string,bool>,
     *     advanced:array<string,mixed>
     * }
     */
    private function postedHardeningProposal(HardeningSettings $current): array
    {
        $hideSignature = $this->postFlag('hide_wordpress_signature');
        $forceSslEnabled = $this->postFlag('force_ssl_enabled');
        $disableFileEdit = $this->postFlag('disable_file_edit');
        $responseHeaders = HardeningSettings::responseHeadersFromSelection($this->postedArray('response_headers'));
        $adminGateEnabled = $current->adminGateEnabled();
        $adminGateKey = $current->adminGateKey();
        $advanced = [
            'block_xmlrpc' => $this->postFlag('block_xmlrpc'),
            'disable_pingbacks' => $this->postFlag('disable_pingbacks'),
            'disable_admin_scripts_concatenation' => $this->postFlag('disable_admin_scripts_concatenation'),
            'block_author_scans' => $this->postFlag('block_author_scans'),
            'generic_login_errors' => $this->postFlag('generic_login_errors'),
            'bot_protection_enabled' => $this->postFlag('bot_protection_enabled'),
            'direct_core_access_protection' => $this->postFlag('direct_core_access_protection'),
            'sensitive_file_protection' => $this->postFlag('sensitive_file_protection'),
            'directory_browsing_protection' => $this->postFlag('directory_browsing_protection'),
            'data_php_execution_protection' => $this->postFlag('data_php_execution_protection'),
            'admin_gate_strict_cookie' => $current->adminGateStrictCookie(),
        ];

        return [
            'settings' => new HardeningSettings(array_merge([
                'hide_wordpress_signature' => $hideSignature,
                'force_ssl_enabled' => $forceSslEnabled,
                'disable_file_edit' => $disableFileEdit,
                'response_headers' => $responseHeaders,
                'admin_gate_enabled' => $adminGateEnabled,
                'admin_gate_key' => $adminGateKey,
            ], $advanced)),
            'hide_signature' => $hideSignature,
            'admin_gate_enabled' => $adminGateEnabled,
            'admin_gate_key' => $adminGateKey,
            'force_ssl_enabled' => $forceSslEnabled,
            'disable_file_edit' => $disableFileEdit,
            'response_headers' => $responseHeaders,
            'advanced' => $advanced,
        ];
    }

    public function saveLockdownSettings(): void
    {
        $this->assertCanSave('shieldx_save_lockdown_settings');

        $policy = $this->postedLockdownPolicy();
        $action = $this->postedText('lockdown_submit', 'save');

        if ($action === 'disable') {
            $this->clearLockdownPreviewPolicy();
            $result = $this->plugin->revertLockdown(true);
            $this->redirectLockdown($this->lockdownResultRedirectQuery($result, 'shieldx_lockdown_reverted', 'Lockdown revert failed.'));
        }

        if ($action === 'force_revert') {
            $this->clearLockdownPreviewPolicy();
            $result = $this->plugin->forceRevertLockdownWithoutManifest(
                $this->postFlag('lockdown_force_revert_confirm'),
                'shieldx_save_lockdown_settings'
            );
            $this->redirectLockdown($this->lockdownResultRedirectQuery($result, 'shieldx_lockdown_force_reverted', 'Lockdown force revert failed.'));
        }

        if ($action === 'preview') {
            $this->storeLockdownPreviewPolicy($policy);
            $this->redirectLockdown(['shieldx_lockdown_preview' => '1']);
        }

        if ($action === 'permission_fix') {
            $ajax = $this->isProgressiveScanAjax();
            $availableScopes = [];
            foreach (LocalScanner::fromWordPress()->availableScopes(false) as $scope) {
                if (!is_array($scope) || !isset($scope['id'])) {
                    continue;
                }

                $scopeId = sanitize_key((string) $scope['id']);
                if ($scopeId !== '' && $scopeId !== 'database') {
                    $availableScopes[$scopeId] = true;
                }
            }

            $selectedScopes = [];
            foreach ($this->postedArray('permission_scopes') as $scopeId) {
                $scopeId = sanitize_key($scopeId);
                if (isset($availableScopes[$scopeId])) {
                    $selectedScopes[$scopeId] = true;
                }
            }

            if (empty($selectedScopes)) {
                if ($ajax) {
                    $this->sendProgressiveScanJson([
                        'ok' => false,
                        'status' => 'failed',
                        'error' => __('Select at least one WordPress area before running Permission Fix.', 'xerotact-shield-x-security'),
                        'redirect_url' => add_query_arg([
                            'shieldx_permission_error' => AdminFlashNotice::flag('shieldx_permission_error', __('Select at least one WordPress area before running Permission Fix.', 'xerotact-shield-x-security')),
                        ], admin_url('admin.php?page=shield-x-lockdown')) . '#shieldx-permission-fix',
                    ], 400);
                }

                $this->redirectLockdown([
                    'shieldx_permission_error' => __('Select at least one WordPress area before running Permission Fix.', 'xerotact-shield-x-security'),
                ]);
            }

            $job = $this->plugin->startProgressivePermissionFix(array_keys($selectedScopes));
            $jobId = (string) ($job['job_id'] ?? '');
            if (empty($job['ok']) || $jobId === '') {
                $message = (string) ($job['message'] ?? __('Permission Fix could not start.', 'xerotact-shield-x-security'));
                if ($ajax) {
                    $this->sendProgressiveScanJson([
                        'ok' => false,
                        'status' => 'failed',
                        'error' => $message,
                        'progress' => $this->permissionFixProgressPayload($job),
                        'redirect_url' => add_query_arg([
                            'shieldx_permission_error' => AdminFlashNotice::flag('shieldx_permission_error', $message),
                        ], admin_url('admin.php?page=shield-x-lockdown')) . '#shieldx-permission-fix',
                    ], 400);
                }

                $this->redirectLockdown(['shieldx_permission_error' => $message]);
            }

            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => true,
                    'status' => (string) ($job['status'] ?? 'running'),
                    'scan_id' => $jobId,
                    'progress' => $this->permissionFixProgressPayload($job),
                    'next_url' => $this->progressivePermissionFixUrl($jobId, true),
                ]);
            }

            wp_safe_redirect($this->progressivePermissionFixUrl($jobId));
            exit;
        }

        if ($action === 'enable') {
            $this->clearLockdownPreviewPolicy();
            $result = $this->plugin->applyLockdown($policy, $this->postFlag('lockdown_confirm'), true, 'shieldx_save_lockdown_settings');
            $this->redirectLockdown($this->lockdownResultRedirectQuery($result, 'shieldx_lockdown_applied', 'Lockdown apply failed.'));
        }

        if ($action === 'reapply') {
            $this->clearLockdownPreviewPolicy();
            $result = $this->plugin->reapplyLockdown(true);
            $this->redirectLockdown($this->lockdownResultRedirectQuery($result, 'shieldx_lockdown_applied', 'Lockdown reapply failed.'));
        }

        if ($action === 'save') {
            $this->clearLockdownPreviewPolicy();
            $this->plugin->saveLockdownPolicy($policy);
        }

        $this->redirectAfterSave('shield-x-lockdown');
    }

    public function continuePermissionFix(): void
    {
        $this->denyUnauthorizedProgressiveRequest();
        $ajax = $this->isProgressiveScanAjax();
        $jobId = strtolower($this->progressiveScanQueryText('permission_job_id'));
        if ($jobId === '' || preg_match('/^pf-[a-f0-9]{16,40}$/', $jobId) !== 1) {
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'error' => __('The Permission Fix session is invalid.', 'xerotact-shield-x-security'),
                    'redirect_url' => admin_url('admin.php?page=shield-x-lockdown') . '#shieldx-permission-fix',
                ], 400);
            }

            wp_die(esc_html__('The Permission Fix session is invalid.', 'xerotact-shield-x-security'));
        }

        $nonceAction = 'shieldx_continue_permission_fix_' . $jobId;
        $nonce = $this->progressiveScanQueryText('_wpnonce');
        if (!$this->canSaveWithNonce($nonceAction, $nonce)) {
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'error' => __('The Permission Fix session security check failed.', 'xerotact-shield-x-security'),
                    'redirect_url' => admin_url('admin.php?page=shield-x-lockdown') . '#shieldx-permission-fix',
                ], 403);
            }

            $this->assertCanSaveWithNonce($nonceAction, $nonce);
        }

        $this->prepareLongAdminAction();
        $result = $this->plugin->runProgressivePermissionFixBatch($jobId);
        $status = (string) ($result['status'] ?? 'failed');

        if ($status === 'completed') {
            $redirectUrl = $this->permissionFixCompletionUrl($result);
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => true,
                    'status' => 'completed',
                    'scan_id' => $jobId,
                    'progress' => $this->permissionFixProgressPayload($result),
                    'redirect_url' => $redirectUrl,
                ]);
            }

            wp_safe_redirect($redirectUrl);
            exit;
        }

        if ($status === 'failed') {
            $message = (string) ($result['message'] ?? __('Permission Fix failed.', 'xerotact-shield-x-security'));
            $redirectUrl = add_query_arg([
                'shieldx_permission_error' => AdminFlashNotice::flag('shieldx_permission_error', $message),
            ], admin_url('admin.php?page=shield-x-lockdown')) . '#shieldx-permission-fix';

            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'scan_id' => $jobId,
                    'error' => $message,
                    'progress' => $this->permissionFixProgressPayload($result),
                    'redirect_url' => $redirectUrl,
                ], 400);
            }

            wp_safe_redirect($redirectUrl);
            exit;
        }

        $nextUrl = $this->progressivePermissionFixUrl($jobId, $ajax);
        $progress = $this->permissionFixProgressPayload($result);
        if ($ajax) {
            $this->sendProgressiveScanJson([
                'ok' => true,
                'status' => 'running',
                'scan_id' => $jobId,
                'progress' => $progress,
                'next_url' => $nextUrl,
            ]);
        }

        $this->renderProgressiveScanPage($progress, $nextUrl);
        exit;
    }

    public function saveScanSettings(): void
    {
        $this->assertCanSave('shieldx_save_scan_settings');

        $settings = ScheduledScanSettings::fromWordPress();
        $scanner = LocalScanner::fromWordPress();
        $type = ScheduledScanSettings::sanitizeType($this->postedText('scheduled_scan_type', ScheduledScanSettings::TYPE_FILE));
        $current = $settings->all();
        $sections = isset($current['sections']) && is_array($current['sections']) ? $current['sections'] : [];
        $currentSection = isset($sections[$type]) && is_array($sections[$type]) ? $sections[$type] : $settings->section($type);
        $scopes = [];

        if ($type === ScheduledScanSettings::TYPE_FILE) {
            $scopes = $scanner->sanitizeScopes($this->postedArray('scheduled_scan_scopes'));
        } elseif ($type === ScheduledScanSettings::TYPE_DATABASE) {
            $scopes = ['database'];
        }

        $externalToken = $this->postFlag('regenerate_external_token') ? '' : (string) ($currentSection['external_token'] ?? '');

        $sections[$type] = [
            'type' => $type,
            'enabled' => $this->postFlag('scheduled_scan_enabled'),
            'recurrence' => $this->postedText('scheduled_scan_recurrence', (string) ($currentSection['recurrence'] ?? 'daily')),
            'scopes' => $scopes,
            'max_files' => $this->postedInt('scheduled_scan_max_files', (int) ($currentSection['max_files'] ?? LocalScanner::DEFAULT_MAX_FILES)),
            'database_depth' => $type === ScheduledScanSettings::TYPE_DATABASE && $this->postedText('scheduled_database_depth', 'quick') === 'deep' ? 'deep' : 'quick',
            'email_reports_enabled' => $this->postFlag('email_reports_enabled'),
            'alert_on_issues' => $this->postFlag('alert_on_issues'),
            'alert_on_failure' => $this->postFlag('alert_on_failure'),
            'external_runner_enabled' => $this->postFlag('external_runner_enabled'),
            'external_token' => $externalToken,
            'recipients' => $this->postedText('scan_report_recipients', $this->lineListValue($currentSection['recipients'] ?? [])),
        ];

        $settings->save(['sections' => $sections]);

        SchedulerBridge::refresh();
        wp_safe_redirect(add_query_arg([
            'shieldx_schedule_saved' => rawurlencode($type),
        ], admin_url('admin.php?page=shield-x-scan')) . '#' . $this->scanAnchorForType($type));
        exit;
    }

    public function saveBackupSettings(): void
    {
        $this->assertCanSave('shieldx_save_backup_settings');

        $settings = ScheduledBackupSettings::fromWordPress();
        $current = $settings->all();
        $externalToken = $this->postFlag('regenerate_external_token') ? '' : (string) ($current['external_token'] ?? '');

        $settings->save([
            'enabled' => $this->postFlag('scheduled_backup_enabled'),
            'recurrence' => $this->postedText('scheduled_backup_recurrence', (string) ($current['recurrence'] ?? 'daily')),
            'max_rows_per_table' => $this->postedInt('scheduled_backup_max_rows_per_table', (int) ($current['max_rows_per_table'] ?? ScheduledBackupSettings::DEFAULT_MAX_ROWS_PER_TABLE)),
            'email_reports_enabled' => $this->postFlag('email_reports_enabled'),
            'alert_on_warning' => $this->postFlag('alert_on_warning'),
            'alert_on_failure' => $this->postFlag('alert_on_failure'),
            'external_runner_enabled' => $this->postFlag('external_runner_enabled'),
            'external_token' => $externalToken,
            'recipients' => $this->postedText('backup_report_recipients', $this->lineListValue($current['recipients'] ?? [])),
        ]);

        SchedulerBridge::refresh();
        wp_safe_redirect(add_query_arg([
            'shieldx_backup_schedule_saved' => '1',
        ], admin_url('admin.php?page=shield-x-backup')) . '#shieldx-database-backups');
        exit;
    }

    public function saveFileTypeSettings(): void
    {
        $this->assertCanSave('shieldx_save_file_type_settings');

        $settings = FileTypeSettings::fromWordPress();
        $settings->save([
            FileTypeSettings::CONTEXT_MALWARE => $this->postedArray('malware_file_types'),
            FileTypeSettings::CONTEXT_BACKUP => $this->postedArray('backup_file_types'),
        ]);

        wp_safe_redirect(add_query_arg([
            'shieldx_file_types_saved' => '1',
        ], admin_url('admin.php?page=shield-x-system')) . '#shieldx-file-type-policy');
        exit;
    }

    public function saveBackupStorage(): void
    {
        $this->assertCanSave('shieldx_save_backup_storage');

        $result = BackupStorageManager::fromWordPress()->saveMode($this->postedText('backup_storage_mode', BackupStorageSettings::MODE_LOCAL));
        $query = empty($result['ok'])
            ? ['shieldx_google_drive_error' => AdminFlashNotice::flag('shieldx_google_drive_error', (string) ($result['message'] ?? __('Connect Google Drive before selecting Google storage.', 'xerotact-shield-x-security')))]
            : ['shieldx_backup_storage_saved' => '1'];

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-backup')) . '#shieldx-backup-storage');
        exit;
    }

    public function startGoogleDriveAuth(): void
    {
        $this->assertCanSave('shieldx_google_drive_auth_start');

        $result = BackupStorageManager::fromWordPress()->startGoogleAuth();
        $query = empty($result['ok'])
            ? ['shieldx_google_drive_error' => AdminFlashNotice::flag('shieldx_google_drive_error', (string) ($result['message'] ?? __('Google Drive authorization could not start.', 'xerotact-shield-x-security')))]
            : ['shieldx_google_drive_auth_started' => '1'];

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-backup')) . '#shieldx-backup-storage');
        exit;
    }

    public function saveGoogleDriveOAuthApp(): void
    {
        $this->assertCanSave('shieldx_google_drive_oauth_save');

        $settings = GoogleDriveSettings::fromWordPress();
        if (!GoogleDriveSettings::secretStorageReady()) {
            wp_safe_redirect(add_query_arg([
                'shieldx_google_drive_error' => AdminFlashNotice::flag('shieldx_google_drive_error', __('OpenSSL and WordPress salts are required before SHIELD-X can store Google OAuth credentials locally.', 'xerotact-shield-x-security')),
            ], admin_url('admin.php?page=shield-x-backup')) . '#shieldx-backup-storage');
            exit;
        }

        $saved = $settings->saveOAuthCredentials(
            $this->postedText('google_drive_client_id', ''),
            $this->postedText('google_drive_client_secret', '')
        );

        $query = $saved
            ? ['shieldx_google_drive_oauth_saved' => '1']
            : ['shieldx_google_drive_error' => AdminFlashNotice::flag('shieldx_google_drive_error', __('Enter both the Google OAuth client ID and client secret.', 'xerotact-shield-x-security'))];

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-backup')) . '#shieldx-backup-storage');
        exit;
    }

    public function completeGoogleDriveAuth(): void
    {
        $this->assertCanSave('shieldx_google_drive_auth_complete');

        $result = BackupStorageManager::fromWordPress()->completeGoogleAuth();
        $query = empty($result['ok'])
            ? ['shieldx_google_drive_error' => AdminFlashNotice::flag('shieldx_google_drive_error', (string) ($result['message'] ?? __('Google Drive authorization is not complete yet.', 'xerotact-shield-x-security')))]
            : ['shieldx_google_drive_connected' => '1'];

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-backup')) . '#shieldx-backup-storage');
        exit;
    }

    public function disconnectGoogleDrive(): void
    {
        $this->assertCanSave('shieldx_google_drive_disconnect');

        BackupStorageManager::fromWordPress()->disconnectGoogle();

        wp_safe_redirect(add_query_arg([
            'shieldx_google_drive_disconnected' => '1',
        ], admin_url('admin.php?page=shield-x-backup')) . '#shieldx-backup-storage');
        exit;
    }

    public function testGoogleDriveConnection(): void
    {
        $this->assertCanSave('shieldx_google_drive_test');

        $result = BackupStorageManager::fromWordPress()->testGoogleConnection();
        $query = empty($result['ok'])
            ? ['shieldx_google_drive_error' => AdminFlashNotice::flag('shieldx_google_drive_error', (string) ($result['message'] ?? __('Google Drive connection test failed.', 'xerotact-shield-x-security')))]
            : ['shieldx_google_drive_tested' => '1'];

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-backup')) . '#shieldx-backup-storage');
        exit;
    }

    public function saveRestorePointSchedule(): void
    {
        $this->assertCanSave('shieldx_save_restore_point_schedule');

        $settings = ScheduledRestorePointSettings::fromWordPress();
        $current = $settings->all();
        $externalToken = $this->postFlag('regenerate_external_token') ? '' : (string) ($current['external_token'] ?? '');

        $settings->save([
            'enabled' => $this->postFlag('scheduled_restore_point_enabled'),
            'recurrence' => $this->postedText('scheduled_restore_point_recurrence', (string) ($current['recurrence'] ?? 'daily')),
            'include_database' => $this->postFlag('scheduled_restore_point_include_database'),
            'email_reports_enabled' => $this->postFlag('email_reports_enabled'),
            'alert_on_warning' => $this->postFlag('alert_on_warning'),
            'alert_on_failure' => $this->postFlag('alert_on_failure'),
            'external_runner_enabled' => $this->postFlag('external_runner_enabled'),
            'external_token' => $externalToken,
            'recipients' => $this->postedText('restore_point_report_recipients', $this->lineListValue($current['recipients'] ?? [])),
        ]);

        SchedulerBridge::refresh();
        wp_safe_redirect(add_query_arg([
            'shieldx_restore_point_schedule_saved' => '1',
        ], admin_url('admin.php?page=shield-x-backup')) . '#shieldx-restore-points');
        exit;
    }

    public function runScan(): void
    {
        $this->assertCanSave('shieldx_run_scan');
        $this->prepareLongAdminAction();

        $ajax = $this->isProgressiveScanAjax();
        $scanner = LocalScanner::fromWordPress();
        $scopes = $scanner->sanitizeScopes($this->postedArray('scan_scopes'));
        $redirectPage = sanitize_key($this->postedText('redirect_page', 'shield-x-scan'));
        if (!in_array($redirectPage, ['shield-x-scan', 'shield-x-backup'], true)) {
            $redirectPage = 'shield-x-scan';
        }
        $scanMode = sanitize_key($this->postedText('scan_mode', ''));
        $databaseDepth = sanitize_key($this->postedText('database_depth', 'quick'));
        $databaseDepth = $databaseDepth === 'deep' ? 'deep' : 'quick';
        $precheckQueue = $redirectPage !== 'shield-x-backup' && $scanMode !== 'shadow';

        $job = $scanner->startProgressiveScan(LocalScanner::DEFAULT_MAX_FILES, $scopes, $redirectPage, $precheckQueue, $scanMode, $databaseDepth);
        $scanId = (string) ($job['scan_id'] ?? '');

        if ($ajax) {
            $this->sendProgressiveScanJson([
                'ok' => true,
                'status' => (string) ($job['status'] ?? 'running'),
                'scan_id' => $scanId,
                'progress' => $this->progressiveScanPayload($job),
                'next_url' => $this->progressiveScanUrl($scanId, true),
            ]);
        }

        wp_safe_redirect($this->progressiveScanUrl($scanId));
        exit;
    }

    public function continueScan(): void
    {
        $this->denyUnauthorizedProgressiveRequest();
        $ajax = $this->isProgressiveScanAjax();
        $scanId = strtolower($this->progressiveScanQueryText('scan_id'));
        if ($scanId === '' || preg_match('/^[a-f0-9]{32}$/', $scanId) !== 1) {
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'error' => __('The SHIELD-X scan session is invalid.', 'xerotact-shield-x-security'),
                    'redirect_url' => admin_url('admin.php?page=shield-x-scan'),
                ], 400);
            }

            wp_die(esc_html__('The SHIELD-X scan session is invalid.', 'xerotact-shield-x-security'));
        }

        $nonceAction = 'shieldx_continue_scan_' . $scanId;
        $nonce = $this->progressiveScanQueryText('_wpnonce');
        if (!$this->canSaveWithNonce($nonceAction, $nonce)) {
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'error' => __('The SHIELD-X scan session security check failed.', 'xerotact-shield-x-security'),
                    'redirect_url' => admin_url('admin.php?page=shield-x-scan'),
                ], 403);
            }

            $this->assertCanSaveWithNonce($nonceAction, $nonce);
        }

        $this->prepareLongAdminAction();
        $result = LocalScanner::fromWordPress()->runProgressiveScanBatch($scanId);
        $status = (string) ($result['status'] ?? 'failed');

        if ($status === 'completed') {
            $redirectUrl = $this->progressiveScanCompletionUrl($result, $scanId);
            $this->notifyManualScannerResult($result, $scanId);

            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => true,
                    'status' => 'completed',
                    'scan_id' => $scanId,
                    'progress' => $this->progressiveScanPayload($result),
                    'redirect_url' => $redirectUrl,
                ]);
            }

            wp_safe_redirect($redirectUrl);
            exit;
        }

        if ($status === 'failed') {
            $this->notifyManualScannerFailure($result, $scanId, (string) ($result['error'] ?? __('Scan failed.', 'xerotact-shield-x-security')));
            $redirectUrl = add_query_arg([
                'shieldx_scan_action_error' => AdminFlashNotice::flag('shieldx_scan_action_error', (string) ($result['error'] ?? __('Scan failed.', 'xerotact-shield-x-security'))),
            ], admin_url('admin.php?page=shield-x-scan'));

            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'scan_id' => $scanId,
                    'error' => (string) ($result['error'] ?? __('Scan failed.', 'xerotact-shield-x-security')),
                    'progress' => $this->progressiveScanPayload($result),
                    'redirect_url' => $redirectUrl,
                ]);
            }

            wp_safe_redirect($redirectUrl);
            exit;
        }

        $nextUrl = $this->progressiveScanUrl($scanId, $ajax);

        if ($ajax) {
            $this->sendProgressiveScanJson([
                'ok' => true,
                'status' => 'running',
                'scan_id' => $scanId,
                'progress' => $this->progressiveScanPayload($result),
                'next_url' => $nextUrl,
            ]);
        }

        $this->renderProgressiveScanPage($result, $nextUrl);
        exit;
    }

    private function progressiveScanUrl(string $scanId, bool $ajax = false): string
    {
        $query = [
            'action' => 'shieldx_continue_scan',
            'scan_id' => $scanId,
            '_wpnonce' => wp_create_nonce('shieldx_continue_scan_' . $scanId),
        ];

        if ($ajax) {
            $query['shieldx_ajax_progress'] = '1';
        }

        return add_query_arg($query, admin_url('admin-post.php'));
    }

    private function progressivePermissionFixUrl(string $jobId, bool $ajax = false): string
    {
        $query = [
            'action' => 'shieldx_continue_permission_fix',
            'permission_job_id' => $jobId,
            '_wpnonce' => wp_create_nonce('shieldx_continue_permission_fix_' . $jobId),
        ];

        if ($ajax) {
            $query['shieldx_ajax_progress'] = '1';
        }

        return add_query_arg($query, admin_url('admin-post.php'));
    }

    /**
     * @param array<string,mixed> $result
     */
    private function permissionFixCompletionUrl(array $result): string
    {
        $failedCount = isset($result['failed_total'])
            ? (int) $result['failed_total']
            : count((array) ($result['failed'] ?? []));
        $query = [
            'shieldx_permission_fixed' => '1',
            'shieldx_permission_files' => (string) (int) ($result['files'] ?? 0),
            'shieldx_permission_dirs' => (string) (int) ($result['directories'] ?? 0),
            'shieldx_permission_unchanged' => (string) (int) ($result['unchanged'] ?? 0),
            'shieldx_permission_skipped' => (string) (int) ($result['skipped'] ?? 0),
            'shieldx_permission_failed' => (string) $failedCount,
        ];

        if (empty($result['ok'])) {
            $query['shieldx_permission_error'] = (string) ($result['message'] ?? __('Some permissions could not be fixed.', 'xerotact-shield-x-security'));
        }

        if (!empty($result['failed']) && is_array($result['failed'])) {
            $reportId = $this->savePermissionFixReport([
                'created_at' => gmdate('Y-m-d H:i:s'),
                'files' => (int) ($result['files'] ?? 0),
                'directories' => (int) ($result['directories'] ?? 0),
                'unchanged' => (int) ($result['unchanged'] ?? 0),
                'skipped' => (int) ($result['skipped'] ?? 0),
                'failed_total' => $failedCount,
                'failed' => $result['failed'],
                'scopes' => isset($result['scopes']) && is_array($result['scopes']) ? array_values(array_map('strval', $result['scopes'])) : [],
            ]);
            if ($reportId !== '') {
                $query['shieldx_permission_report'] = $reportId;
            }
        }

        return add_query_arg(
            $this->protectRedirectQuery($query),
            admin_url('admin.php?page=shield-x-lockdown')
        ) . '#shieldx-permission-fix';
    }

    /**
     * @param array<string,mixed> $progress
     * @return array<string,mixed>
     */
    private function permissionFixProgressPayload(array $progress): array
    {
        $payload = $this->progressiveScanPayload($progress);
        $status = (string) ($progress['status'] ?? 'running');
        $paths = max(0, (int) ($progress['files_processed'] ?? 0));
        $batches = max(0, (int) ($progress['batches'] ?? 0));
        $scopeId = (string) ($progress['current_scope'] ?? '');
        $scopeLabel = $scopeId !== '' ? $this->permissionFixScopeLabel($scopeId) : __('Selected WordPress areas', 'xerotact-shield-x-security');
        $scopeIndex = max(0, (int) ($progress['current_scope_index'] ?? 0));
        $scopeCount = max(0, (int) ($progress['scope_count'] ?? 0));
        $changedFiles = max(0, (int) ($progress['files'] ?? 0));
        $changedDirs = max(0, (int) ($progress['directories'] ?? 0));
        $failed = isset($progress['failed_total']) ? max(0, (int) $progress['failed_total']) : count((array) ($progress['failed'] ?? []));

        $payload['title_label'] = $status === 'completed'
            ? __('Permission Fix complete', 'xerotact-shield-x-security')
            : __('Permission Fix in progress', 'xerotact-shield-x-security');
        $payload['intro_label'] = $status === 'completed'
            ? __('Selected permissions were checked. Review the summary and any host-owned paths below.', 'xerotact-shield-x-security')
            : __('SHIELD-X is normalizing selected WordPress paths in small batches.', 'xerotact-shield-x-security');
        $payload['percent_label'] = __('Areas', 'xerotact-shield-x-security');
        $payload['files_metric_label'] = __('Paths', 'xerotact-shield-x-security');
        $payload['batches_metric_label'] = __('Batches', 'xerotact-shield-x-security');
        $payload['files_label'] = sprintf(
            /* translators: %d: number of filesystem paths checked by Permission Fix. */
            _n('%d path checked', '%d paths checked', $paths, 'xerotact-shield-x-security'),
            $paths
        );
        $payload['batches_label'] = (string) $batches;

        if ($status === 'completed') {
            $payload['scope_label'] = sprintf(
                /* translators: 1: changed files, 2: changed folders, 3: failed paths. */
                __('Updated %1$d files and %2$d folders. %3$d failed.', 'xerotact-shield-x-security'),
                $changedFiles,
                $changedDirs,
                $failed
            );
            return $payload;
        }

        if ($scopeId !== '' && $scopeCount > 0) {
            $payload['scope_label'] = sprintf(
                /* translators: 1: current Permission Fix area label, 2: current area index, 3: total selected areas. */
                __('Checking %1$s (%2$d of %3$d).', 'xerotact-shield-x-security'),
                $scopeLabel,
                min($scopeCount, $scopeIndex + 1),
                $scopeCount
            );
        } else {
            $payload['scope_label'] = __('Preparing selected Permission Fix areas.', 'xerotact-shield-x-security');
        }

        return $payload;
    }

    private function permissionFixScopeLabel(string $scopeId): string
    {
        foreach (LocalScanner::fromWordPress()->availableScopes(false) as $scope) {
            if (is_array($scope) && (string) ($scope['id'] ?? '') === $scopeId) {
                return (string) ($scope['label'] ?? $scopeId);
            }
        }

        return $this->formatLabel($scopeId);
    }

    private function progressiveBackupUrl(string $backupId, bool $ajax = false): string
    {
        $query = [
            'action' => 'shieldx_continue_backup',
            'backup_id' => $backupId,
            '_wpnonce' => wp_create_nonce('shieldx_continue_backup_' . $backupId),
        ];

        if ($ajax) {
            $query['shieldx_ajax_progress'] = '1';
        }

        return add_query_arg($query, admin_url('admin-post.php'));
    }

    private function progressiveRestorePointUploadUrl(string $restoreId, bool $ajax = false): string
    {
        $query = [
            'action' => 'shieldx_upload_restore_point_google',
            'restore_id' => $restoreId,
            '_wpnonce' => wp_create_nonce('shieldx_upload_restore_point_google_' . $restoreId),
        ];

        if ($ajax) {
            $query['shieldx_ajax_progress'] = '1';
        }

        return add_query_arg($query, admin_url('admin-post.php'));
    }

    private function progressivePluginPackagesUrl(string $jobId, bool $ajax = false): string
    {
        $query = [
            'action' => 'shieldx_continue_plugin_packages',
            'plugin_job_id' => $jobId,
            '_wpnonce' => wp_create_nonce('shieldx_continue_plugin_packages_' . $jobId),
        ];

        if ($ajax) {
            $query['shieldx_ajax_progress'] = '1';
        }

        return add_query_arg($query, admin_url('admin-post.php'));
    }

    private function progressiveCoreReleaseUrl(string $jobId, bool $ajax = false): string
    {
        $query = [
            'action' => 'shieldx_continue_core_release',
            'core_job_id' => $jobId,
            '_wpnonce' => wp_create_nonce('shieldx_continue_core_release_' . $jobId),
        ];

        if ($ajax) {
            $query['shieldx_ajax_progress'] = '1';
        }

        return add_query_arg($query, admin_url('admin-post.php'));
    }

    /**
     * @param array<string,mixed> $result
     */
    private function progressiveBackupCompletionUrl(array $result, string $backupId): string
    {
        if (strpos($backupId, 'restore-') === 0) {
            return add_query_arg([
                'shieldx_restore_created' => (string) ($result['restore_id'] ?? $backupId),
            ], admin_url('admin.php?page=shield-x-backup')) . '#shieldx-restore-points';
        }

        return add_query_arg([
            'shieldx_db_backup_created' => (string) ($result['backup_id'] ?? $backupId),
        ], admin_url('admin.php?page=shield-x-backup'));
    }

    /**
     * @param array<string,mixed> $result
     */
    private function progressiveScanCompletionUrl(array $result, string $scanId): string
    {
        $scanResult = isset($result['result']) && is_array($result['result']) ? $result['result'] : [];
        $redirectPage = sanitize_key((string) ($result['redirect_page'] ?? 'shield-x-scan'));
        if (!in_array($redirectPage, ['shield-x-scan', 'shield-x-backup'], true)) {
            $redirectPage = 'shield-x-scan';
        }

        $query = [
            'shieldx_scopes' => (string) ($result['scope'] ?? ($scanResult['scope'] ?? '')),
        ];

        if ($redirectPage === 'shield-x-backup') {
            $shadowItems = isset($scanResult['shadow_changed_items']) && is_array($scanResult['shadow_changed_items']) ? $scanResult['shadow_changed_items'] : [];
            $query['shieldx_shadow_scanned'] = '1';
            $query['shieldx_shadow_scan'] = $scanId;
            $query['shieldx_shadow_changes'] = (string) count($shadowItems);
        } else {
            $query['shieldx_scanned'] = '1';
        }

        return add_query_arg($query, admin_url('admin.php?page=' . $redirectPage)) . ($redirectPage === 'shield-x-backup' ? '#shieldx-file-shadow-copies' : '');
    }

    /**
     * @param array<string,mixed> $result
     */
    private function notifyManualScannerResult(array $result, string $scanId): void
    {
        $scanResult = isset($result['result']) && is_array($result['result']) ? $result['result'] : $result;
        $context = $this->manualScannerNotificationContext($scanResult, $scanId);
        NotificationManager::fromWordPress()->notify('scanner.scan_completed', $context, 'info');

        $shadowItems = isset($scanResult['shadow_changed_items']) && is_array($scanResult['shadow_changed_items'])
            ? $scanResult['shadow_changed_items']
            : [];
        if (!empty($shadowItems)) {
            NotificationManager::fromWordPress()->notify('backup.shadow_changes', array_merge($context, [
                'operation' => 'shadow_scan',
                'changed_files' => count($shadowItems),
            ]), 'warning');
        }

        $issuesFound = (int) ($context['issues_found'] ?? 0);
        if ($issuesFound <= 0) {
            return;
        }

        NotificationManager::fromWordPress()->notify($this->manualScannerFindingEventKey($context), $context, 'warning');
    }

    /**
     * @param array<string,mixed> $result
     */
    private function notifyManualScannerFailure(array $result, string $scanId, string $message): void
    {
        $scanResult = isset($result['result']) && is_array($result['result']) ? $result['result'] : $result;
        $context = $this->manualScannerNotificationContext($scanResult, $scanId);
        $context['status'] = 'failed';
        $context['error'] = $message;

        NotificationManager::fromWordPress()->notify('scanner.scan_failed', $context, 'warning');
    }

    /**
     * @param array<string,mixed> $scanResult
     * @return array<string,mixed>
     */
    private function manualScannerNotificationContext(array $scanResult, string $scanId): array
    {
        $scanType = (string) ($scanResult['scan_type'] ?? '');
        if ($scanType === '') {
            if ((int) ($scanResult['advisory_hits'] ?? 0) > 0 || isset($scanResult['software_advisory_hits'])) {
                $scanType = 'vulnerability';
            } elseif (!empty($scanResult['database_selected']) || (string) ($scanResult['scope'] ?? '') === 'database') {
                $scanType = 'database';
            } else {
                $scanType = 'file';
            }
        }

        return [
            'operation' => 'manual_scan',
            'scan_id' => (string) ($scanResult['scan_id'] ?? $scanId),
            'scan_type' => $scanType,
            'severity' => $this->manualScannerMaxSeverity(isset($scanResult['issues']) && is_array($scanResult['issues']) ? $scanResult['issues'] : []),
            'status' => (string) ($scanResult['status'] ?? 'completed'),
            'scope' => (string) ($scanResult['scope'] ?? ''),
            'scope_label' => (string) ($scanResult['scope_label'] ?? ''),
            'files_total' => (int) ($scanResult['files_total'] ?? 0),
            'files_scanned' => (int) ($scanResult['files_scanned'] ?? 0),
            'files_skipped' => (int) ($scanResult['files_skipped'] ?? 0),
            'files_hash_checked' => (int) ($scanResult['files_hash_checked'] ?? 0),
            'database_items_scanned' => (int) ($scanResult['database_items_scanned'] ?? 0),
            'database_issues_found' => (int) ($scanResult['database_issues_found'] ?? 0),
            'issues_found' => (int) ($scanResult['issues_found'] ?? 0),
            'duration_ms' => (int) ($scanResult['duration_ms'] ?? 0),
        ];
    }

    /**
     * @param array<string,mixed> $context
     */
    private function manualScannerFindingEventKey(array $context): string
    {
        $scanType = (string) ($context['scan_type'] ?? '');
        if ($scanType === 'database' || (int) ($context['database_issues_found'] ?? 0) > 0) {
            return 'scanner.db_finding';
        }

        if ($scanType === 'vulnerability') {
            return 'scanner.vulnerability_finding';
        }

        return strtolower((string) ($context['severity'] ?? '')) === 'medium'
            ? 'scanner.medium_finding'
            : 'scanner.high_finding';
    }

    /**
     * @param array<int,mixed> $issues
     */
    private function manualScannerMaxSeverity(array $issues): string
    {
        $ranked = [
            'critical' => 4,
            'high' => 3,
            'medium' => 2,
            'low' => 1,
        ];
        $max = '';
        $maxRank = 0;

        foreach ($issues as $issue) {
            if (!is_array($issue)) {
                continue;
            }

            $severity = strtolower((string) ($issue['severity'] ?? ''));
            $rank = (int) ($ranked[$severity] ?? 0);
            if ($rank > $maxRank) {
                $max = $severity;
                $maxRank = $rank;
            }
        }

        return $max;
    }

    /**
     * @param array<string,mixed> $progress
     * @return array<string,mixed>
     */
    private function progressiveScanPayload(array $progress): array
    {
        $processed = max(0, (int) ($progress['files_processed'] ?? 0));
        $total = max(0, (int) ($progress['files_total'] ?? 0));
        $discovered = max(0, (int) ($progress['files_discovered'] ?? $total));
        $precheckedSkipped = max(0, (int) ($progress['files_prechecked_skipped'] ?? 0));
        $filesLabel = (string) ($progress['files_label'] ?? '');
        if ($filesLabel === '' && $discovered > $total) {
            /* translators: 1: queued files already checked, 2: total files queued for deeper checks, 3: unchanged files skipped before scanning. */
            $filesLabel = sprintf(__('%1$d / %2$d deep checks, %3$d unchanged skipped', 'xerotact-shield-x-security'), $processed, $total, $precheckedSkipped);
        }
        $stageLabel = (string) ($progress['stage_label'] ?? '');
        if ($stageLabel === '' && $precheckedSkipped > 0) {
            /* translators: %d: unchanged files skipped before hashing/content scanning. */
            $stageLabel = sprintf(__('%d unchanged files skipped before hashing.', 'xerotact-shield-x-security'), $precheckedSkipped);
        }

        $failureDetails = [];
        if (isset($progress['failure_details']) && is_array($progress['failure_details'])) {
            foreach ($progress['failure_details'] as $detail) {
                if (!is_array($detail)) {
                    continue;
                }

                $plugin = sanitize_text_field((string) ($detail['plugin'] ?? ''));
                $reason = sanitize_text_field((string) ($detail['reason'] ?? ''));
                if ($plugin === '' && $reason === '') {
                    continue;
                }

                $failureDetails[] = [
                    'plugin' => $plugin !== '' ? $plugin : __('Unknown plugin', 'xerotact-shield-x-security'),
                    'reason' => $reason !== '' ? $reason : __('No failure reason was returned.', 'xerotact-shield-x-security'),
                ];
            }
        }

        return [
            'status' => (string) ($progress['status'] ?? 'running'),
            'percent' => max(0, min(100, (int) ($progress['percent'] ?? 0))),
            'indeterminate' => !empty($progress['indeterminate']),
            'stage_label' => $stageLabel,
            'title_label' => (string) ($progress['title_label'] ?? ''),
            'intro_label' => (string) ($progress['intro_label'] ?? ''),
            'percent_label' => (string) ($progress['percent_label'] ?? ''),
            'percent_value_label' => (string) ($progress['percent_value_label'] ?? ''),
            'files_metric_label' => (string) ($progress['files_metric_label'] ?? ''),
            'batches_metric_label' => (string) ($progress['batches_metric_label'] ?? ''),
            'files_processed' => $processed,
            'files_total' => $total,
            'files_label' => $filesLabel,
            'batches' => max(0, (int) ($progress['batches'] ?? 0)),
            'batches_label' => (string) ($progress['batches_label'] ?? ''),
            'scope_label' => (string) ($progress['scope_label'] ?? __('Selected scan scopes', 'xerotact-shield-x-security')),
            'failure_details' => $failureDetails,
        ];
    }

    /**
     * @param array<string,mixed> $progress
     * @return array<string,mixed>
     */
    private function progressiveBackupPayload(array $progress): array
    {
        $payload = $this->progressiveScanPayload($progress);
        if ((string) ($progress['type'] ?? '') !== 'restore_point' || !$this->restorePointNeedsGoogleUpload($progress)) {
            return $payload;
        }

        $payload['stage_label'] = __('Stage 1 of 2: Creating local restore point', 'xerotact-shield-x-security');
        $payload['title_label'] = __('Creating local restore point', 'xerotact-shield-x-security');
        $payload['intro_label'] = __('First SHIELD-X builds the local restore point. Google Drive upload starts after the local snapshot is complete.', 'xerotact-shield-x-security');
        $payload['percent_label'] = __('Build', 'xerotact-shield-x-security');

        return $payload;
    }

    /**
     * @param array<string,mixed> $result
     */
    private function restorePointNeedsGoogleUpload(array $result): bool
    {
        if (empty($result['ok']) || (string) ($result['type'] ?? '') !== 'restore_point') {
            return false;
        }

        $summary = BackupStorageManager::fromWordPress()->summary();

        return !empty($summary['uses_google']);
    }

    /**
     * @param array<string,mixed> $result
     * @return array<string,mixed>
     */
    private function restorePointGoogleUploadProgressPayload(array $result, string $status, string $message = ''): array
    {
        $files = max(0, (int) ($result['files'] ?? 0));
        $bytes = max(0, (int) ($result['bytes_exported'] ?? 0));
        $sizeLabel = $bytes > 0
            ? (function_exists('size_format') ? size_format($bytes) : number_format($bytes) . ' B')
            : __('size pending', 'xerotact-shield-x-security');
        $statusLabel = __('Uploading', 'xerotact-shield-x-security');
        $scopeLabel = __('Uploading restore point files to Google Drive. Time remaining depends on backup size and the server connection; keep this page open.', 'xerotact-shield-x-security');
        $percent = 98;

        if ($status === 'completed') {
            $statusLabel = __('Uploaded', 'xerotact-shield-x-security');
            $scopeLabel = __('Google Drive upload finished. SHIELD-X is refreshing the restore point list.', 'xerotact-shield-x-security');
            $percent = 100;
        } elseif ($status === 'failed') {
            $statusLabel = __('Failed', 'xerotact-shield-x-security');
            $scopeLabel = $message !== '' ? $message : __('Google Drive upload failed. The local restore point was retained.', 'xerotact-shield-x-security');
            $percent = 100;
        }

        return [
            'status' => $status,
            'percent' => $percent,
            'indeterminate' => $status === 'running',
            'stage_label' => $status === 'completed'
                ? __('Stage 2 of 2: Google Drive upload complete', 'xerotact-shield-x-security')
                : __('Stage 2 of 2: Uploading to Google Drive', 'xerotact-shield-x-security'),
            'title_label' => __('Uploading restore point to Google Drive', 'xerotact-shield-x-security'),
            'intro_label' => __('The local restore point is complete. SHIELD-X is now sending a Google Drive copy in a separate step.', 'xerotact-shield-x-security'),
            'percent_label' => __('Stage', 'xerotact-shield-x-security'),
            'files_metric_label' => __('Package', 'xerotact-shield-x-security'),
            'batches_metric_label' => __('Google Drive', 'xerotact-shield-x-security'),
            'files_processed' => $files,
            'files_total' => max(1, $files),
            /* translators: 1: number of restore point files ready for upload, 2: formatted restore point size. */
            'files_label' => sprintf(__('%1$d files ready / %2$s', 'xerotact-shield-x-security'), $files, $sizeLabel),
            'batches' => 0,
            'batches_label' => $statusLabel,
            'scope_label' => $scopeLabel,
            'failure_details' => [],
        ];
    }

    /**
     * @param array<string,mixed> $progress
     * @return array<string,mixed>
     */
    private function logRotationScanProgressPayload(array $progress): array
    {
        $status = (string) ($progress['status'] ?? 'running');
        $scannedEntries = max(0, (int) ($progress['scanned_entries'] ?? 0));
        $discoveredFiles = max(0, (int) ($progress['discovered_files'] ?? $progress['total_files'] ?? 0));
        $markedFiles = max(0, (int) ($progress['marked_files'] ?? 0));
        $percent = $status === 'completed'
            ? 100
            : min(95, max(5, (int) floor(($scannedEntries / max(1, $scannedEntries + 1000)) * 95)));
        /* translators: %d: number of filesystem entries checked during log discovery. */
        $filesLabel = sprintf(__('%d entries checked', 'xerotact-shield-x-security'), $scannedEntries);
        /* translators: %d: number of log files found during log discovery. */
        $batchesLabel = sprintf(__('%d logs found', 'xerotact-shield-x-security'), $discoveredFiles);
        /* translators: 1: discovered log file count, 2: log files marked for rotation. */
        $scopeLabel = sprintf(__('Whole-site log discovery: %1$d found, %2$d marked for rotation.', 'xerotact-shield-x-security'), $discoveredFiles, $markedFiles);

        return [
            'status' => $status,
            'percent' => $percent,
            'files_processed' => $scannedEntries,
            'files_total' => $status === 'completed' ? $scannedEntries : max($scannedEntries + 1, 1),
            'files_label' => $filesLabel,
            'batches' => $discoveredFiles,
            'batches_label' => $batchesLabel,
            'scope_label' => $scopeLabel,
        ];
    }

    private function isProgressiveScanAjax(): bool
    {
        $hasProgressFlag = $this->postedText('shieldx_ajax_progress', '') === '1'
            || $this->queryText('shieldx_ajax_progress') === '1';

        if (!$hasProgressFlag) {
            return $this->progressiveRequestHeaderWantsJson();
        }

        return $this->progressiveRequestHeaderWantsJson() || $this->progressiveRequestAcceptsJson();
    }

    private function progressiveRequestHeaderWantsJson(): bool
    {
        $header = '';
        if (isset($_SERVER['HTTP_X_SHIELDX_PROGRESS']) && is_scalar($_SERVER['HTTP_X_SHIELDX_PROGRESS'])) {
            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Header is unslashed then sanitized before comparison.
            $header = sanitize_text_field((string) wp_unslash($_SERVER['HTTP_X_SHIELDX_PROGRESS']));
        }

        return $header === '1';
    }

    private function progressiveRequestAcceptsJson(): bool
    {
        if (!isset($_SERVER['HTTP_ACCEPT']) || !is_scalar($_SERVER['HTTP_ACCEPT'])) {
            return false;
        }

        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Header is unslashed then sanitized before content negotiation.
        $accept = strtolower(sanitize_text_field((string) wp_unslash($_SERVER['HTTP_ACCEPT'])));

        return strpos($accept, 'application/json') !== false;
    }

    private function denyUnauthorizedProgressiveRequest(): void
    {
        if (self::currentUserCanManageShieldX()) {
            return;
        }

        if ($this->progressiveRequestHeaderWantsJson()) {
            $this->sendProgressiveScanJson([
                'ok' => false,
                'status' => 'failed',
                'error' => __('You do not have permission to continue this SHIELD-X job.', 'xerotact-shield-x-security'),
            ], 403);
        }

        wp_die(esc_html__('You do not have permission to change SHIELD-X settings.', 'xerotact-shield-x-security'));
    }

    /**
     * @param array<string,mixed> $payload
     */
    private function sendProgressiveScanJson(array $payload, int $statusCode = 200): void
    {
        nocache_headers();
        wp_send_json($payload, $statusCode);
    }

    private function progressiveScanQueryText(string $key): string
    {
        $value = $this->queryRaw($key);
        if (is_string($value)) {
            return sanitize_text_field(rawurldecode($value));
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Compatibility read for malformed progressive-scan URLs; assertCanSave() verifies the recovered nonce immediately after this helper returns.
        foreach ($_GET as $queryKey => $queryValue) {
            if (!is_scalar($queryKey) || !is_scalar($queryValue)) {
                continue;
            }

            $normalizedKey = $this->normalizeProgressiveScanQueryKey((string) $queryKey);
            if ($normalizedKey !== $key) {
                continue;
            }

            return sanitize_text_field(rawurldecode((string) wp_unslash($queryValue)));
        }

        return '';
    }

    private function normalizeProgressiveScanQueryKey(string $key): string
    {
        $key = strtolower(rawurldecode($key));
        $key = str_replace([';', '.', ' ', '-'], '_', $key);
        $key = preg_replace('/[^a-z0-9_]+/', '_', $key) ?: '';
        $key = trim($key, '_');

        while (strpos($key, 'amp_') === 0) {
            $key = substr($key, 4);
        }

        return $key;
    }

    /**
     * @param array<string,mixed> $progress
     */
    private function renderProgressiveScanPage(array $progress, string $nextUrl): void
    {
        status_header(200);
        nocache_headers();

        $percent = max(0, min(99, (int) ($progress['percent'] ?? 0)));
        $processed = max(0, (int) ($progress['files_processed'] ?? 0));
        $total = max(0, (int) ($progress['files_total'] ?? 0));
        $scopeLabel = (string) ($progress['scope_label'] ?? __('Selected scan scopes', 'xerotact-shield-x-security'));
        $batches = max(0, (int) ($progress['batches'] ?? 0));
        $title = (string) ($progress['title_label'] ?? __('Scanning in progress', 'xerotact-shield-x-security'));
        $intro = (string) ($progress['intro_label'] ?? __('SHIELD-X is processing this scan in small batches so large sites stay below PHP and server timeout limits.', 'xerotact-shield-x-security'));
        $percentLabel = (string) ($progress['percent_label'] ?? __('Progress', 'xerotact-shield-x-security'));
        $filesMetricLabel = (string) ($progress['files_metric_label'] ?? __('Files', 'xerotact-shield-x-security'));
        $batchesMetricLabel = (string) ($progress['batches_metric_label'] ?? __('Batches', 'xerotact-shield-x-security'));
        $filesLabel = (string) ($progress['files_label'] ?? ((string) $processed . ' / ' . (string) $total));
        $batchesLabel = (string) ($progress['batches_label'] ?? (string) $batches);
        ?>
        <!doctype html>
        <html <?php language_attributes(); ?>>
        <head>
            <meta charset="<?php bloginfo('charset'); ?>">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <meta http-equiv="refresh" content="1;url=<?php echo esc_url($nextUrl); ?>">
            <title><?php echo esc_html($title); ?></title>
            <?php $this->printProgressiveScanPageStyles(); ?>
        </head>
        <body>
            <main class="shieldx-scan-progress" role="main" aria-live="polite">
                <h1><?php echo esc_html($title); ?></h1>
                <p><?php echo esc_html($intro); ?></p>
                <div class="shieldx-progress-track" aria-label="<?php echo esc_attr__('Scan progress', 'xerotact-shield-x-security'); ?>" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="<?php echo esc_attr((string) $percent); ?>">
                    <div class="shieldx-progress-fill" style="width: <?php echo esc_attr((string) $percent); ?>%;"></div>
                </div>
                <div class="shieldx-progress-meta">
                    <span><?php echo esc_html($percentLabel); ?><strong><?php echo esc_html((string) $percent); ?>%</strong></span>
                    <span><?php echo esc_html($filesMetricLabel); ?><strong><?php echo esc_html($filesLabel); ?></strong></span>
                    <span><?php echo esc_html($batchesMetricLabel); ?><strong><?php echo esc_html($batchesLabel); ?></strong></span>
                </div>
                <p><?php echo esc_html($scopeLabel); ?></p>
                <a class="button" href="<?php echo esc_url($nextUrl); ?>"><?php echo esc_html__('Continue scan now', 'xerotact-shield-x-security'); ?></a>
            </main>
        </body>
        </html>
        <?php
    }

    private function printProgressiveScanPageStyles(): void
    {
        if (!function_exists('wp_register_style') || !function_exists('wp_add_inline_style') || !function_exists('wp_print_styles')) {
            return;
        }

        wp_register_style('shieldx-progress-page', false, [], defined('SHIELDX_VERSION') ? (string) SHIELDX_VERSION : '1.0.0');
        wp_add_inline_style('shieldx-progress-page', 'body{margin:0;background:#f3f5f9;color:#101936;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shieldx-scan-progress{max-width:720px;margin:8vh auto;padding:32px;background:#fff;border:1px solid #dfe7f4;border-radius:10px;box-shadow:0 18px 48px rgba(15,23,42,.08)}.shieldx-scan-progress h1{font-size:28px;font-weight:600;margin:0 0 8px}.shieldx-scan-progress p{color:#53627e;font-size:15px;line-height:1.6;margin:0 0 20px}.shieldx-progress-track{height:14px;background:#e9eef7;border-radius:999px;overflow:hidden}.shieldx-progress-fill{height:100%;background:#2f6df6;border-radius:999px;transition:width .25s ease}.shieldx-progress-meta{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;margin:22px 0}.shieldx-progress-meta span{display:block;padding:14px;border:1px solid #dfe7f4;border-radius:8px;background:#f8fbff;color:#53627e}.shieldx-progress-meta strong{display:block;color:#101936;font-size:20px;margin-top:4px}.button{display:inline-block;padding:12px 18px;border:1px solid #bcd1f4;border-radius:8px;background:#fff;color:#0b57e3;text-decoration:none;font-weight:600}');
        wp_print_styles(['shieldx-progress-page']);
    }

    public function runVulnerabilityScan(): void
    {
        $this->assertCanSave('shieldx_run_vulnerability_scan');
        $this->prepareLongAdminAction();

        $ajax = $this->isProgressiveScanAjax();

        VulnerabilityScanner::saveSettings([
            'feed_source' => $this->postedText('vulnerability_feed_source', 'wpvulnerability'),
            'custom_feed_url' => $this->postedText('vulnerability_custom_feed_url', ''),
        ]);
        $result = VulnerabilityScanner::fromWordPress()->scan($this->postFlag('refresh_vulnerability_feed'));
        $this->notifyManualScannerResult($result, 'vulnerability');
        $redirectUrl = add_query_arg([
            'shieldx_vuln_scanned' => '1',
        ], admin_url('admin.php?page=shield-x-scan')) . '#shieldx-vulnerability-scanner';

        if ($ajax) {
            $this->sendProgressiveScanJson([
                'ok' => true,
                'status' => 'completed',
                'scan_id' => 'vulnerability',
                'progress' => $this->vulnerabilityScanProgressPayload($result),
                'redirect_url' => $redirectUrl,
            ]);
        }

        wp_safe_redirect($redirectUrl);
        exit;
    }

    /**
     * @param array<string,mixed> $result
     * @return array<string,mixed>
     */
    private function vulnerabilityScanProgressPayload(array $result): array
    {
        $plugins = max(0, (int) ($result['plugins_total'] ?? 0));
        $themes = max(0, (int) ($result['themes_total'] ?? 0));
        $software = max(0, (int) ($result['software_total'] ?? 0));
        $checked = max(1, 1 + $plugins + $themes + $software);
        $issues = max(0, (int) ($result['issues_found'] ?? 0));
        $advisoryCache = isset($result['advisory_cache']) && is_array($result['advisory_cache'])
            ? $result['advisory_cache']
            : [];
        $feedSource = $this->formatLabel((string) ($advisoryCache['source'] ?? 'local-cache'));
        $feedRecords = max(0, (int) ($advisoryCache['records_total'] ?? 0));
        $feedProblem = (string) ($advisoryCache['last_error'] ?? '');
        if ($feedProblem === '') {
            $feedProblem = (string) ($advisoryCache['last_warning'] ?? '');
        }

        /* translators: %d: number of vulnerability-scanner components checked. */
        $componentsLabel = sprintf(_n('%d component checked', '%d components checked', $checked, 'xerotact-shield-x-security'), $checked);
        /* translators: %d: number of vulnerability scanner findings. */
        $findingsLabel = sprintf(_n('%d finding', '%d findings', $issues, 'xerotact-shield-x-security'), $issues);
        /* translators: 1: plugin count, 2: theme count, 3: software component count, 4: vulnerability feed name, 5: advisory record count. */
        $scopeLabel = sprintf(__('Checked WordPress core, %1$d plugins, %2$d themes, and %3$d software components. Feed: %4$s with %5$d advisory records.', 'xerotact-shield-x-security'), $plugins, $themes, $software, $feedSource, $feedRecords);
        if ($feedProblem !== '') {
            /* translators: %s: vulnerability feed warning or error. */
            $scopeLabel .= ' ' . sprintf(__('Feed note: %s', 'xerotact-shield-x-security'), $feedProblem);
        }

        return [
            'status' => 'completed',
            'percent' => 100,
            'title_label' => __('Vulnerability scan complete', 'xerotact-shield-x-security'),
            'intro_label' => __('SHIELD-X checked installed versions and vulnerability feed data.', 'xerotact-shield-x-security'),
            'percent_label' => __('Progress', 'xerotact-shield-x-security'),
            'files_metric_label' => __('Components', 'xerotact-shield-x-security'),
            'batches_metric_label' => __('Findings', 'xerotact-shield-x-security'),
            'files_processed' => $checked,
            'files_total' => $checked,
            'files_label' => $componentsLabel,
            'batches' => $issues,
            'batches_label' => $findingsLabel,
            'scope_label' => $scopeLabel,
        ];
    }

    public function createRestorePoint(): void
    {
        $this->assertCanSave('shieldx_create_restore_point');

        $ajax = $this->isProgressiveScanAjax();
        $forceFullSnapshot = $this->postedText('restore_point_mode', '') === 'full';
        $result = RestorePointManager::fromWordPress()->startProgressiveCreate(
            $this->postedText('restore_reason', 'manual'),
            [],
            function_exists('get_current_user_id') ? (int) get_current_user_id() : 0,
            true,
            $forceFullSnapshot
        );
        $restoreId = (string) ($result['restore_id'] ?? '');

        if (empty($result['ok']) || (string) ($result['status'] ?? '') === 'failed' || $restoreId === '') {
            NotificationManager::fromWordPress()->notify('backup.backup_failed', [
                'operation' => 'restore_point_create',
                'status' => (string) ($result['status'] ?? 'failed'),
                'error' => (string) ($result['message'] ?? __('Restore point failed.', 'xerotact-shield-x-security')),
            ], 'warning');

            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'error' => (string) ($result['message'] ?? __('Restore point failed.', 'xerotact-shield-x-security')),
                    'progress' => $this->progressiveBackupPayload($result),
                    'redirect_url' => admin_url('admin.php?page=shield-x-backup'),
                ], 400);
            }

            $query = ['shieldx_restore_error' => AdminFlashNotice::flag('shieldx_restore_error', (string) ($result['message'] ?? __('Restore point failed.', 'xerotact-shield-x-security')))];
            wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-backup')) . '#shieldx-restore-points');
            exit;
        }

        if ($ajax) {
            $this->sendProgressiveScanJson([
                'ok' => true,
                'status' => (string) ($result['status'] ?? 'running'),
                'scan_id' => $restoreId,
                'progress' => $this->progressiveBackupPayload($result),
                'next_url' => $this->progressiveBackupUrl($restoreId, true),
            ]);
        }

        wp_safe_redirect($this->progressiveBackupUrl($restoreId));
        exit;
    }

    public function restoreRestorePoint(): void
    {
        $this->assertCanSave('shieldx_restore_restore_point');
        $restoreId = '';
        $isolatedRestoreStarted = false;

        try {
            $this->executeRestoreRestorePoint($restoreId, $isolatedRestoreStarted);
        } catch (\Throwable $error) {
            $this->failRestoreActionSafely($error, $restoreId, $isolatedRestoreStarted);
        }
    }

    private function executeRestoreRestorePoint(string &$restoreId, bool &$isolatedRestoreStarted): void
    {
        $restoreId = $this->postedText('restore_id', '');
        $selectedRestore = $this->postedText('restore_selection_mode', '') === 'selected';
        $options = [];

        if (!$selectedRestore) {
            $restoreSource = $this->postedText('restore_source', 'auto');
            try {
                if (!$this->isolatedRecoveryRestoreAvailable()) {
                    $result = [
                        'ok' => false,
                        'message' => __('SHIELD-X Recovery must be active before a full-site restore can begin.', 'xerotact-shield-x-security'),
                    ];
                } else {
                    $result = \ShieldXRecovery\IsolatedRestoreCoordinator::fromWordPress()->start(
                        $restoreId,
                        function_exists('get_current_user_id') ? (int) get_current_user_id() : 0,
                        $restoreSource
                    );
                }
            } catch (\Throwable $error) {
                $result = [
                    'ok' => false,
                    'message' => __('The isolated full restore could not start safely. No live files were changed.', 'xerotact-shield-x-security'),
                ];
            }

            if (!empty($result['ok'])) {
                $isolatedRestoreStarted = true;
                $this->renderIsolatedRestoreHandoff($result);
            }

            NotificationManager::fromWordPress()->notify('backup.restore_failed', [
                'operation' => 'isolated_restore_start',
                'restore_id' => $restoreId,
                'restore_source' => $restoreSource,
                'status' => 'failed',
                'error' => (string) ($result['message'] ?? __('Isolated full restore could not start.', 'xerotact-shield-x-security')),
            ], 'warning');
            $query = [
                'shieldx_restore_error' => AdminFlashNotice::flag(
                    'shieldx_restore_error',
                    (string) ($result['message'] ?? __('Isolated full restore could not start.', 'xerotact-shield-x-security'))
                ),
                'shieldx_restore_view' => $restoreId,
            ];
            wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-backup-restore')));
            exit;
        }

        if ($selectedRestore) {
            $scope = $this->postedText('restore_scope', 'files');
            $includeFiles = $scope !== 'database';
            $includeDatabase = $scope === 'database';
            $options = [
                'include_files' => $includeFiles,
                'file_paths' => $includeFiles ? $this->postedRestoreFilePaths() : [],
                'include_database' => $includeDatabase,
                'table_names' => $includeDatabase ? $this->postedRestoreTableNames() : [],
            ];
        }
        $options['restore_source'] = $this->postedText('restore_source', 'auto');

        try {
            $result = RestorePointManager::fromWordPress()->restore(
                $restoreId,
                function_exists('get_current_user_id') ? (int) get_current_user_id() : 0,
                $options
            );
        } catch (\Throwable $error) {
            $result = [
                'ok' => false,
                'message' => __('The selected restore could not be completed safely. No additional file changes were made.', 'xerotact-shield-x-security'),
            ];
        }
        $notificationContext = [
            'operation' => 'restore_point_restore',
            'restore_id' => (string) ($result['restore_id'] ?? $restoreId),
            'restore_source' => (string) ($options['restore_source'] ?? 'auto'),
            'include_files' => isset($options['include_files']) ? (bool) $options['include_files'] : true,
            'include_database' => isset($options['include_database']) ? (bool) $options['include_database'] : true,
            'protected_files_skipped' => (int) ($result['protected_files_skipped'] ?? 0),
            'status' => empty($result['ok']) ? 'failed' : 'completed',
        ];

        if (empty($result['ok'])) {
            $notificationContext['error'] = (string) ($result['message'] ?? __('Restore point restore failed.', 'xerotact-shield-x-security'));
            NotificationManager::fromWordPress()->notify('backup.restore_failed', $notificationContext, 'warning');
            $query = [
                'shieldx_restore_error' => AdminFlashNotice::flag('shieldx_restore_error', (string) ($result['message'] ?? __('Restore point restore failed.', 'xerotact-shield-x-security'))),
                'shieldx_restore_view' => $restoreId,
            ];
        } else {
            NotificationManager::fromWordPress()->notify('backup.restore_restored', $notificationContext, 'info');
            $query = [
                'shieldx_restore_restored' => (string) ($result['restore_id'] ?? ''),
                'shieldx_restore_view' => (string) ($result['restore_id'] ?? $restoreId),
            ];
            if ((int) ($result['protected_files_skipped'] ?? 0) > 0) {
                $query['shieldx_restore_protected_skipped'] = (string) (int) ($result['protected_files_skipped'] ?? 0);
            }
        }

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-backup-restore')));
        exit;
    }

    private function failRestoreActionSafely(\Throwable $error, string $restoreId, bool $isolatedRestoreStarted): void
    {
        try {
            \ShieldX\Support\ErrorLog::write(sprintf(
                '[SHIELD-X] Restore admin action failed safely (%s): %s',
                get_class($error),
                $error->getMessage()
            ));
        } catch (\Throwable $loggingError) {
            // The recovery response must not depend on logging being available.
        }

        $failureState = $isolatedRestoreStarted ? 'started' : 'failed';
        $redirectUrl = '/wp-admin/admin.php?page=shield-x-backup';

        try {
            $redirectUrl = add_query_arg(
                [
                    'page' => 'shield-x-backup-restore',
                    'shieldx_restore_action_failed' => $failureState,
                    'shieldx_restore_view' => sanitize_text_field($restoreId),
                ],
                admin_url('admin.php')
            );

            if (wp_safe_redirect($redirectUrl)) {
                exit;
            }
        } catch (\Throwable $redirectError) {
            try {
                \ShieldX\Support\ErrorLog::write('[SHIELD-X] Restore failure redirect was unavailable.');
            } catch (\Throwable $loggingError) {
                // Continue to the dependency-free response below.
            }
        }

        if (!headers_sent()) {
            header('Location: ' . $redirectUrl, true, 302);
            exit;
        }

        $message = $isolatedRestoreStarted
            ? 'The restore started, but SHIELD-X could not open the protected Recovery controller. Return to Backup and open the active restore.'
            : 'SHIELD-X stopped the restore action safely before it could finish. Return to Backup and try again.';
        echo '<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>SHIELD-X restore</title></head><body>';
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Dependency-free last-resort response; the value is escaped with native PHP.
        echo '<main><h1>SHIELD-X restore</h1><p>' . htmlspecialchars($message, ENT_QUOTES, 'UTF-8') . '</p>';
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Dependency-free last-resort response; the value is escaped with native PHP.
        echo '<p><a href="' . htmlspecialchars($redirectUrl, ENT_QUOTES, 'UTF-8') . '">Return to Backup</a></p></main></body></html>';
        exit;
    }

    /**
     * Download an administrator-safe handoff for a hosting provider. This is
     * deliberately metadata only: it must never disclose restore storage
     * paths, database credentials, recovery tokens, or backed-up file data.
     */
    public function downloadRestoreHandoff(): void
    {
        $this->assertCanSave('shieldx_download_restore_handoff');

        $restoreId = $this->sanitizeRestorePointId($this->postedText('restore_id', ''));
        $source = $this->postedText('restore_source', 'local') === 'google' ? 'google' : 'local';
        if ($restoreId === '') {
            wp_die(esc_html__('Choose a valid restore point before creating a support handoff.', 'xerotact-shield-x-security'));
        }

        $detail = RestorePointManager::fromWordPress()->details($restoreId, 1, 1);
        if ($detail === null || (string) ($detail['restore_id'] ?? '') !== $restoreId) {
            wp_die(esc_html__('This restore point is no longer available. Refresh the Backup page and choose an available point.', 'xerotact-shield-x-security'));
        }

        $database = isset($detail['database']) && is_array($detail['database']) ? $detail['database'] : [];
        $handoff = [
            'format' => 'shieldx-hosting-restore-handoff-v1',
            'generated_at' => gmdate('c'),
            'restore_point' => [
                'id' => $restoreId,
                'created_at' => (string) ($detail['created_at'] ?? ''),
                'source' => $source,
                'snapshot_scope' => (string) ($detail['snapshot_scope'] ?? ''),
                'snapshot_mode' => (string) ($detail['snapshot_mode'] ?? ''),
                'base_restore_id' => (string) ($detail['base_restore_id'] ?? ''),
                'files_count' => max(0, (int) ($detail['files_count'] ?? 0)),
                'bytes_exported' => max(0, (int) ($detail['bytes_exported'] ?? 0)),
                'database_backup_included' => !empty($database['backup_id']),
            ],
            'hosting_request' => [
                'summary' => 'Please restore the WordPress files and database from the hosting backup that was taken before the incident.',
                'safety_notes' => [
                    'Restore files and database together when the hosting backup supports both.',
                    'Do not change permissions on the WordPress root directory itself or any parent directory.',
                    'Do not remove the SHIELD-X plugin or recovery storage while the incident is being investigated.',
                    'Contact the site owner before replacing files when the available host backup is older than this restore point.',
                ],
            ],
            'privacy' => [
                'included' => 'Restore-point metadata only.',
                'excluded' => [
                    'backup files and database contents',
                    'absolute server paths',
                    'database credentials',
                    'recovery tokens and authentication data',
                    'site URL and administrator identity',
                ],
            ],
        ];
        $json = wp_json_encode($handoff, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        if (!is_string($json)) {
            wp_die(esc_html__('SHIELD-X could not build the hosting support handoff.', 'xerotact-shield-x-security'));
        }

        NotificationManager::fromWordPress()->notify('backup.restore_handoff_downloaded', [
            'operation' => 'restore_handoff_downloaded',
            'restore_id' => $restoreId,
            'restore_source' => $source,
        ], 'info');

        if (function_exists('nocache_headers')) {
            nocache_headers();
        }

        $filename = 'shield-x-restore-handoff-' . gmdate('Ymd-His') . '.json';
        header('Content-Type: application/json; charset=utf-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
        header('X-Content-Type-Options: nosniff');
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- JSON file response, not HTML output.
        echo $json;
        exit;
    }

    /**
     * Hand the isolated controller credential to the same-origin gateway in a
     * short-lived HttpOnly cookie. The redirect contains only the non-secret
     * job identifier, so optimizers never receive a generated HTML handoff and
     * the credential never enters browser history, referrers, or server logs.
     *
     * @param array<string,mixed> $result
     */
    private function renderIsolatedRestoreHandoff(array $result): void
    {
        $gatewayUrl = (string) ($result['gateway_url'] ?? '');
        $jobId = (string) ($result['job_id'] ?? '');
        $browserToken = (string) ($result['browser_token'] ?? '');
        if ($gatewayUrl === '' || $jobId === '' || $browserToken === '') {
            return;
        }

        if (headers_sent()) {
            throw new \RuntimeException('The isolated Recovery handoff could not set its protected browser credential.');
        }

        $gatewayPath = (string) wp_parse_url($gatewayUrl, PHP_URL_PATH);
        if ($gatewayPath === '' || $gatewayPath[0] !== '/') {
            throw new \RuntimeException('The isolated Recovery gateway path is invalid.');
        }

        $secure = function_exists('is_ssl')
            ? is_ssl()
            : strtolower((string) wp_parse_url($gatewayUrl, PHP_URL_SCHEME)) === 'https';
        if (!setcookie('shieldx_restore_session', $browserToken, [
            'expires' => time() + 900,
            'path' => $gatewayPath,
            'secure' => $secure,
            'httponly' => true,
            'samesite' => 'Strict',
        ])) {
            throw new \RuntimeException('The isolated Recovery handoff credential could not be protected.');
        }

        if (function_exists('nocache_headers')) {
            nocache_headers();
        }
        header('Referrer-Policy: no-referrer');
        $handoffUrl = add_query_arg([
            'restore_action' => 'open',
            'job_id' => $jobId,
        ], $gatewayUrl);
        if (!wp_safe_redirect($handoffUrl, 303, 'SHIELD-X')) {
            throw new \RuntimeException('The isolated Recovery gateway redirect was rejected.');
        }
        exit;
    }

    public function deleteRestorePointsBefore(): void
    {
        $this->assertCanSave('shieldx_delete_restore_points_before');

        $result = RestorePointManager::fromWordPress()->deleteBeforeDate($this->postedText('delete_before_date', ''));
        $deletedRestorePoints = max(0, (int) ($result['deleted_restore_points'] ?? 0));
        $skippedRestorePoints = max(0, (int) ($result['skipped_restore_points'] ?? 0));
        $skippedActiveRestorePoints = max(0, (int) ($result['skipped_active_restore_points'] ?? 0));
        $skippedRequiredRestorePoints = max(0, (int) ($result['skipped_required_restore_points'] ?? 0));
        $deletedFiles = max(0, (int) ($result['deleted_files'] ?? 0));
        $deletedBytes = max(0, (int) ($result['deleted_bytes'] ?? 0));
        $failedFiles = max(0, (int) ($result['failed_files'] ?? 0));

        if (empty($result['ok']) && $deletedRestorePoints === 0 && $deletedFiles === 0 && $failedFiles === 0) {
            wp_safe_redirect(add_query_arg([
                'shieldx_backup_error' => AdminFlashNotice::flag('shieldx_backup_error', (string) ($result['message'] ?? __('Restore point cleanup failed.', 'xerotact-shield-x-security'))),
            ], admin_url('admin.php?page=shield-x-backup')) . '#shieldx-restore-points');
            exit;
        }

        wp_safe_redirect(add_query_arg([
            'shieldx_restore_points_cleaned' => '1',
            'shieldx_restore_points_deleted' => (string) $deletedRestorePoints,
            'shieldx_restore_points_skipped' => (string) $skippedRestorePoints,
            'shieldx_restore_points_skipped_active' => (string) $skippedActiveRestorePoints,
            'shieldx_restore_points_skipped_required' => (string) $skippedRequiredRestorePoints,
            'shieldx_restore_point_cleanup_files' => (string) $deletedFiles,
            'shieldx_restore_point_cleanup_bytes' => (string) $deletedBytes,
            'shieldx_restore_point_cleanup_failed' => (string) $failedFiles,
            'shieldx_restore_point_cleanup_before' => (string) ($result['before_date'] ?? ''),
        ], admin_url('admin.php?page=shield-x-backup') . '#shieldx-restore-points'));
        exit;
    }

    public function deleteRestorePoint(): void
    {
        $this->assertCanSave('shieldx_delete_restore_point');

        $restoreId = $this->postedText('restore_id', '');
        $password = $this->postedText('shieldx_current_password', '');
        $userId = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;

        if (!$this->currentUserPasswordMatches($userId, $password)) {
            $message = $password === ''
                ? __('Enter the current administrator password to delete this restore point.', 'xerotact-shield-x-security')
                : __('The administrator password did not match. The restore point was not deleted.', 'xerotact-shield-x-security');
            wp_safe_redirect(add_query_arg([
                'shieldx_backup_error' => AdminFlashNotice::flag('shieldx_backup_error', $message),
            ], admin_url('admin.php?page=shield-x-backup')) . '#shieldx-restore-points');
            exit;
        }

        $result = RestorePointManager::fromWordPress()->deleteById($restoreId);
        if (empty($result['ok'])) {
            wp_safe_redirect(add_query_arg([
                'shieldx_backup_error' => AdminFlashNotice::flag(
                    'shieldx_backup_error',
                    (string) ($result['message'] ?? __('The restore point could not be deleted.', 'xerotact-shield-x-security'))
                ),
            ], admin_url('admin.php?page=shield-x-backup')) . '#shieldx-restore-points');
            exit;
        }

        wp_safe_redirect(add_query_arg([
            'shieldx_restore_point_deleted' => '1',
        ], admin_url('admin.php?page=shield-x-backup')) . '#shieldx-restore-points');
        exit;
    }

    public function createDatabaseBackup(): void
    {
        $this->assertCanSave('shieldx_create_database_backup');

        $ajax = $this->isProgressiveScanAjax();
        $result = DatabaseBackupManager::fromWordPress()->startProgressiveBackup([], 20000);
        $backupId = (string) ($result['backup_id'] ?? '');

        if (empty($result['ok']) || (string) ($result['status'] ?? '') === 'failed' || $backupId === '') {
            NotificationManager::fromWordPress()->notify('backup.backup_failed', [
                'operation' => 'database_backup_create',
                'status' => (string) ($result['status'] ?? 'failed'),
                'error' => (string) ($result['message'] ?? __('Database backup failed.', 'xerotact-shield-x-security')),
            ], 'warning');

            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'error' => (string) ($result['message'] ?? __('Database backup failed.', 'xerotact-shield-x-security')),
                    'progress' => $this->progressiveBackupPayload($result),
                    'redirect_url' => admin_url('admin.php?page=shield-x-backup'),
                ], 400);
            }

            $query = ['shieldx_backup_error' => AdminFlashNotice::flag('shieldx_backup_error', (string) ($result['message'] ?? __('Database backup failed.', 'xerotact-shield-x-security')))];
            wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-backup')));
            exit;
        }

        if ($ajax) {
            $this->sendProgressiveScanJson([
                'ok' => true,
                'status' => (string) ($result['status'] ?? 'running'),
                'scan_id' => $backupId,
                'progress' => $this->progressiveScanPayload($result),
                'next_url' => $this->progressiveBackupUrl($backupId, true),
            ]);
        }

        wp_safe_redirect($this->progressiveBackupUrl($backupId));
        exit;
    }

    public function deleteDatabaseBackupsBefore(): void
    {
        $this->assertCanSave('shieldx_delete_database_backups_before');

        $backupManager = DatabaseBackupManager::fromWordPress();
        $beforeDate = $this->postedText('delete_before_date', '');
        if (!$backupManager->canDeleteBeforeDate($beforeDate)) {
            wp_safe_redirect(add_query_arg([
                'shieldx_backup_error' => AdminFlashNotice::flag('shieldx_backup_error', __('Choose a valid before date.', 'xerotact-shield-x-security')),
            ], admin_url('admin.php?page=shield-x-backup') . '#shieldx-database-backups'));
            exit;
        }

        // The cleanup safety copy stays local and must return before the admin request limit.
        $safetyBackup = $backupManager->create([], 20000, false, 20);
        $safetyBackupId = (string) ($safetyBackup['backup_id'] ?? '');
        if (empty($safetyBackup['ok']) || $safetyBackupId === '') {
            wp_safe_redirect(add_query_arg([
                'shieldx_backup_error' => AdminFlashNotice::flag('shieldx_backup_error', (string) ($safetyBackup['message'] ?? __('SHIELD-X could not create a safety database backup, so no old backups were deleted.', 'xerotact-shield-x-security'))),
            ], admin_url('admin.php?page=shield-x-backup') . '#shieldx-database-backups'));
            exit;
        }

        $result = $backupManager->deleteBeforeDate($beforeDate, $safetyBackupId);
        $deletedBackups = max(0, (int) ($result['deleted_backups'] ?? 0));
        $deletedFiles = max(0, (int) ($result['deleted_files'] ?? 0));
        $deletedBytes = max(0, (int) ($result['deleted_bytes'] ?? 0));
        $failedFiles = max(0, (int) ($result['failed_files'] ?? 0));

        if (empty($result['ok']) && $deletedBackups === 0 && $deletedFiles === 0 && $failedFiles === 0) {
            wp_safe_redirect(add_query_arg([
                'shieldx_backup_error' => AdminFlashNotice::flag('shieldx_backup_error', (string) ($result['message'] ?? __('Database backup cleanup failed.', 'xerotact-shield-x-security'))),
            ], admin_url('admin.php?page=shield-x-backup') . '#shieldx-database-backups'));
            exit;
        }

        wp_safe_redirect(add_query_arg([
            'shieldx_db_backups_cleaned' => '1',
            'shieldx_db_backups_deleted' => (string) $deletedBackups,
            'shieldx_db_backup_cleanup_files' => (string) $deletedFiles,
            'shieldx_db_backup_cleanup_bytes' => (string) $deletedBytes,
            'shieldx_db_backup_cleanup_failed' => (string) $failedFiles,
            'shieldx_db_backup_cleanup_before' => (string) ($result['before_date'] ?? ''),
            'shieldx_db_backup_cleanup_safety_backup' => $safetyBackupId,
        ], admin_url('admin.php?page=shield-x-backup') . '#shieldx-database-backups'));
        exit;
    }

    public function continueBackup(): void
    {
        $this->denyUnauthorizedProgressiveRequest();
        $ajax = $this->isProgressiveScanAjax();
        $backupId = strtolower($this->progressiveScanQueryText('backup_id'));
        if ($backupId === '' || preg_match('/^(restore-[0-9]{8}-[0-9]{6}-[a-f0-9]{32}|db-[0-9]{8}-[0-9]{6}-[a-f0-9]{32})$/', $backupId) !== 1) {
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'error' => __('The SHIELD-X backup session is invalid.', 'xerotact-shield-x-security'),
                    'redirect_url' => admin_url('admin.php?page=shield-x-backup'),
                ], 400);
            }

            wp_die(esc_html__('The SHIELD-X backup session is invalid.', 'xerotact-shield-x-security'));
        }

        $nonceAction = 'shieldx_continue_backup_' . $backupId;
        $nonce = $this->progressiveScanQueryText('_wpnonce');
        if (!$this->canSaveWithNonce($nonceAction, $nonce)) {
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'error' => __('The SHIELD-X backup session security check failed.', 'xerotact-shield-x-security'),
                    'redirect_url' => admin_url('admin.php?page=shield-x-backup'),
                ], 403);
            }

            $this->assertCanSaveWithNonce($nonceAction, $nonce);
        }

        $this->prepareLongAdminAction();
        $result = strpos($backupId, 'restore-') === 0
            ? RestorePointManager::fromWordPress()->runProgressiveCreateBatch($backupId)
            : DatabaseBackupManager::fromWordPress()->runProgressiveBackupBatch($backupId);
        $status = (string) ($result['status'] ?? 'failed');

        if ($status === 'completed') {
            if (strpos($backupId, 'restore-') === 0 && $this->restorePointNeedsGoogleUpload($result)) {
                $nextUrl = $this->progressiveRestorePointUploadUrl($backupId, $ajax);
                $progress = $this->restorePointGoogleUploadProgressPayload($result, 'running');

                if ($ajax) {
                    $this->sendProgressiveScanJson([
                        'ok' => true,
                        'status' => 'running',
                        'scan_id' => $backupId,
                        'progress' => $progress,
                        'next_url' => $nextUrl,
                    ]);
                }

                $this->renderProgressiveScanPage($progress, $nextUrl);
                exit;
            }

            if (strpos($backupId, 'restore-') === 0) {
                NotificationManager::fromWordPress()->notify('backup.restore_point_created', [
                    'operation' => 'restore_point_create',
                    'restore_id' => (string) ($result['restore_id'] ?? $backupId),
                    'status' => (string) ($result['status'] ?? 'completed'),
                    'files' => (int) ($result['files'] ?? 0),
                    'bytes_exported' => (int) ($result['bytes_exported'] ?? 0),
                    'database_backup_id' => (string) ($result['database_backup_id'] ?? ''),
                    'remote_status' => (string) ($result['remote_status'] ?? 'local'),
                ], 'info');
            }

            $redirectUrl = $this->progressiveBackupCompletionUrl($result, $backupId);

            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => true,
                    'status' => 'completed',
                    'scan_id' => $backupId,
                    'progress' => $this->progressiveBackupPayload($result),
                    'redirect_url' => $redirectUrl,
                ]);
            }

            wp_safe_redirect($redirectUrl);
            exit;
        }

        if ($status === 'failed') {
            NotificationManager::fromWordPress()->notify('backup.backup_failed', [
                'operation' => strpos($backupId, 'restore-') === 0 ? 'restore_point_create' : 'database_backup_create',
                'restore_id' => strpos($backupId, 'restore-') === 0 ? $backupId : '',
                'backup_id' => strpos($backupId, 'db-') === 0 ? $backupId : '',
                'status' => 'failed',
                'error' => (string) ($result['message'] ?? __('Backup failed.', 'xerotact-shield-x-security')),
            ], 'warning');

            $redirectUrl = add_query_arg([
                'shieldx_backup_error' => AdminFlashNotice::flag('shieldx_backup_error', (string) ($result['message'] ?? __('Backup failed.', 'xerotact-shield-x-security'))),
            ], admin_url('admin.php?page=shield-x-backup'));

            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'scan_id' => $backupId,
                    'error' => (string) ($result['message'] ?? __('Backup failed.', 'xerotact-shield-x-security')),
                    'progress' => $this->progressiveBackupPayload($result),
                    'redirect_url' => $redirectUrl,
                ]);
            }

            wp_safe_redirect($redirectUrl);
            exit;
        }

        $nextUrl = $this->progressiveBackupUrl($backupId, $ajax);
        if ($ajax) {
            $this->sendProgressiveScanJson([
                'ok' => true,
                'status' => 'running',
                'scan_id' => $backupId,
                'progress' => $this->progressiveBackupPayload($result),
                'next_url' => $nextUrl,
            ]);
        }

        $this->renderProgressiveScanPage($result, $nextUrl);
        exit;
    }

    public function uploadRestorePointGoogle(): void
    {
        $this->denyUnauthorizedProgressiveRequest();
        $ajax = $this->isProgressiveScanAjax();
        $restoreId = strtolower($this->progressiveScanQueryText('restore_id'));
        if ($restoreId === '' || preg_match('/^restore-[0-9]{8}-[0-9]{6}-[a-f0-9]{32}$/', $restoreId) !== 1) {
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'error' => __('The SHIELD-X restore point session is invalid.', 'xerotact-shield-x-security'),
                    'redirect_url' => admin_url('admin.php?page=shield-x-backup'),
                ], 400);
            }

            wp_die(esc_html__('The SHIELD-X restore point session is invalid.', 'xerotact-shield-x-security'));
        }

        $nonceAction = 'shieldx_upload_restore_point_google_' . $restoreId;
        $nonce = $this->progressiveScanQueryText('_wpnonce');
        if (!$this->canSaveWithNonce($nonceAction, $nonce)) {
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'error' => __('The SHIELD-X restore point upload security check failed.', 'xerotact-shield-x-security'),
                    'redirect_url' => admin_url('admin.php?page=shield-x-backup'),
                ], 403);
            }

            $this->assertCanSaveWithNonce($nonceAction, $nonce);
        }

        $this->prepareLongAdminAction();
        $restorePoints = RestorePointManager::fromWordPress();
        $localResult = $restorePoints->localRestorePointResult($restoreId);
        $redirectUrl = $this->progressiveBackupCompletionUrl($localResult, $restoreId);
        if (empty($localResult['ok'])) {
            $message = (string) ($localResult['message'] ?? __('Local restore point files were not found.', 'xerotact-shield-x-security'));
            NotificationManager::fromWordPress()->notify('backup.backup_failed', [
                'operation' => 'restore_point_google_upload',
                'restore_id' => $restoreId,
                'status' => 'failed',
                'error' => $message,
            ], 'warning');
            $redirectUrl = add_query_arg([
                'shieldx_restore_error' => AdminFlashNotice::flag('shieldx_restore_error', $message),
            ], admin_url('admin.php?page=shield-x-backup')) . '#shieldx-restore-points';

            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'scan_id' => $restoreId,
                    'error' => $message,
                    'progress' => $this->restorePointGoogleUploadProgressPayload($localResult, 'failed', $message),
                    'redirect_url' => $redirectUrl,
                ], 400);
            }

            wp_safe_redirect($redirectUrl);
            exit;
        }

        $storage = BackupStorageManager::fromWordPress();
        if (!$this->restorePointNeedsGoogleUpload($localResult)) {
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => true,
                    'status' => 'completed',
                    'scan_id' => $restoreId,
                    'progress' => $this->progressiveScanPayload($localResult),
                    'redirect_url' => $redirectUrl,
                ]);
            }

            wp_safe_redirect($redirectUrl);
            exit;
        }

        $upload = $storage->handleRestorePointCreated($localResult);
        $remoteStatus = (string) ($upload['remote_status'] ?? '');
        if (empty($upload['ok']) || $remoteStatus === 'failed') {
            $message = (string) ($upload['storage_message'] ?? $upload['message'] ?? __('Google Drive restore point upload failed. Local restore point was retained.', 'xerotact-shield-x-security'));
            NotificationManager::fromWordPress()->notify('backup.backup_failed', [
                'operation' => 'restore_point_google_upload',
                'restore_id' => $restoreId,
                'status' => 'failed',
                'remote_status' => $remoteStatus,
                'error' => $message,
            ], 'warning');
            $redirectUrl = add_query_arg([
                'shieldx_restore_error' => AdminFlashNotice::flag('shieldx_restore_error', $message),
            ], admin_url('admin.php?page=shield-x-backup')) . '#shieldx-restore-points';

            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'scan_id' => $restoreId,
                    'error' => $message,
                    'progress' => $this->restorePointGoogleUploadProgressPayload($upload, 'failed', $message),
                    'redirect_url' => $redirectUrl,
                ], 500);
            }

            wp_safe_redirect($redirectUrl);
            exit;
        }

        NotificationManager::fromWordPress()->notify('backup.restore_point_created', [
            'operation' => 'restore_point_create',
            'restore_id' => (string) ($upload['restore_id'] ?? $restoreId),
            'status' => (string) ($upload['status'] ?? 'completed'),
            'files' => (int) ($upload['files'] ?? 0),
            'bytes_exported' => (int) ($upload['bytes_exported'] ?? 0),
            'database_backup_id' => (string) ($upload['database_backup_id'] ?? ''),
            'remote_status' => $remoteStatus !== '' ? $remoteStatus : 'uploaded',
        ], 'info');

        $redirectUrl = $this->progressiveBackupCompletionUrl($upload, $restoreId);
        if ($ajax) {
            $this->sendProgressiveScanJson([
                'ok' => true,
                'status' => 'completed',
                'scan_id' => $restoreId,
                'progress' => $this->restorePointGoogleUploadProgressPayload($upload, 'completed'),
                'redirect_url' => $redirectUrl,
            ]);
        }

        wp_safe_redirect($redirectUrl);
        exit;
    }

    public function restoreManualDatabaseBackup(): void
    {
        $this->assertCanSave('shieldx_restore_manual_db_backup');

        $backupId = $this->postedText('backup_id', '');
        $backupManager = DatabaseBackupManager::fromWordPress();
        if (!$backupManager->backupById($backupId)) {
            wp_safe_redirect(add_query_arg(
                ['shieldx_backup_error' => AdminFlashNotice::flag('shieldx_backup_error', __('Database backup was not found.', 'xerotact-shield-x-security'))],
                admin_url('admin.php?page=shield-x-backup')
            ));
            exit;
        }

        $this->createPreChangeRestorePoint('pre_manual_database_restore');

        $result = $backupManager->restore($backupId);
        if (empty($result['ok'])) {
            NotificationManager::fromWordPress()->notify('backup.restore_failed', [
                'operation' => 'database_backup_restore',
                'backup_id' => $backupId,
                'status' => 'failed',
                'error' => (string) ($result['message'] ?? __('Database restore failed.', 'xerotact-shield-x-security')),
            ], 'warning');
        }

        $query = empty($result['ok'])
            ? ['shieldx_backup_error' => AdminFlashNotice::flag('shieldx_backup_error', (string) ($result['message'] ?? __('Database restore failed.', 'xerotact-shield-x-security')))]
            : ['shieldx_db_backup_restored' => (string) ($result['backup_id'] ?? '')];

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-backup')));
        exit;
    }

    public function restoreShadowCopy(): void
    {
        $this->assertCanSave('shieldx_restore_shadow_copy');

        $path = $this->postedRelativeFilePath('file_path');
        $this->createPreChangeRestorePoint('pre_shadow_restore', [$path]);

        $result = FileShadowCopyManager::fromWordPress()->restore(
            $path,
            function_exists('get_current_user_id') ? (int) get_current_user_id() : 0
        );

        if (!empty($result['ok'])) {
            ScannerRemediation::fromWordPress()->recheckFile($path, 'shieldx_restore_shadow_copy');
        }

        $query = empty($result['ok'])
            ? ['shieldx_backup_error' => AdminFlashNotice::flag('shieldx_backup_error', (string) ($result['message'] ?? __('Shadow copy restore failed.', 'xerotact-shield-x-security')))]
            : ['shieldx_shadow_restored' => '1', 'shieldx_action_path' => $path];

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-backup')));
        exit;
    }

    public function confirmShadowCopy(): void
    {
        $this->assertCanSave('shieldx_confirm_shadow_copy');

        $path = $this->postedRelativeFilePath('file_path');
        $result = ScannerRemediation::fromWordPress()->trustCurrentFile(
            $path,
            function_exists('get_current_user_id') ? (int) get_current_user_id() : 0,
            'shieldx_confirm_shadow_copy'
        );

        $query = empty($result['ok'])
            ? ['shieldx_backup_error' => AdminFlashNotice::flag('shieldx_backup_error', (string) ($result['message'] ?? __('Could not confirm the current file.', 'xerotact-shield-x-security')))]
            : ['shieldx_shadow_confirmed' => '1', 'shieldx_action_path' => $path];

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-backup')));
        exit;
    }

    public function confirmShadowSession(): void
    {
        $this->assertCanSave('shieldx_confirm_shadow_session');

        $scanId = strtolower(trim($this->postedText('shadow_scan', '')));
        if (preg_match('/^[a-f0-9]{32}$/', $scanId) !== 1) {
            wp_safe_redirect(add_query_arg([
                'shieldx_backup_error' => AdminFlashNotice::flag('shieldx_backup_error', __('Invalid shadow scan session.', 'xerotact-shield-x-security')),
            ], admin_url('admin.php?page=shield-x-backup')) . '#shieldx-file-shadow-copies');
            exit;
        }

        $scope = $this->postedText('confirm_scope', 'next') === 'all' ? 'all' : 'next';
        if ($scope === 'all') {
            $this->prepareLongAdminAction();
        }

        $shadowManager = FileShadowCopyManager::fromWordPress();
        $items = $scope === 'all'
            ? $shadowManager->allChangedForScan($scanId)
            : $shadowManager->changedForScan($scanId, 26);
        if ($scope !== 'all') {
            $items = array_slice($items, 0, 25);
        }

        $remediation = ScannerRemediation::fromWordPress();
        $remediation->useShadowCopyManager($shadowManager);
        $userId = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;
        $confirmed = 0;
        $failed = 0;

        if ($scope === 'all') {
            $result = $remediation->trustCurrentFiles(
                array_map(static function (array $item): string {
                    return (string) ($item['path'] ?? '');
                }, $items),
                $userId,
                'shieldx_confirm_shadow_session'
            );
            $confirmed = (int) ($result['confirmed_count'] ?? 0);
            $failed = (int) ($result['failed_count'] ?? count($items));
        } else {
            foreach ($items as $item) {
                $path = (string) ($item['path'] ?? '');
                if ($path === '') {
                    $failed++;
                    continue;
                }

                $result = $remediation->trustCurrentFile($path, $userId, 'shieldx_confirm_shadow_session');
                if (!empty($result['ok'])) {
                    $confirmed++;
                } else {
                    $failed++;
                }
            }
        }

        $remaining = count($shadowManager->allChangedForScan($scanId));

        wp_safe_redirect(add_query_arg([
            'shieldx_shadow_session_confirmed' => '1',
            'shieldx_shadow_ok' => (string) $confirmed,
            'shieldx_shadow_failed' => (string) $failed,
            'shieldx_shadow_more' => $remaining > 0 ? '1' : '0',
            'shieldx_shadow_scanned' => '1',
            'shieldx_shadow_scan' => $scanId,
            'shieldx_shadow_changes' => (string) $remaining,
        ], admin_url('admin.php?page=shield-x-backup')) . '#shieldx-file-shadow-copies');
        exit;
    }

    public function prepareCoreRelease(): void
    {
        $this->assertCanSave('shieldx_prepare_core_release');
        $this->prepareLongAdminAction();
        $ajax = $this->isProgressiveScanAjax();

        try {
            $job = CoreFileVerifier::fromWordPress()->startProgressivePrepare(true, 'shieldx_prepare_core_release');
            $jobId = (string) ($job['job_id'] ?? '');

            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => true,
                    'status' => (string) ($job['status'] ?? 'running'),
                    'scan_id' => $jobId,
                    'progress' => $this->progressiveScanPayload($job),
                    'next_url' => $this->progressiveCoreReleaseUrl($jobId, true),
                ]);
            }

            wp_safe_redirect($this->progressiveCoreReleaseUrl($jobId));
        } catch (\Throwable $error) {
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'error' => substr($error->getMessage(), 0, 180),
                    'redirect_url' => admin_url('admin.php?page=shield-x-scan#shieldx-file-scanner'),
                ], 400);
            }

            wp_safe_redirect(add_query_arg([
                'shieldx_core_error' => AdminFlashNotice::flag('shieldx_core_error', substr($error->getMessage(), 0, 180)),
            ], admin_url('admin.php?page=shield-x-scan')) . '#shieldx-file-scanner');
        }

        exit;
    }

    public function continueCoreRelease(): void
    {
        $this->denyUnauthorizedProgressiveRequest();
        $ajax = $this->isProgressiveScanAjax();
        $jobId = strtolower($this->progressiveScanQueryText('core_job_id'));
        if ($jobId === '' || preg_match('/^[a-f0-9]{32}$/', $jobId) !== 1) {
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'error' => __('The SHIELD-X official core session is invalid.', 'xerotact-shield-x-security'),
                    'redirect_url' => admin_url('admin.php?page=shield-x-scan#shieldx-file-scanner'),
                ], 400);
            }

            wp_die(esc_html__('The SHIELD-X official core session is invalid.', 'xerotact-shield-x-security'));
        }

        $nonceAction = 'shieldx_continue_core_release_' . $jobId;
        $nonce = $this->progressiveScanQueryText('_wpnonce');
        if (!$this->canSaveWithNonce($nonceAction, $nonce)) {
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'error' => __('The SHIELD-X official core session security check failed.', 'xerotact-shield-x-security'),
                    'redirect_url' => admin_url('admin.php?page=shield-x-scan#shieldx-file-scanner'),
                ], 403);
            }

            $this->assertCanSaveWithNonce($nonceAction, $nonce);
        }

        $this->prepareLongAdminAction();
        $result = CoreFileVerifier::fromWordPress()->runProgressivePrepareBatch($jobId, 250, 10, $nonceAction, $nonce);
        $status = (string) ($result['status'] ?? 'failed');

        if ($status === 'completed') {
            $redirectUrl = add_query_arg([
                'shieldx_core_ready' => '1',
            ], admin_url('admin.php?page=shield-x-scan')) . '#shieldx-file-scanner';

            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => true,
                    'status' => 'completed',
                    'scan_id' => $jobId,
                    'progress' => $this->progressiveScanPayload($result),
                    'redirect_url' => $redirectUrl,
                ]);
            }

            wp_safe_redirect($redirectUrl);
            exit;
        }

        if ($status === 'failed') {
            $message = (string) ($result['message'] ?? __('Official WordPress core refresh failed.', 'xerotact-shield-x-security'));
            $redirectUrl = add_query_arg([
                'shieldx_core_error' => AdminFlashNotice::flag('shieldx_core_error', $message),
            ], admin_url('admin.php?page=shield-x-scan')) . '#shieldx-file-scanner';

            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'scan_id' => $jobId,
                    'error' => $message,
                    'progress' => $this->progressiveScanPayload($result),
                    'redirect_url' => $redirectUrl,
                ]);
            }

            wp_safe_redirect($redirectUrl);
            exit;
        }

        $nextUrl = $this->progressiveCoreReleaseUrl($jobId, $ajax);

        if ($ajax) {
            $this->sendProgressiveScanJson([
                'ok' => true,
                'status' => 'running',
                'scan_id' => $jobId,
                'progress' => $this->progressiveScanPayload($result),
                'next_url' => $nextUrl,
            ]);
        }

        $this->renderProgressiveScanPage($result, $nextUrl);
        exit;
    }

    public function preparePluginPackages(): void
    {
        $this->assertCanSave('shieldx_prepare_plugin_packages');
        $this->prepareLongAdminAction();
        $ajax = $this->isProgressiveScanAjax();

        try {
            $job = PluginFileVerifier::fromWordPress()->startProgressivePrepare(true, 'shieldx_prepare_plugin_packages');
            $jobId = (string) ($job['job_id'] ?? '');

            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => true,
                    'status' => (string) ($job['status'] ?? 'running'),
                    'scan_id' => $jobId,
                    'progress' => $this->progressiveScanPayload($job),
                    'next_url' => $this->progressivePluginPackagesUrl($jobId, true),
                ]);
            }

            wp_safe_redirect($this->progressivePluginPackagesUrl($jobId));
        } catch (\Throwable $error) {
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'error' => substr($error->getMessage(), 0, 180),
                    'redirect_url' => admin_url('admin.php?page=shield-x-scan#shieldx-file-scanner'),
                ], 400);
            }

            wp_safe_redirect(add_query_arg([
                'shieldx_plugin_packages_error' => AdminFlashNotice::flag('shieldx_plugin_packages_error', substr($error->getMessage(), 0, 180)),
            ], admin_url('admin.php?page=shield-x-scan')) . '#shieldx-file-scanner');
        }

        exit;
    }

    public function continuePluginPackages(): void
    {
        $this->denyUnauthorizedProgressiveRequest();
        $ajax = $this->isProgressiveScanAjax();
        $jobId = strtolower($this->progressiveScanQueryText('plugin_job_id'));
        if ($jobId === '' || preg_match('/^[a-f0-9]{32}$/', $jobId) !== 1) {
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'error' => __('The SHIELD-X plugin package session is invalid.', 'xerotact-shield-x-security'),
                    'redirect_url' => admin_url('admin.php?page=shield-x-scan#shieldx-file-scanner'),
                ], 400);
            }

            wp_die(esc_html__('The SHIELD-X plugin package session is invalid.', 'xerotact-shield-x-security'));
        }

        $nonceAction = 'shieldx_continue_plugin_packages_' . $jobId;
        $nonce = $this->progressiveScanQueryText('_wpnonce');
        if (!$this->canSaveWithNonce($nonceAction, $nonce)) {
            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'error' => __('The SHIELD-X plugin package session security check failed.', 'xerotact-shield-x-security'),
                    'redirect_url' => admin_url('admin.php?page=shield-x-scan#shieldx-file-scanner'),
                ], 403);
            }

            $this->assertCanSaveWithNonce($nonceAction, $nonce);
        }

        $this->prepareLongAdminAction();
        $result = PluginFileVerifier::fromWordPress()->runProgressivePrepareBatch($jobId, 1, 10, $nonceAction, $nonce);
        $status = (string) ($result['status'] ?? 'failed');

        if ($status === 'completed') {
            $errors = isset($result['errors']) && is_array($result['errors']) ? count($result['errors']) : 0;
            $redirectUrl = add_query_arg([
                'shieldx_plugin_packages_ready' => '1',
                'shieldx_plugin_packages_prepared' => (string) ((int) ($result['prepared'] ?? 0)),
                'shieldx_plugin_packages_errors' => (string) ($errors + (int) ($result['skipped'] ?? 0)),
            ], admin_url('admin.php?page=shield-x-scan')) . '#shieldx-file-scanner';

            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => true,
                    'status' => 'completed',
                    'scan_id' => $jobId,
                    'progress' => $this->progressiveScanPayload($result),
                    'redirect_url' => $redirectUrl,
                ]);
            }

            wp_safe_redirect($redirectUrl);
            exit;
        }

        if ($status === 'failed') {
            $redirectUrl = add_query_arg([
                'shieldx_plugin_packages_error' => AdminFlashNotice::flag('shieldx_plugin_packages_error', (string) ($result['message'] ?? __('Official plugin package refresh failed.', 'xerotact-shield-x-security'))),
            ], admin_url('admin.php?page=shield-x-scan')) . '#shieldx-file-scanner';

            if ($ajax) {
                $this->sendProgressiveScanJson([
                    'ok' => false,
                    'status' => 'failed',
                    'scan_id' => $jobId,
                    'error' => (string) ($result['message'] ?? __('Official plugin package refresh failed.', 'xerotact-shield-x-security')),
                    'progress' => $this->progressiveScanPayload($result),
                    'redirect_url' => $redirectUrl,
                ]);
            }

            wp_safe_redirect($redirectUrl);
            exit;
        }

        $nextUrl = $this->progressivePluginPackagesUrl($jobId, $ajax);

        if ($ajax) {
            $this->sendProgressiveScanJson([
                'ok' => true,
                'status' => 'running',
                'scan_id' => $jobId,
                'progress' => $this->progressiveScanPayload($result),
                'next_url' => $nextUrl,
            ]);
        }

        $this->renderProgressiveScanPage($result, $nextUrl);
        exit;
    }

    public function uploadPluginPackage(): void
    {
        $this->assertCanSave('shieldx_upload_plugin_package');
        $redirect = $this->componentProvenanceRedirect();
        $redirectBase = $redirect['base'];
        $redirectAnchor = $redirect['anchor'];
        $slug = sanitize_key($this->postedText('plugin_slug', ''));
        $managedUploadPath = '';

        try {
            if ($slug === '') {
                throw new \InvalidArgumentException(__('Choose the plugin that this ZIP belongs to.', 'xerotact-shield-x-security'));
            }

            // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- assertCanSave() verified the nonce above; the file array is passed directly to WordPress upload handling below.
            $upload = isset($_FILES['plugin_package']) && is_array($_FILES['plugin_package']) ? $_FILES['plugin_package'] : [];
            $errorCode = isset($upload['error']) ? (int) $upload['error'] : UPLOAD_ERR_NO_FILE;
            if ($errorCode !== UPLOAD_ERR_OK) {
                throw new \RuntimeException($this->pluginPackageUploadErrorMessage($errorCode));
            }

            $originalNameRaw = isset($upload['name']) && is_scalar($upload['name']) ? (string) wp_unslash($upload['name']) : '';
            $originalName = function_exists('sanitize_file_name')
                ? sanitize_file_name($originalNameRaw)
                : sanitize_text_field($originalNameRaw);

            if ($originalName === '' || strtolower(pathinfo($originalName, PATHINFO_EXTENSION)) !== 'zip') {
                throw new \InvalidArgumentException(__('Upload a .zip plugin package.', 'xerotact-shield-x-security'));
            }

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

            $handledUpload = wp_handle_upload($upload, [
                'test_form' => false,
                'mimes' => ['zip' => 'application/zip'],
            ]);
            $managedUploadPath = is_array($handledUpload) && isset($handledUpload['file']) && is_scalar($handledUpload['file'])
                ? (string) $handledUpload['file']
                : '';
            $uploadError = is_array($handledUpload) && isset($handledUpload['error']) && is_scalar($handledUpload['error'])
                ? sanitize_text_field((string) $handledUpload['error'])
                : '';

            if ($uploadError !== '') {
                throw new \RuntimeException($uploadError);
            }

            if ($managedUploadPath === '' || !is_file($managedUploadPath) || !is_readable($managedUploadPath)) {
                throw new \InvalidArgumentException(__('The uploaded plugin package was not received by WordPress.', 'xerotact-shield-x-security'));
            }

            $entry = PluginFileVerifier::fromWordPress()->prepareUploadedPluginPackage($slug, $managedUploadPath, $originalName);
            $label = trim((string) ($entry['name'] ?? $slug) . ' ' . (string) ($entry['version'] ?? ''));
            $redirectUrl = add_query_arg([
                'shieldx_plugin_package_uploaded' => AdminFlashNotice::flag('shieldx_plugin_package_uploaded', $label !== '' ? $label : $slug),
            ], $redirectBase) . $redirectAnchor;
        } catch (\Throwable $error) {
            $redirectUrl = add_query_arg([
                'shieldx_plugin_package_upload_error' => AdminFlashNotice::flag('shieldx_plugin_package_upload_error', substr($error->getMessage(), 0, 180)),
            ], $redirectBase) . $redirectAnchor;
        } finally {
            if ($managedUploadPath !== '' && is_file($managedUploadPath)) {
                wp_delete_file($managedUploadPath);
            }
        }

        wp_safe_redirect($redirectUrl);
        exit;
    }

    public function trustInstalledPluginPackage(): void
    {
        $this->assertCanSave('shieldx_trust_installed_plugin');
        $redirect = $this->componentProvenanceRedirect();
        $redirectBase = $redirect['base'];
        $redirectAnchor = $redirect['anchor'];
        $slug = sanitize_key($this->postedText('plugin_slug', ''));

        try {
            if ($slug === '') {
                throw new \InvalidArgumentException(__('Choose the plugin to trust.', 'xerotact-shield-x-security'));
            }

            $entry = PluginFileVerifier::fromWordPress()->trustInstalledPluginVersion($slug);
            $label = trim((string) ($entry['name'] ?? $slug) . ' ' . (string) ($entry['version'] ?? ''));
            $redirectUrl = add_query_arg([
                'shieldx_plugin_package_trusted' => AdminFlashNotice::flag('shieldx_plugin_package_trusted', $label !== '' ? $label : $slug),
            ], $redirectBase) . $redirectAnchor;
        } catch (\Throwable $error) {
            $redirectUrl = add_query_arg([
                'shieldx_plugin_package_trust_error' => AdminFlashNotice::flag('shieldx_plugin_package_trust_error', substr($error->getMessage(), 0, 180)),
            ], $redirectBase) . $redirectAnchor;
        }

        wp_safe_redirect($redirectUrl);
        exit;
    }

    /**
     * @return array{base:string,anchor:string}
     */
    private function componentProvenanceRedirect(): array
    {
        if ($this->postedText('provenance_return', '') === 'system') {
            return [
                'base' => admin_url('admin.php?page=shield-x-system'),
                'anchor' => '#shieldx-component-provenance',
            ];
        }

        return [
            'base' => admin_url('admin.php?page=shield-x-scan'),
            'anchor' => '#shieldx-plugin-compare',
        ];
    }

    public function uploadThemePackage(): void
    {
        $this->assertCanSave('shieldx_upload_theme_package');
        $redirectBase = admin_url('admin.php?page=shield-x-system');
        $slug = sanitize_key($this->postedText('theme_slug', ''));
        $managedUploadPath = '';

        try {
            if ($slug === '') {
                throw new \InvalidArgumentException(__('Choose the theme that this ZIP belongs to.', 'xerotact-shield-x-security'));
            }

            // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- assertCanSave() verified the nonce above; WordPress validates and moves the uploaded file below.
            $upload = isset($_FILES['theme_package']) && is_array($_FILES['theme_package']) ? $_FILES['theme_package'] : [];
            $errorCode = isset($upload['error']) ? (int) $upload['error'] : UPLOAD_ERR_NO_FILE;
            if ($errorCode !== UPLOAD_ERR_OK) {
                throw new \RuntimeException($this->themePackageUploadErrorMessage($errorCode));
            }

            $originalNameRaw = isset($upload['name']) && is_scalar($upload['name']) ? (string) wp_unslash($upload['name']) : '';
            $originalName = function_exists('sanitize_file_name')
                ? sanitize_file_name($originalNameRaw)
                : sanitize_text_field($originalNameRaw);
            if ($originalName === '' || strtolower(pathinfo($originalName, PATHINFO_EXTENSION)) !== 'zip') {
                throw new \InvalidArgumentException(__('Upload a .zip theme package.', 'xerotact-shield-x-security'));
            }

            if (!function_exists('wp_handle_upload')) {
                require_once ABSPATH . 'wp-admin/includes/file.php';
            }
            $handledUpload = wp_handle_upload($upload, [
                'test_form' => false,
                'mimes' => ['zip' => 'application/zip'],
            ]);
            $managedUploadPath = is_array($handledUpload) && isset($handledUpload['file']) && is_scalar($handledUpload['file'])
                ? (string) $handledUpload['file']
                : '';
            $uploadError = is_array($handledUpload) && isset($handledUpload['error']) && is_scalar($handledUpload['error'])
                ? sanitize_text_field((string) $handledUpload['error'])
                : '';
            if ($uploadError !== '') {
                throw new \RuntimeException($uploadError);
            }
            if ($managedUploadPath === '' || !is_file($managedUploadPath) || !is_readable($managedUploadPath)) {
                throw new \InvalidArgumentException(__('The uploaded theme package was not received by WordPress.', 'xerotact-shield-x-security'));
            }
            if (!\ShieldX\Scanner\ComponentProvenance::fromWordPress()->trustThemePackage($slug, $managedUploadPath)) {
                throw new \RuntimeException(__('The theme ZIP does not match the installed theme slug and version.', 'xerotact-shield-x-security'));
            }

            $redirectUrl = add_query_arg([
                'shieldx_theme_package_uploaded' => AdminFlashNotice::flag('shieldx_theme_package_uploaded', $slug),
            ], $redirectBase) . '#shieldx-component-provenance';
        } catch (\Throwable $error) {
            $redirectUrl = add_query_arg([
                'shieldx_theme_package_upload_error' => AdminFlashNotice::flag('shieldx_theme_package_upload_error', substr($error->getMessage(), 0, 180)),
            ], $redirectBase) . '#shieldx-component-provenance';
        } finally {
            if ($managedUploadPath !== '' && is_file($managedUploadPath)) {
                wp_delete_file($managedUploadPath);
            }
        }

        wp_safe_redirect($redirectUrl);
        exit;
    }

    public function trustInstalledThemePackage(): void
    {
        $this->assertCanSave('shieldx_trust_installed_theme');
        $redirectBase = admin_url('admin.php?page=shield-x-system');
        $slug = sanitize_key($this->postedText('theme_slug', ''));

        try {
            if ($slug === '' || !\ShieldX\Scanner\ComponentProvenance::fromWordPress()->trustInstalledTheme($slug)) {
                throw new \InvalidArgumentException(__('Choose an installed theme to trust.', 'xerotact-shield-x-security'));
            }
            $redirectUrl = add_query_arg([
                'shieldx_theme_package_trusted' => AdminFlashNotice::flag('shieldx_theme_package_trusted', $slug),
            ], $redirectBase) . '#shieldx-component-provenance';
        } catch (\Throwable $error) {
            $redirectUrl = add_query_arg([
                'shieldx_theme_package_trust_error' => AdminFlashNotice::flag('shieldx_theme_package_trust_error', substr($error->getMessage(), 0, 180)),
            ], $redirectBase) . '#shieldx-component-provenance';
        }

        wp_safe_redirect($redirectUrl);
        exit;
    }

    private function pluginPackageUploadErrorMessage(int $errorCode): string
    {
        switch ($errorCode) {
            case UPLOAD_ERR_INI_SIZE:
            case UPLOAD_ERR_FORM_SIZE:
                return __('The uploaded plugin ZIP is larger than the server allows.', 'xerotact-shield-x-security');
            case UPLOAD_ERR_PARTIAL:
                return __('The plugin ZIP upload was incomplete. Try again.', 'xerotact-shield-x-security');
            case UPLOAD_ERR_NO_FILE:
                return __('Choose a plugin ZIP to upload.', 'xerotact-shield-x-security');
            default:
                return __('The plugin ZIP upload failed. Try again.', 'xerotact-shield-x-security');
        }
    }

    private function themePackageUploadErrorMessage(int $errorCode): string
    {
        switch ($errorCode) {
            case UPLOAD_ERR_INI_SIZE:
            case UPLOAD_ERR_FORM_SIZE:
                return __('The uploaded theme ZIP is larger than the server allows.', 'xerotact-shield-x-security');
            case UPLOAD_ERR_PARTIAL:
                return __('The theme ZIP upload was incomplete. Try again.', 'xerotact-shield-x-security');
            case UPLOAD_ERR_NO_FILE:
                return __('Choose a theme ZIP to upload.', 'xerotact-shield-x-security');
            default:
                return __('The theme ZIP upload failed. Try again.', 'xerotact-shield-x-security');
        }
    }

    public function restoreCoreFile(): void
    {
        $this->assertCanSave('shieldx_restore_core_file');

        $path = $this->postedFileRoutePath('core_file_path');

        try {
            $this->createPreChangeRestorePoint('pre_core_file_restore', [$path]);
            $verifier = CoreFileVerifier::fromWordPress();
            $verifier->prepareRelease();
            $restored = $verifier->replaceFile($path, 'shieldx_restore_core_file');
            $repository = ScanRepository::fromWordPress();

            $repository->recordFileBaseline([
                'path' => (string) ($restored['path'] ?? ''),
                'hash' => (string) ($restored['hash'] ?? ''),
                'size' => (int) ($restored['size'] ?? 0),
                'modified_at' => (string) ($restored['modified_at'] ?? current_time('mysql', true)),
                'plugin_slug' => '',
                'is_core' => true,
            ]);
            $repository->resolveVerifiedCoreFileIssues((string) ($restored['path'] ?? ''), function_exists('get_current_user_id') ? (int) get_current_user_id() : 0);
            $recheck = ScannerRemediation::fromWordPress()->recheckFile((string) ($restored['path'] ?? $path), 'shieldx_restore_core_file');

            $query = [
                'shieldx_core_restored' => '1',
                'shieldx_core_compare_token' => $this->fileRouteToken((string) ($restored['path'] ?? $path)),
                'shieldx_recheck_status' => (string) ($recheck['status'] ?? 'clean'),
                'shieldx_recheck_count' => (string) ((int) ($recheck['remaining_count'] ?? 0)),
            ];

            wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-scan')) . '#shieldx-core-compare');
        } catch (\Throwable $error) {
            wp_safe_redirect(add_query_arg([
                'shieldx_core_error' => AdminFlashNotice::flag('shieldx_core_error', substr($error->getMessage(), 0, 180)),
                'shieldx_core_compare_token' => $this->fileRouteToken($path),
            ], admin_url('admin.php?page=shield-x-scan')) . '#shieldx-core-compare');
        }

        exit;
    }

    public function quarantineScanIssue(): void
    {
        $this->assertCanSave('shieldx_quarantine_scan_issue');

        $threatId = $this->postedText('threat_id', '');
        $path = $this->issuePath($threatId);
        $remediation = ScannerRemediation::fromWordPress();
        if (!$remediation->hasExistingFile($path)) {
            $this->redirectScanRemediation($this->missingScannerFileResult($path), 'shieldx_quarantined');
        }
        $hashGate = $this->verifyAnalyzedScannerFileHash($path, $threatId);
        if (empty($hashGate['ok'])) {
            $this->redirectScanRemediation($hashGate, 'shieldx_quarantined');
        }

        $this->createPreChangeRestorePoint('pre_file_quarantine', [$path]);

        $result = $remediation->quarantine(
            $threatId,
            function_exists('get_current_user_id') ? (int) get_current_user_id() : 0
        );

        $this->redirectScanRemediation($result, 'shieldx_quarantined');
    }

    public function restoreQuarantinedIssue(): void
    {
        $this->assertCanSave('shieldx_restore_quarantined_issue');

        $result = ScannerRemediation::fromWordPress()->restore(
            $this->postedText('threat_id', ''),
            function_exists('get_current_user_id') ? (int) get_current_user_id() : 0
        );

        $this->redirectScanRemediation($result, 'shieldx_quarantine_restored');
    }

    public function ignoreScanIssue(): void
    {
        $this->assertCanSave('shieldx_ignore_scan_issue');

        $threatId = $this->postedText('threat_id', '');
        $hashGate = $this->verifyAnalyzedScannerFileHash($this->issuePath($threatId), $threatId);
        if (empty($hashGate['ok'])) {
            $this->redirectScanRemediation($hashGate, 'shieldx_ignored');
        }
        $result = ScannerRemediation::fromWordPress()->ignore(
            $threatId,
            $this->postedText('ignore_scope', 'once'),
            function_exists('get_current_user_id') ? (int) get_current_user_id() : 0
        );

        $this->redirectScanRemediation($result, 'shieldx_ignored');
    }

    public function removeScanIgnore(): void
    {
        $this->assertCanSave('shieldx_remove_scan_ignore');

        $result = ScannerRemediation::fromWordPress()->removeIgnore(
            $this->postedText('ignore_type', 'path'),
            $this->postedText('ignore_value', ''),
            function_exists('get_current_user_id') ? (int) get_current_user_id() : 0
        );

        $this->redirectScanRemediation($result, 'shieldx_ignore_removed');
    }

    public function bulkScanAction(): void
    {
        $this->assertCanSave('shieldx_bulk_scan_action');

        $action = $this->postedText('bulk_scan_action', 'recheck');
        $allowed = ['recheck', 'trust_current', 'ignore_once', 'ignore_file', 'ignore_rule', 'quarantine'];
        if (!in_array($action, $allowed, true)) {
            $action = 'recheck';
        }

        $threatIds = array_values(array_unique(array_filter(array_map('strval', $this->postedArray('bulk_threat_ids')))));
        $threatIds = array_slice($threatIds, 0, 50);
        if (empty($threatIds)) {
            wp_safe_redirect(add_query_arg([
                'shieldx_scan_action_error' => AdminFlashNotice::flag('shieldx_scan_action_error', __('Select at least one file scanner issue first.', 'xerotact-shield-x-security')),
            ], admin_url('admin.php?page=shield-x-scan')));
            exit;
        }

        $repository = ScanRepository::fromWordPress();
        $remediation = ScannerRemediation::fromWordPress();
        $userId = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;
        $paths = [];
        $issues = [];
        $missingFiles = 0;

        foreach ($threatIds as $threatId) {
            $issue = $repository->issueByThreatId($threatId);
            if (!is_array($issue)) {
                continue;
            }

            $path = (string) ($issue['affected_path'] ?? '');
            if ($path === '' || strpos($path, 'db:') === 0 || $this->isVirtualScannerPath($path)) {
                continue;
            }

            if (!$remediation->hasExistingFile($path)) {
                $missingFiles++;
            }

            $issues[] = $issue;
            $paths[] = $path;
        }

        if (empty($issues)) {
            wp_safe_redirect(add_query_arg([
                'shieldx_scan_action_error' => AdminFlashNotice::flag('shieldx_scan_action_error', __('Selected issues do not point to local files.', 'xerotact-shield-x-security')),
            ], admin_url('admin.php?page=shield-x-scan')));
            exit;
        }

        if ($missingFiles > 0 && in_array($action, ['recheck', 'trust_current', 'quarantine'], true)) {
            wp_safe_redirect(add_query_arg([
                'shieldx_scan_action_error' => AdminFlashNotice::flag(
                    'shieldx_scan_action_error',
                    sprintf(
                        /* translators: %d: number of missing files selected for a bulk scanner action. */
                        _n('%d selected file no longer exists. Use an ignore action or deselect it.', '%d selected files no longer exist. Use an ignore action or deselect them.', $missingFiles, 'xerotact-shield-x-security'),
                        $missingFiles
                    )
                ),
            ], admin_url('admin.php?page=shield-x-scan')));
            exit;
        }

        if (in_array($action, ['trust_current', 'quarantine'], true)) {
            $this->createPreChangeRestorePoint('pre_bulk_scan_' . $action, $paths);
        }

        $ok = 0;
        $failed = 0;

        foreach ($issues as $issue) {
            $threatId = (string) ($issue['threat_id'] ?? '');
            $path = (string) ($issue['affected_path'] ?? '');
            $result = ['ok' => false];

            if ($action === 'recheck') {
                $recheck = $remediation->recheckFile($path, 'shieldx_bulk_scan_action');
                $result['ok'] = (string) ($recheck['status'] ?? '') !== 'missing';
            } elseif ($action === 'trust_current') {
                $result = $remediation->trustCurrentFile($path, $userId, 'shieldx_bulk_scan_action');
            } elseif ($action === 'quarantine') {
                $result = $remediation->quarantine($threatId, $userId, 'shieldx_bulk_scan_action');
            } elseif ($action === 'ignore_once') {
                $result = $remediation->ignore($threatId, 'once', $userId, 'shieldx_bulk_scan_action');
            } elseif ($action === 'ignore_file') {
                $result = $remediation->ignore($threatId, 'file', $userId, 'shieldx_bulk_scan_action');
            } elseif ($action === 'ignore_rule') {
                $result = $remediation->ignore($threatId, 'rule', $userId, 'shieldx_bulk_scan_action');
            }

            if (!empty($result['ok'])) {
                $ok++;
            } else {
                $failed++;
            }
        }

        wp_safe_redirect(add_query_arg([
            'shieldx_bulk_action' => $action,
            'shieldx_bulk_ok' => (string) $ok,
            'shieldx_bulk_failed' => (string) $failed,
        ], admin_url('admin.php?page=shield-x-scan')));
        exit;
    }

    public function repairHtaccess(): void
    {
        $this->assertCanSave('shieldx_repair_htaccess');

        $this->createPreChangeRestorePoint('pre_htaccess_repair', ['.htaccess']);

        $result = ScannerRemediation::fromWordPress()->repairHtaccess(
            function_exists('get_current_user_id') ? (int) get_current_user_id() : 0
        );

        $this->redirectProtectedFileAction($result, 'shieldx_htaccess_repaired');
    }

    public function trustScannerFile(): void
    {
        $this->assertCanSave('shieldx_trust_scanner_file');

        $path = $this->postedFileRoutePath('file_path');
        $fromPluginCompare = $this->postedText('compare_context', '') === 'plugin_compare';
        $remediation = ScannerRemediation::fromWordPress();
        if (!$remediation->hasExistingFile($path)) {
            $result = $this->missingScannerFileResult($path);
            if ($fromPluginCompare) {
                $this->redirectPluginCompareRemediation($result, 'shieldx_file_trusted');
            }
            $this->redirectFileRemediation($result, 'shieldx_file_trusted');
        }
        $hashGate = $fromPluginCompare
            ? $this->verifyComparedPluginFileHash($path)
            : $this->verifyAnalyzedScannerFileHash($path, $this->postedText('threat_id', ''));
        if (empty($hashGate['ok'])) {
            if ($fromPluginCompare) {
                $this->redirectPluginCompareRemediation($hashGate, 'shieldx_file_trusted');
            }
            $this->redirectFileRemediation($hashGate, 'shieldx_file_trusted');
        }

        $result = $remediation->trustCurrentFile(
            $path,
            function_exists('get_current_user_id') ? (int) get_current_user_id() : 0
        );

        if ($fromPluginCompare) {
            $this->redirectPluginCompareRemediation($result, 'shieldx_file_trusted');
        }
        $this->redirectFileRemediation($result, 'shieldx_file_trusted');
    }

    public function saveComparedPluginFile(): void
    {
        $this->assertCanSave('shieldx_save_compared_plugin_file');

        $path = $this->postedFileRoutePath('file_path');
        $hashGate = $this->verifyComparedPluginFileHash($path);
        if (empty($hashGate['ok'])) {
            $this->redirectPluginCompareRemediation($hashGate, 'shieldx_file_saved');
        }

        $replacement = $this->postedRawText('replacement_content');
        $restorePoint = $this->createRequiredPreChangeRestorePoint('pre_plugin_compare_edit', [$path]);
        if (empty($restorePoint['ok'])) {
            $this->redirectPluginCompareRemediation([
                'ok' => false,
                'message' => (string) ($restorePoint['message'] ?? __('SHIELD-X could not create a restore point before saving this file.', 'xerotact-shield-x-security')),
                'path' => $path,
            ], 'shieldx_file_saved');
        }
        $result = ScannerRemediation::fromWordPress()->replaceCurrentTextFile(
            $path,
            $this->postedText('expected_file_hash', ''),
            $replacement,
            function_exists('get_current_user_id') ? (int) get_current_user_id() : 0
        );
        $result['restore_id'] = (string) ($restorePoint['restore_id'] ?? '');

        $this->redirectPluginCompareRemediation($result, 'shieldx_file_saved');
    }

    public function recheckScannerFile(): void
    {
        $this->assertCanSave('shieldx_recheck_scanner_file');

        $path = $this->postedFileRoutePath('file_path');
        $remediation = ScannerRemediation::fromWordPress();
        if (!$remediation->hasExistingFile($path)) {
            $this->redirectFileRemediation($this->missingScannerFileResult($path), 'shieldx_file_rechecked');
        }

        $recheck = $remediation->recheckFile($path);
        $ok = (string) ($recheck['status'] ?? '') !== 'missing';

        $result = [
            'ok' => $ok,
            'message' => $ok ? __('File was rechecked.', 'xerotact-shield-x-security') : __('The selected file could not be found for recheck.', 'xerotact-shield-x-security'),
            'path' => (string) ($recheck['path'] ?? $path),
            'recheck' => $recheck,
        ];

        $this->redirectFileRemediation($result, 'shieldx_file_rechecked');
    }

    public function cleanupConfigFile(): void
    {
        $this->assertCanSave('shieldx_cleanup_config_file');

        $path = $this->postedFileRoutePath('file_path');
        $this->createPreChangeRestorePoint('pre_config_cleanup', [$path]);

        $result = ScannerRemediation::fromWordPress()->cleanupProtectedConfig(
            $path,
            $this->postedArray('cleanup_lines'),
            function_exists('get_current_user_id') ? (int) get_current_user_id() : 0
        );

        $this->redirectProtectedFileAction($result, 'shieldx_config_cleaned');
    }

    public function remediateDatabaseIssue(): void
    {
        $this->assertCanSave('shieldx_remediate_db_issue');

        $this->createPreChangeRestorePoint('pre_database_remediation');

        $result = DatabaseRemediation::fromWordPress()->remediate(
            $this->postedText('threat_id', ''),
            $this->postedText('db_action', ''),
            function_exists('get_current_user_id') ? (int) get_current_user_id() : 0
        );

        $this->redirectScanRemediation($result, 'shieldx_db_remediated');
    }

    public function restoreDatabaseBackup(): void
    {
        $this->assertCanSave('shieldx_restore_db_backup');

        $this->createPreChangeRestorePoint('pre_database_backup_restore');

        $result = DatabaseRemediation::fromWordPress()->restoreBackup(
            $this->postedText('backup_id', ''),
            function_exists('get_current_user_id') ? (int) get_current_user_id() : 0
        );

        $this->redirectScanRemediation($result, 'shieldx_db_restored');
    }

    public function recheckDatabaseIssue(): void
    {
        $this->assertCanSave('shieldx_recheck_db_issue');

        $threatId = $this->postedText('threat_id', '');
        $result = DatabaseRemediation::fromWordPress()->recheckIssue($threatId);

        if (!empty($result['ok']) && $threatId !== '') {
            $result['review_threat_id'] = $threatId;
        }

        $this->redirectScanRemediation($result, 'shieldx_db_rechecked');
    }

    public function saveLoginSecuritySettings(): void
    {
        $this->assertCanSave('shieldx_save_login_security');

        $section = $this->postedText('shieldx_login_settings_section', 'policy');
        if ($section === 'gate') {
            $this->saveLoginEntryGateSettings();
            $this->redirectAfterSave('shield-x-login-security');
        }

        if ($section === 'lockout') {
            $this->saveLoginLockoutSettings();
            $this->redirectAfterSave('shield-x-login-security');
        }

        if ($section === 'challenge') {
            $this->saveHumanChallengeSettingsFromPost(['login']);
            $this->redirectAfterSave('shield-x-login-security');
        }

        $settings = LoginSecuritySettings::fromWordPress();
        $previousRequiredRoles = $settings->twoFactorRequiredRoles();
        $loginSecurityEnabled = $this->postFlag('login_security_enabled');
        $requiredRoles = $this->postedArray('two_factor_required_roles');
        $sessionRoles = $this->postedArray('session_protected_roles');
        $recoveryEmail = $this->postedText('recovery_email', $settings->recoveryEmail());
        $adminEmail = function_exists('get_option') ? (string) get_option('admin_email', '') : '';
        if (function_exists('sanitize_email')) {
            $adminEmail = sanitize_email($adminEmail);
        }
        $args = ['shieldx_saved' => '1'];

        if ($recoveryEmail !== '' && $adminEmail !== '' && strcasecmp($recoveryEmail, $adminEmail) === 0) {
            $recoveryEmail = $settings->recoveryEmail();
            $args['shieldx_login_security_error'] = AdminFlashNotice::flag('shieldx_login_security_error', __('Use a recovery email that is separate from the WordPress admin email.', 'xerotact-shield-x-security'));
        }

        $settings->save([
            'enabled' => $loginSecurityEnabled,
            'two_factor_required_roles' => $requiredRoles,
            'session_policy_enabled' => $this->postFlag('session_policy_enabled'),
            'session_protected_roles' => $sessionRoles,
            'session_maximum_age' => $this->postedInt('session_maximum_age_days', 7) * 86400,
            'session_idle_timeout' => $this->postedInt('session_idle_timeout_minutes', 120) * 60,
            'magic_login_enabled' => $this->postFlag('magic_login_enabled'),
            'recovery_email' => $recoveryEmail,
        ]);

        if ($this->postFlag('send_recovery_email')) {
            $result = (new LoginSecurity($settings))->sendOwnerRecoveryEmail(true, 'manual');
            $args[!empty($result['sent']) ? 'shieldx_email_sent' : 'shieldx_email_failed'] = '1';
        }

        if ($loginSecurityEnabled && $settings->twoFactorRequiredRoles() !== [] && ($previousRequiredRoles !== $settings->twoFactorRequiredRoles() || $this->postFlag('send_2fa_setup_emails'))) {
            $result = (new LoginSecurity($settings))->sendTwoFactorSetupEmailsToPendingUsers();
            $args['shieldx_2fa_invites_sent'] = (string) (int) $result['sent'];
            $args['shieldx_2fa_invites_failed'] = (string) (int) $result['failed'];
            $args['shieldx_2fa_invites_total'] = (string) (int) $result['total'];
        }

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($args), admin_url('admin.php?page=shield-x-login-security')));
        exit;
    }

    public function revokeLoginSessions(): void
    {
        $this->assertCanSave('shieldx_revoke_login_sessions');

        $userId = $this->postedInt('user_id', 0);
        $currentUserId = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;
        $security = LoginSecurity::fromWordPress();
        $ok = $userId === $currentUserId
            ? $security->revokeOtherSessions()
            : $security->revokeAllSessionsForUser($userId);

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery([
            $ok ? 'shieldx_sessions_revoked' : 'shieldx_sessions_revoke_failed' => '1',
        ]), admin_url('admin.php?page=shield-x-login-security#shieldx-session-review')));
        exit;
    }

    private function saveLoginEntryGateSettings(): void
    {
        $hardeningSettings = HardeningSettings::fromWordPress();
        $current = $hardeningSettings->all();
        $adminGateEnabled = $this->postFlag('admin_gate_enabled');
        $adminGateKey = $this->postedText('admin_gate_key', $hardeningSettings->adminGateKey());

        if (!HardeningSettings::isValidAdminGateKey($adminGateKey)) {
            wp_safe_redirect(add_query_arg([
                'shieldx_login_security_error' => AdminFlashNotice::flag('shieldx_login_security_error', __('Choose an admin gate keyword with 3-48 lowercase letters, numbers, dashes, or underscores.', 'xerotact-shield-x-security')),
            ], admin_url('admin.php?page=shield-x-login-security#shieldx-login-entry-gate')));
            exit;
        }

        if ($adminGateEnabled && HardeningSettings::isReservedAdminGateKey($adminGateKey)) {
            wp_safe_redirect(add_query_arg([
                'shieldx_login_security_error' => AdminFlashNotice::flag('shieldx_login_security_error', __('Choose a unique admin gate keyword before enabling the admin gate. The default "secretpath" is not allowed.', 'xerotact-shield-x-security')),
            ], admin_url('admin.php?page=shield-x-login-security#shieldx-login-entry-gate')));
            exit;
        }

        $hardeningSettings->save(
            (bool) ($current['hide_wordpress_signature'] ?? true),
            $adminGateEnabled,
            $adminGateKey,
            (bool) ($current['force_ssl_enabled'] ?? false),
            (bool) ($current['disable_file_edit'] ?? true),
            isset($current['response_headers']) && is_array($current['response_headers'])
                ? $current['response_headers']
                : HardeningSettings::defaultResponseHeaders(),
            [
                'generic_login_errors' => (bool) ($current['generic_login_errors'] ?? false),
                'block_xmlrpc' => (bool) ($current['block_xmlrpc'] ?? false),
                'disable_pingbacks' => (bool) ($current['disable_pingbacks'] ?? false),
                'disable_admin_scripts_concatenation' => (bool) ($current['disable_admin_scripts_concatenation'] ?? false),
                'block_author_scans' => (bool) ($current['block_author_scans'] ?? false),
                'bot_protection_enabled' => (bool) ($current['bot_protection_enabled'] ?? false),
                'direct_core_access_protection' => (bool) ($current['direct_core_access_protection'] ?? false),
                'sensitive_file_protection' => (bool) ($current['sensitive_file_protection'] ?? false),
                'directory_browsing_protection' => (bool) ($current['directory_browsing_protection'] ?? false),
                'data_php_execution_protection' => (bool) ($current['data_php_execution_protection'] ?? false),
                'admin_gate_strict_cookie' => $this->postFlag('admin_gate_strict_cookie'),
            ]
        );

        if ($hardeningSettings->adminGateEnabled()) {
            Hardening::setAccessCookie($hardeningSettings);
        } else {
            Hardening::clearAccessCookie();
        }
    }

    private function saveLoginLockoutSettings(): void
    {
        $rateLimitSettings = RateLimitSettings::fromWordPress();
        $current = $rateLimitSettings->all();
        $rateLimitSettings->save([
            'request_rate_enabled' => (bool) $current['request_rate_enabled'],
            'request_rate_mode' => (string) $current['request_rate_mode'],
            'request_limit' => (int) $current['request_limit'],
            'request_window_seconds' => (int) $current['request_window_seconds'],
            'burst_limit' => (int) $current['burst_limit'],
            'burst_window_seconds' => (int) $current['burst_window_seconds'],
            'login_protection_enabled' => $this->postFlag('login_protection_enabled'),
            'login_failure_limit' => $this->postedInt('login_failure_limit', (int) $current['login_failure_limit']),
            'login_window_seconds' => $this->postedInt('login_window_seconds', (int) $current['login_window_seconds']),
            'login_lockout_seconds' => $this->postedInt('login_lockout_seconds', (int) $current['login_lockout_seconds']),
            'endpoint_profiles' => (array) ($current['endpoint_profiles'] ?? []),
        ]);
    }

    public function regenerateEmergencyRecoveryUrl(): void
    {
        $this->assertCanSave('shieldx_regenerate_emergency_recovery_url');

        $settings = LoginSecuritySettings::fromWordPress();
        $token = $settings->rotateEmergencyRecoveryToken();
        $this->storeRevealedEmergencyRecoveryToken($token);

        wp_safe_redirect(add_query_arg([
            'shieldx_recovery_rotated' => '1',
            'shieldx_recovery_revealed' => '1',
        ], admin_url('admin.php?page=shield-x')));
        exit;
    }

    public function revealEmergencyRecoveryUrl(): void
    {
        $this->assertCanSave('shieldx_reveal_emergency_recovery_url');

        $settings = LoginSecuritySettings::fromWordPress();
        $token = $settings->ensureEmergencyRecoveryToken();
        $this->storeRevealedEmergencyRecoveryToken($token);

        wp_safe_redirect(add_query_arg([
            'shieldx_recovery_revealed' => '1',
        ], admin_url('admin.php?page=shield-x')));
        exit;
    }

    public function enableTwoFactor(): void
    {
        $this->assertCanSave('shieldx_enable_2fa');

        $userId = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;
        $security = LoginSecurity::fromWordPress();
        $result = $security->enableForUser($userId, $this->postedText('totp_code', ''));

        if (empty($result['ok'])) {
            wp_safe_redirect(add_query_arg([
                'shieldx_2fa_error' => AdminFlashNotice::flag('shieldx_2fa_error', (string) ($result['error'] ?? __('Unable to enable 2FA.', 'xerotact-shield-x-security'))),
            ], admin_url('admin.php?page=shield-x-login-security')));
            exit;
        }

        $codesId = $security->storeCodesForDisplay($userId, (array) ($result['codes'] ?? []));
        $emailResult = $security->sendRecoveryCodesEmail($userId, (array) ($result['codes'] ?? []));
        $args = [
            'shieldx_2fa_enabled' => '1',
            'shieldx_codes' => $codesId,
        ];
        $args[!empty($emailResult['sent']) ? 'shieldx_codes_email_sent' : 'shieldx_codes_email_failed'] = '1';

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($args), admin_url('admin.php?page=shield-x-login-security')));
        exit;
    }

    public function regenerateTwoFactorCodes(): void
    {
        $this->assertCanSave('shieldx_regenerate_2fa_codes');

        $userId = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;
        $security = LoginSecurity::fromWordPress();
        $codes = $security->regenerateRecoveryCodes($userId);
        $codesId = $security->storeCodesForDisplay($userId, $codes);

        wp_safe_redirect(add_query_arg([
            'shieldx_codes_regenerated' => '1',
            'shieldx_codes' => $codesId,
        ], admin_url('admin.php?page=shield-x-login-security')));
        exit;
    }

    public function disableTwoFactor(): void
    {
        $this->assertCanSave('shieldx_disable_2fa');

        $userId = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;
        $password = $this->postedText('shieldx_current_password', '');

        if (!$this->currentUserPasswordMatches($userId, $password)) {
            $message = $password === ''
                ? __('Enter the current administrator password to disable SHIELD-X 2FA.', 'xerotact-shield-x-security')
                : __('The administrator password did not match. 2FA is still enabled.', 'xerotact-shield-x-security');

            wp_safe_redirect(add_query_arg([
                'shieldx_2fa_error' => AdminFlashNotice::flag('shieldx_2fa_error', $message),
            ], admin_url('admin.php?page=shield-x-login-security')));
            exit;
        }

        LoginSecurity::fromWordPress()->disableForUser($userId);

        wp_safe_redirect(add_query_arg([
            'shieldx_2fa_disabled' => '1',
        ], admin_url('admin.php?page=shield-x-login-security')));
        exit;
    }

    public function disableAdminTwoFactor(): void
    {
        $targetUserId = $this->postedInt('shieldx_target_user_id', 0);
        $this->assertCanSave('shieldx_disable_admin_2fa_' . (string) $targetUserId);

        $currentUserId = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;
        if ($targetUserId <= 0) {
            $this->redirectAdminTwoFactorError(__('No matching SHIELD-X administrator with 2FA was found.', 'xerotact-shield-x-security'));
        }

        if ($currentUserId > 0 && $targetUserId === $currentUserId) {
            $this->redirectAdminTwoFactorError(__('Enter the current administrator password to disable SHIELD-X 2FA.', 'xerotact-shield-x-security'));
        }

        if (!$this->targetUserCanManageOptions($targetUserId)) {
            $this->redirectAdminTwoFactorError(__('No matching SHIELD-X administrator with 2FA was found.', 'xerotact-shield-x-security'));
        }

        if (!$this->currentUserCanEditTargetUser($targetUserId)) {
            $this->redirectAdminTwoFactorError(__('You do not have permission to change 2FA for this administrator.', 'xerotact-shield-x-security'));
        }

        $security = LoginSecurity::fromWordPress();
        if (!$security->isTwoFactorEnabled($targetUserId)) {
            $this->redirectAdminTwoFactorError(__('SHIELD-X 2FA is not enabled for this account.', 'xerotact-shield-x-security'));
        }

        $security->disableForUser($targetUserId);

        wp_safe_redirect(add_query_arg([
            'shieldx_admin_2fa_disabled' => '1',
        ], admin_url('admin.php?page=shield-x-login-security')) . '#shieldx-admin-users');
        exit;
    }

    public function requestTwoFactorReset(): void
    {
        $this->assertCanSave('shieldx_request_2fa_reset');

        $userId = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;
        $password = $this->postedText('shieldx_current_password', '');

        if (!$this->currentUserPasswordMatches($userId, $password)) {
            $message = $password === ''
                ? __('Enter the current administrator password to email a SHIELD-X 2FA reset code.', 'xerotact-shield-x-security')
                : __('The administrator password did not match. No SHIELD-X 2FA reset code was sent.', 'xerotact-shield-x-security');

            $this->redirectLoginSecurityWithError($message);
        }

        $result = LoginSecurity::fromWordPress()->requestTwoFactorResetCode($userId);
        if (empty($result['sent'])) {
            $this->redirectLoginSecurityWithError((string) ($result['error'] ?? __('SHIELD-X could not send the 2FA reset code.', 'xerotact-shield-x-security')));
        }

        wp_safe_redirect(add_query_arg([
            'shieldx_2fa_reset_sent' => '1',
        ], admin_url('admin.php?page=shield-x-login-security')) . '#shieldx-authenticator-2fa');
        exit;
    }

    public function confirmTwoFactorReset(): void
    {
        $this->assertCanSave('shieldx_confirm_2fa_reset');

        $userId = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;
        $result = LoginSecurity::fromWordPress()->confirmTwoFactorResetCode($userId, $this->postedText('shieldx_reset_code', ''));

        if (empty($result['ok'])) {
            $this->redirectLoginSecurityWithError((string) ($result['error'] ?? __('The SHIELD-X 2FA reset code could not be confirmed.', 'xerotact-shield-x-security')));
        }

        wp_safe_redirect(add_query_arg([
            'shieldx_2fa_disabled' => '1',
            'shieldx_2fa_reset_confirmed' => '1',
        ], admin_url('admin.php?page=shield-x-login-security')) . '#shieldx-authenticator-2fa');
        exit;
    }

    private function currentUserPasswordMatches(int $userId, string $password): bool
    {
        if ($userId <= 0 || $password === '' || !function_exists('get_userdata') || !function_exists('wp_check_password')) {
            return false;
        }

        $user = get_userdata($userId);
        if (!is_object($user) || !isset($user->user_pass)) {
            return false;
        }

        return wp_check_password($password, (string) $user->user_pass, $userId);
    }

    private function redirectLoginSecurityWithError(string $message): void
    {
        wp_safe_redirect(add_query_arg([
            'shieldx_2fa_error' => AdminFlashNotice::flag('shieldx_2fa_error', $message),
        ], admin_url('admin.php?page=shield-x-login-security')) . '#shieldx-authenticator-2fa');
        exit;
    }

    private function redirectAdminTwoFactorError(string $message): void
    {
        wp_safe_redirect(add_query_arg([
            'shieldx_admin_2fa_error' => AdminFlashNotice::flag('shieldx_admin_2fa_error', $message),
        ], admin_url('admin.php?page=shield-x-login-security')) . '#shieldx-admin-users');
        exit;
    }

    /**
     * @return array<string,mixed>
     */
    private function statusForPage(): array
    {
        if (!self::currentUserCanManageShieldX()) {
            wp_die(esc_html__('You do not have permission to access SHIELD-X.', 'xerotact-shield-x-security'));
        }

        return $this->plugin->adminStatus($this->queryKey('page', 'shield-x'));
    }

    /**
     * @param array<string,mixed> $status
     */
    private function fileEditConfigLabel(array $status): string
    {
        if (empty($status['exists'])) {
            return __('wp-config.php was not found; runtime protection is used while SHIELD-X is active.', 'xerotact-shield-x-security');
        }

        if (!empty($status['configured'])) {
            return __('wp-config.php line detected.', 'xerotact-shield-x-security');
        }

        if (empty($status['writable'])) {
            return __('wp-config.php line is missing and the file is not writable.', 'xerotact-shield-x-security');
        }

        return __('wp-config.php line is missing; saving will add it.', 'xerotact-shield-x-security');
    }

    /**
     * @param array<string,mixed> $status
     */
    private function openPage(string $activePage, array $status, bool $lightweight = false): void
    {
        if ($this->renderingAjaxContent) {
            return;
        }

        $settingsSaved = $this->queryFlag('shieldx_saved');
        $wafError = $this->queryText('shieldx_waf_error');
        $wafAccessUpdated = $this->queryFlag('shieldx_waf_access_updated');
        $twoFactorEnabled = $this->queryFlag('shieldx_2fa_enabled');
        $twoFactorDisabled = $this->queryFlag('shieldx_2fa_disabled');
        $adminTwoFactorDisabled = $this->queryFlag('shieldx_admin_2fa_disabled');
        $adminTwoFactorError = $this->queryText('shieldx_admin_2fa_error');
        $twoFactorResetSent = $this->queryFlag('shieldx_2fa_reset_sent');
        $twoFactorResetConfirmed = $this->queryFlag('shieldx_2fa_reset_confirmed');
        $codesRegenerated = $this->queryFlag('shieldx_codes_regenerated');
        $codesEmailSent = $this->queryFlag('shieldx_codes_email_sent');
        $codesEmailFailed = $this->queryFlag('shieldx_codes_email_failed');
        $recoveryEmailSent = $this->queryFlag('shieldx_email_sent');
        $recoveryEmailFailed = $this->queryFlag('shieldx_email_failed');
        $twoFactorInvitesSent = $this->queryInt('shieldx_2fa_invites_sent', 0);
        $twoFactorInvitesFailed = $this->queryInt('shieldx_2fa_invites_failed', 0);
        $twoFactorInvitesTotal = $this->queryInt('shieldx_2fa_invites_total', 0);
        $twoFactorInvitesChecked = $this->queryRaw('shieldx_2fa_invites_total') !== null;
        $scanCompleted = $this->queryFlag('shieldx_scanned');
        $shadowScanCompleted = $this->queryFlag('shieldx_shadow_scanned');
        $shadowScanChanges = $this->queryInt('shieldx_shadow_changes', 0);
        $shadowSessionConfirmed = $this->queryFlag('shieldx_shadow_session_confirmed');
        if ($shadowScanCompleted && !$shadowSessionConfirmed && $activePage === 'shield-x-backup') {
            $noticeShadowScanId = strtolower(trim($this->queryText('shieldx_shadow_scan')));
            if (preg_match('/^[a-f0-9]{32}$/', $noticeShadowScanId) === 1) {
                $shadowScanChanges = count(FileShadowCopyManager::fromWordPress()->allChangedForScan($noticeShadowScanId));
            }
        }
        $scheduleSaved = $this->queryKey('shieldx_schedule_saved');
        $backupScheduleSaved = $this->queryFlag('shieldx_backup_schedule_saved');
        $restorePointScheduleSaved = $this->queryFlag('shieldx_restore_point_schedule_saved');
        $vulnerabilityScanCompleted = $this->queryFlag('shieldx_vuln_scanned');
        $coreReady = $this->queryFlag('shieldx_core_ready');
        $pluginPackagesReady = $this->queryFlag('shieldx_plugin_packages_ready');
        $pluginPackagesPrepared = $this->queryInt('shieldx_plugin_packages_prepared', 0);
        $pluginPackagesErrors = $this->queryInt('shieldx_plugin_packages_errors', 0);
        $pluginPackagesError = $this->queryText('shieldx_plugin_packages_error');
        $pluginPackageUploaded = $this->queryText('shieldx_plugin_package_uploaded');
        $pluginPackageUploadError = $this->queryText('shieldx_plugin_package_upload_error');
        $pluginPackageTrusted = $this->queryText('shieldx_plugin_package_trusted');
        $pluginPackageTrustError = $this->queryText('shieldx_plugin_package_trust_error');
        $coreRestored = $this->queryFlag('shieldx_core_restored');
        $coreError = $this->queryText('shieldx_core_error');
        $scanQuarantined = $this->queryFlag('shieldx_quarantined');
        $scanQuarantineRestored = $this->queryFlag('shieldx_quarantine_restored');
        $scanIgnored = $this->queryFlag('shieldx_ignored');
        $scanIgnoreRemoved = $this->queryFlag('shieldx_ignore_removed');
        $htaccessRepaired = $this->queryFlag('shieldx_htaccess_repaired');
        $fileTrusted = $this->queryFlag('shieldx_file_trusted');
        $fileRechecked = $this->queryFlag('shieldx_file_rechecked');
        $fileSaved = $this->queryFlag('shieldx_file_saved');
        $configCleaned = $this->queryFlag('shieldx_config_cleaned');
        $databaseRemediated = $this->queryFlag('shieldx_db_remediated');
        $databaseRestored = $this->queryFlag('shieldx_db_restored');
        $databaseRechecked = $this->queryFlag('shieldx_db_rechecked');
        $databaseRecheckStatus = $this->queryKey('shieldx_recheck_status');
        $databaseRecheckCount = $this->queryInt('shieldx_recheck_count', 0);
        $configRemovedLines = $this->queryInt('shieldx_removed_lines', 0);
        $bulkAction = $this->queryKey('shieldx_bulk_action');
        $bulkOk = $this->queryInt('shieldx_bulk_ok', 0);
        $bulkFailed = $this->queryInt('shieldx_bulk_failed', 0);
        $scanActionError = $this->queryText('shieldx_scan_action_error');
        $lockdownPreview = $this->queryFlag('shieldx_lockdown_preview');
        $lockdownApplied = $this->queryFlag('shieldx_lockdown_applied');
        $lockdownReverted = $this->queryFlag('shieldx_lockdown_reverted');
        $lockdownForceReverted = $this->queryFlag('shieldx_lockdown_force_reverted');
        $lockdownError = $this->queryText('shieldx_lockdown_error');
        $lockdownWarning = $this->queryText('shieldx_lockdown_warning');
        $permissionFixed = $this->queryFlag('shieldx_permission_fixed');
        $permissionFiles = $this->queryInt('shieldx_permission_files', 0);
        $permissionDirectories = $this->queryInt('shieldx_permission_dirs', 0);
        $permissionUnchanged = $this->queryInt('shieldx_permission_unchanged', 0);
        $permissionSkipped = $this->queryInt('shieldx_permission_skipped', 0);
        $permissionFailed = $this->queryInt('shieldx_permission_failed', 0);
        $permissionError = $this->queryText('shieldx_permission_error');
        $hardeningError = '';
        if ($this->queryRaw('shieldx_hardening_error') === '1') {
            $hardeningError = $this->queryText('shieldx_hardening_error');
            if ($hardeningError === '') {
                $hardeningError = __(
                    'Hardening filesystem rules could not be updated. Review the current configuration and try again.',
                    'xerotact-shield-x-security'
                );
            }
        }
        $recoveryRotated = $this->queryFlag('shieldx_recovery_rotated');
        $settingsImported = $this->queryFlag('shieldx_imported');
        $settingsImportModules = $this->queryText('shieldx_import_modules');
        $settingsImportError = $this->queryText('shieldx_import_error');
        $notificationsSaved = $this->queryFlag('shieldx_notifications_saved');
        $licenseActivated = $this->queryFlag('shieldx_license_activated');
        $licenseChecked = $this->queryFlag('shieldx_license_checked');
        $licenseDeactivated = $this->queryFlag('shieldx_license_deactivated');
        $licenseError = $this->queryText('shieldx_license_error');
        $activityBlocked = $this->queryKey('shieldx_blocked');
        $activityBlockError = $this->queryKey('shieldx_block_error');
        $logRotationSaved = $this->queryFlag('shieldx_log_rotation_saved');
        $uninstallSettingsSaved = $this->queryFlag('shieldx_uninstall_settings_saved');
        $logRotationRun = $this->queryFlag('shieldx_log_rotation_run');
        $logRotationStatus = $this->queryText('shieldx_log_rotation_status', 'completed');
        $logRotationFailedFiles = $this->queryInt('shieldx_log_rotation_failed_files', 0);
        $logRotationDeletedFiles = $this->queryInt('shieldx_log_rotation_deleted_files', 0);
        $logRotationDeletedRows = $this->queryInt('shieldx_log_rotation_deleted_rows', 0);
        $logRotationScanned = $this->queryFlag('shieldx_log_scan');
        $logRotationScanMarked = $this->queryInt('shieldx_log_scan_marked', 0);
        $logRotationScanTotal = $this->queryInt('shieldx_log_scan_total', 0);
        $logRotationScanRunning = $this->queryFlag('shieldx_log_scan_running');
        $logRotationScanId = $this->queryText('shieldx_log_scan_id');
        $logRotationScanSeen = $this->queryInt('shieldx_log_scan_seen', 0);
        $logRotationScanFound = $this->queryInt('shieldx_log_scan_found', 0);
        $tempCleanupRun = $this->queryFlag('shieldx_temp_cleanup_run');
        $tempCleanupFiles = $this->queryInt('shieldx_temp_cleanup_files', 0);
        $tempCleanupDirectories = $this->queryInt('shieldx_temp_cleanup_dirs', 0);
        $tempCleanupBytes = $this->queryInt('shieldx_temp_cleanup_bytes', 0);
        $tempCleanupFailed = $this->queryInt('shieldx_temp_cleanup_failed', 0);
        $tempCleanupTruncated = $this->queryFlag('shieldx_temp_cleanup_truncated');
        $restoreCreated = $this->queryText('shieldx_restore_created');
        $restoreRestored = $this->queryText('shieldx_restore_restored');
        $restoreProtectedSkipped = $this->queryInt('shieldx_restore_protected_skipped', 0);
        $restoreActionFailed = $this->queryText('shieldx_restore_action_failed');
        $restoreError = $this->queryText('shieldx_restore_error');
        $restorePointDeleted = $this->queryFlag('shieldx_restore_point_deleted');
        $restorePointsCleaned = $this->queryFlag('shieldx_restore_points_cleaned');
        $restorePointsDeleted = $this->queryInt('shieldx_restore_points_deleted', 0);
        $restorePointCleanupSkipped = $this->queryInt('shieldx_restore_points_skipped', 0);
        $restorePointCleanupSkippedActive = $this->queryInt('shieldx_restore_points_skipped_active', 0);
        $restorePointCleanupSkippedRequired = $this->queryInt('shieldx_restore_points_skipped_required', 0);
        $restorePointCleanupFiles = $this->queryInt('shieldx_restore_point_cleanup_files', 0);
        $restorePointCleanupBytes = $this->queryInt('shieldx_restore_point_cleanup_bytes', 0);
        $restorePointCleanupFailed = $this->queryInt('shieldx_restore_point_cleanup_failed', 0);
        $restorePointCleanupBefore = $this->queryText('shieldx_restore_point_cleanup_before');
        $databaseBackupCreated = $this->queryText('shieldx_db_backup_created');
        $databaseBackupRestored = $this->queryText('shieldx_db_backup_restored');
        $databaseBackupsCleaned = $this->queryFlag('shieldx_db_backups_cleaned');
        $databaseBackupsDeleted = $this->queryInt('shieldx_db_backups_deleted', 0);
        $databaseBackupCleanupFiles = $this->queryInt('shieldx_db_backup_cleanup_files', 0);
        $databaseBackupCleanupBytes = $this->queryInt('shieldx_db_backup_cleanup_bytes', 0);
        $databaseBackupCleanupFailed = $this->queryInt('shieldx_db_backup_cleanup_failed', 0);
        $databaseBackupCleanupBefore = $this->queryText('shieldx_db_backup_cleanup_before');
        $databaseBackupCleanupSafetyBackup = $this->queryText('shieldx_db_backup_cleanup_safety_backup');
        $shadowRestored = $this->queryFlag('shieldx_shadow_restored');
        $shadowConfirmed = $this->queryFlag('shieldx_shadow_confirmed');
        $shadowSessionConfirmed = $this->queryFlag('shieldx_shadow_session_confirmed');
        $shadowSessionOk = $this->queryInt('shieldx_shadow_ok', 0);
        $shadowSessionFailed = $this->queryInt('shieldx_shadow_failed', 0);
        $shadowSessionHasMore = $this->queryFlag('shieldx_shadow_more');
        $backupError = $this->queryText('shieldx_backup_error');
        $backupStorageSaved = $this->queryFlag('shieldx_backup_storage_saved');
        $googleDriveOAuthSaved = $this->queryFlag('shieldx_google_drive_oauth_saved');
        $googleDriveAuthStarted = $this->queryFlag('shieldx_google_drive_auth_started');
        $googleDriveConnected = $this->queryFlag('shieldx_google_drive_connected');
        $googleDriveDisconnected = $this->queryFlag('shieldx_google_drive_disconnected');
        $googleDriveTested = $this->queryFlag('shieldx_google_drive_tested');
        $googleDriveError = $this->queryText('shieldx_google_drive_error');
        $actionPath = $this->queryText('shieldx_action_path');
        $setupModeApplied = $this->queryKey('shieldx_setup_mode_applied');
        $setupModeWarnings = $this->queryInt('shieldx_setup_mode_warnings', 0);
        $setupModeError = $this->queryText('shieldx_setup_mode_error');
        ?>
        <div class="wrap shieldx-admin">
            <div class="shieldx-shell" data-shieldx-admin data-shieldx-toast-query-keys="<?php echo esc_attr((string) wp_json_encode($this->toastQueryKeys())); ?>">
                <?php if ($settingsSaved) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X settings saved.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($wafError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html($wafError); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($wafAccessUpdated) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('WAF access list updated.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($backupStorageSaved) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Backup storage settings saved.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($googleDriveOAuthSaved) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Google OAuth app saved. You can authenticate with Google now.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($googleDriveAuthStarted) : ?>
                    <div class="notice notice-info is-dismissible">
                        <p><?php echo esc_html__('Google authorization started. Open the Google verification link in Backup Storage, approve access, then confirm it in SHIELD-X.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($googleDriveConnected) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Google Drive connected for backup storage.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($googleDriveDisconnected) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Google Drive disconnected. SHIELD-X will keep using local-safe backup behavior.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($googleDriveTested) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Google Drive connection test passed.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($googleDriveError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html($googleDriveError); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($setupModeApplied !== '') : ?>
                    <div class="<?php echo esc_attr($setupModeWarnings > 0 ? 'notice notice-warning is-dismissible' : 'notice notice-success is-dismissible'); ?>">
                        <p>
                            <?php
                            $profile = SetupModeManager::profile($setupModeApplied);
                            if ($setupModeWarnings > 0) {
                                echo esc_html(sprintf(
                                    /* translators: 1: setup mode label, 2: warning count. */
                                    _n('%1$s was applied with %2$d warning.', '%1$s was applied with %2$d warnings.', $setupModeWarnings, 'xerotact-shield-x-security'),
                                    (string) ($profile['label'] ?? $setupModeApplied),
                                    $setupModeWarnings
                                ));
                            } else {
                                echo esc_html(sprintf(
                                    /* translators: %s is the setup mode label. */
                                    __('%s was applied successfully.', 'xerotact-shield-x-security'),
                                    (string) ($profile['label'] ?? $setupModeApplied)
                                ));
                            }
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($setupModeError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html($setupModeError); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($twoFactorEnabled) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X authenticator 2FA is enabled for this account.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($codesEmailSent) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X emailed a themed copy of the 2FA backup codes to this administrator.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($codesEmailFailed) : ?>
                    <div class="notice notice-warning is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X enabled 2FA, but could not email the backup codes. Download and save the recovery codes shown below now.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($twoFactorDisabled) : ?>
                    <div class="notice notice-warning is-dismissible">
                        <p><?php echo esc_html($twoFactorResetConfirmed ? __('SHIELD-X 2FA reset code was accepted and authenticator 2FA is now disabled for this account.', 'xerotact-shield-x-security') : __('SHIELD-X authenticator 2FA was disabled for this account.', 'xerotact-shield-x-security')); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($adminTwoFactorDisabled) : ?>
                    <div class="notice notice-warning is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X authenticator 2FA was disabled for the selected administrator.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($adminTwoFactorError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html($adminTwoFactorError); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($twoFactorResetSent) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X emailed a one-time 2FA reset code to the configured recovery email.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($twoFactorInvitesSent > 0) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p>
                            <?php
                            /* translators: %d: number of administrator setup emails sent. */
                            echo esc_html(sprintf(_n('SHIELD-X emailed 2FA setup instructions to %d pending administrator.', 'SHIELD-X emailed 2FA setup instructions to %d pending administrators.', $twoFactorInvitesSent, 'xerotact-shield-x-security'), $twoFactorInvitesSent));
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($twoFactorInvitesFailed > 0) : ?>
                    <div class="notice notice-error is-dismissible">
                        <p>
                            <?php
                            /* translators: %d: number of failed administrator setup emails. */
                            echo esc_html(sprintf(_n('SHIELD-X could not email 2FA setup to %d pending administrator.', 'SHIELD-X could not email 2FA setup to %d pending administrators.', $twoFactorInvitesFailed, 'xerotact-shield-x-security'), $twoFactorInvitesFailed));
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($twoFactorInvitesChecked && $twoFactorInvitesTotal === 0 && $twoFactorInvitesSent === 0 && $twoFactorInvitesFailed === 0) : ?>
                    <div class="notice notice-info is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X found no pending administrators who need a 2FA setup email.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($codesRegenerated) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X recovery codes were regenerated.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($recoveryEmailSent) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X recovery email was sent.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($recoveryEmailFailed) : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X could not send the recovery email. Check the mail status below.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($scanCompleted) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X quick scan completed.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($shadowScanCompleted && !$shadowSessionConfirmed) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p>
                            <?php
                            echo esc_html($shadowScanChanges > 0
                                /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                                ? sprintf(_n('SHIELD-X shadow scan found %d changed file for review.', 'SHIELD-X shadow scan found %d changed files for review.', $shadowScanChanges, 'xerotact-shield-x-security'), $shadowScanChanges)
                                : __('SHIELD-X shadow scan completed. No file hash changes were detected.', 'xerotact-shield-x-security'));
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($scheduleSaved !== '') : ?>
                    <div class="notice notice-success is-dismissible">
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <p><?php echo esc_html(sprintf(__('Scheduled %s scan settings saved.', 'xerotact-shield-x-security'), strtolower($this->scanTypeLabel($scheduleSaved)))); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($backupScheduleSaved) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Scheduled database backup settings saved.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($restorePointScheduleSaved) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Scheduled restore point settings saved.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($vulnerabilityScanCompleted) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X vulnerability scan completed.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($coreReady) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Official WordPress core package is ready for comparisons.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($pluginPackagesReady) : ?>
                    <div class="<?php echo esc_attr($pluginPackagesErrors > 0 ? 'notice notice-warning is-dismissible' : 'notice notice-success is-dismissible'); ?>">
                        <p>
                            <?php
                            /* translators: 1: prepared plugin package count, 2: package error count. */
                            echo esc_html(sprintf(__('Official plugin package hashes refreshed. Prepared: %1$d. Skipped or failed: %2$d.', 'xerotact-shield-x-security'), $pluginPackagesPrepared, $pluginPackagesErrors));
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($pluginPackagesError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html($pluginPackagesError); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($pluginPackageUploaded !== '') : ?>
                    <div class="notice notice-success is-dismissible">
                        <p>
                            <?php
                            /* translators: %s: plugin name and version from the uploaded package state. */
                            echo esc_html(sprintf(__('Uploaded plugin package accepted for %s.', 'xerotact-shield-x-security'), $pluginPackageUploaded));
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($pluginPackageUploadError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html($pluginPackageUploadError); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($pluginPackageTrusted !== '') : ?>
                    <div class="notice notice-warning is-dismissible">
                        <p>
                            <?php
                            /* translators: %s: plugin name and version trusted from the installed local copy. */
                            echo esc_html(sprintf(__('Installed plugin files are now trusted as the clean baseline for %s.', 'xerotact-shield-x-security'), $pluginPackageTrusted));
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($pluginPackageTrustError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html($pluginPackageTrustError); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($coreRestored) : ?>
                    <div class="<?php echo esc_attr($databaseRecheckStatus === 'remaining' ? 'notice notice-warning is-dismissible' : 'notice notice-success is-dismissible'); ?>">
                        <p>
                            <?php
                            echo esc_html($databaseRecheckStatus === 'remaining'
                                /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                                ? sprintf(_n('Selected core file was replaced with the official WordPress copy, then rechecked. %d finding still matches.', 'Selected core file was replaced with the official WordPress copy, then rechecked. %d findings still match.', $databaseRecheckCount, 'xerotact-shield-x-security'), $databaseRecheckCount)
                                : __('Selected core file was replaced with the official WordPress copy and rechecked clean.', 'xerotact-shield-x-security'));
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($coreError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html($coreError); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($scanQuarantined) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Suspicious file was moved to SHIELD-X quarantine.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($scanQuarantineRestored) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Quarantined file was restored to its original path.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($scanIgnored) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Scanner issue was ignored.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($scanIgnoreRemoved) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Scanner ignore item was removed.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($htaccessRepaired) : ?>
                    <div class="<?php echo esc_attr($databaseRecheckStatus === 'remaining' ? 'notice notice-warning is-dismissible' : 'notice notice-success is-dismissible'); ?>">
                        <p>
                            <?php
                            if ($databaseRecheckStatus === 'remaining') {
                                /* translators: %d: number of remaining scanner findings. */
                                echo esc_html(sprintf(_n('.htaccess WordPress rewrite rules were repaired, then rechecked. %d finding still matches.', '.htaccess WordPress rewrite rules were repaired, then rechecked. %d findings still match.', $databaseRecheckCount, 'xerotact-shield-x-security'), $databaseRecheckCount));
                            } else {
                                echo esc_html__('.htaccess WordPress rewrite rules were repaired, baselined, and rechecked clean.', 'xerotact-shield-x-security');
                            }
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($fileTrusted) : ?>
                    <div class="<?php echo esc_attr($databaseRecheckStatus === 'remaining' ? 'notice notice-warning is-dismissible' : 'notice notice-success is-dismissible'); ?>">
                        <p>
                            <?php
                            if ($databaseRecheckStatus === 'trusted') {
                                echo esc_html__('Current file hash was trusted and matching scanner findings were resolved for this path.', 'xerotact-shield-x-security');
                            } elseif ($databaseRecheckStatus === 'remaining') {
                                /* translators: %d: number of remaining scanner findings. */
                                echo esc_html(sprintf(_n('Current file hash was trusted, then rechecked. %d scanner finding still matches this file.', 'Current file hash was trusted, then rechecked. %d scanner findings still match this file.', $databaseRecheckCount, 'xerotact-shield-x-security'), $databaseRecheckCount));
                            } else {
                                echo esc_html__('Current file hash was trusted and the file rechecked clean.', 'xerotact-shield-x-security');
                            }
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($fileRechecked) : ?>
                    <div class="<?php echo esc_attr($databaseRecheckStatus === 'remaining' ? 'notice notice-warning is-dismissible' : 'notice notice-success is-dismissible'); ?>">
                        <p>
                            <?php
                            echo esc_html($databaseRecheckStatus === 'remaining'
                                /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                                ? sprintf(_n('File recheck completed. %d scanner finding still matches this file.', 'File recheck completed. %d scanner findings still match this file.', $databaseRecheckCount, 'xerotact-shield-x-security'), $databaseRecheckCount)
                                : __('File recheck completed. No remaining scanner findings matched this file.', 'xerotact-shield-x-security'));
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($fileSaved) : ?>
                    <div class="<?php echo esc_attr($databaseRecheckStatus === 'remaining' ? 'notice notice-warning is-dismissible' : 'notice notice-success is-dismissible'); ?>">
                        <p class="shieldx-notice-actions">
                            <?php
                            echo esc_html($databaseRecheckStatus === 'remaining'
                                /* translators: %d: number of remaining scanner findings. */
                                ? sprintf(_n('Edited file saved with a restore point and local backup. %d scanner finding still matches.', 'Edited file saved with a restore point and local backup. %d scanner findings still match.', $databaseRecheckCount, 'xerotact-shield-x-security'), $databaseRecheckCount)
                                : __('Edited file saved with a restore point and local backup, then rechecked clean.', 'xerotact-shield-x-security'));
                            ?>
                            <a class="button button-secondary" href="<?php echo esc_url(admin_url('admin.php?page=shield-x-backup') . '#shieldx-restore-points'); ?>"><?php echo esc_html__('View restore points', 'xerotact-shield-x-security'); ?></a>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($configCleaned) : ?>
                    <div class="<?php echo esc_attr($databaseRecheckStatus === 'remaining' ? 'notice notice-warning is-dismissible' : 'notice notice-success is-dismissible'); ?>">
                        <p>
                            <?php
                            /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                            $lineText = sprintf(_n('%d selected line', '%d selected lines', $configRemovedLines, 'xerotact-shield-x-security'), $configRemovedLines);
                            /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                            $findingText = sprintf(_n('%d scanner finding still matches', '%d scanner findings still match', $databaseRecheckCount, 'xerotact-shield-x-security'), $databaseRecheckCount);
                            if ($databaseRecheckStatus === 'remaining') {
                                /* translators: 1: number of removed suspicious lines, 2: remaining scanner finding summary. */
                                echo esc_html(sprintf(__('wp-config.php cleanup removed %1$s and created a backup, then rechecked. %2$s this file.', 'xerotact-shield-x-security'), $lineText, $findingText));
                            } else {
                                /* translators: %s: number of removed suspicious lines. */
                                echo esc_html(sprintf(__('wp-config.php cleanup removed %s, created a backup, and rechecked clean.', 'xerotact-shield-x-security'), $lineText));
                            }
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($databaseRemediated) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p>
                            <?php
                            if ($databaseRecheckStatus === 'remaining') {
                                /* translators: %d: number of remaining database scanner findings. */
                                echo esc_html(sprintf(_n('Database issue was repaired and backed up, then rechecked. %d finding still matches.', 'Database issue was repaired and backed up, then rechecked. %d findings still match.', $databaseRecheckCount, 'xerotact-shield-x-security'), $databaseRecheckCount));
                            } else {
                                echo esc_html__('Database issue was repaired, backed up, and rechecked clean.', 'xerotact-shield-x-security');
                            }
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($databaseRestored) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Database backup was restored.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($databaseRechecked) : ?>
                    <div class="<?php echo esc_attr($databaseRecheckStatus === 'remaining' ? 'notice notice-warning is-dismissible' : 'notice notice-success is-dismissible'); ?>">
                        <p>
                            <?php
                            echo esc_html($databaseRecheckStatus === 'remaining'
                                /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                                ? sprintf(_n('Database recheck completed. %d finding still matches this item.', 'Database recheck completed. %d findings still match this item.', $databaseRecheckCount, 'xerotact-shield-x-security'), $databaseRecheckCount)
                                : __('Database recheck completed. No remaining payload was found for this item.', 'xerotact-shield-x-security'));
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($bulkAction !== '') : ?>
                    <div class="<?php echo esc_attr($bulkFailed > 0 ? 'notice notice-warning is-dismissible' : 'notice notice-success is-dismissible'); ?>">
                        <p>
                            <?php
                            echo esc_html(sprintf(
                                /* translators: 1: bulk action label, 2: successful item count, 3: failed or skipped item count. */
                                __('Bulk scanner action "%1$s" finished: %2$d succeeded, %3$d failed or skipped.', 'xerotact-shield-x-security'),
                                $this->formatLabel($bulkAction),
                                $bulkOk,
                                $bulkFailed
                            ));
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($scanActionError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html($scanActionError); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($lockdownPreview) : ?>
                    <div class="notice notice-info is-dismissible">
                        <p><?php echo esc_html__('Lockdown dry-run preview is shown below. No filesystem changes were applied.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($lockdownApplied) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X Lockdown was applied and a rollback manifest was written.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($lockdownReverted) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X Lockdown was reverted from its manifest.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($lockdownForceReverted) : ?>
                    <div class="notice notice-warning is-dismissible">
                        <p><?php echo esc_html__('Known SHIELD-X Lockdown markers and orphaned permissions were force-reverted without trusting the corrupt manifest. Review any remaining host-owned paths.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($permissionFixed) : ?>
                    <div class="<?php echo esc_attr($permissionFailed > 0 ? 'notice notice-warning is-dismissible' : 'notice notice-success is-dismissible'); ?>">
                        <p>
                            <?php
                            echo esc_html(sprintf(
                                /* translators: 1: file count, 2: folder count, 3: unchanged count, 4: skipped count, 5: failed count. */
                                __('Permission Fix updated %1$d files and %2$d folders. %3$d unchanged, %4$d skipped, %5$d failed.', 'xerotact-shield-x-security'),
                                $permissionFiles,
                                $permissionDirectories,
                                $permissionUnchanged,
                                $permissionSkipped,
                                $permissionFailed
                            ));
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($permissionError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html($permissionError); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($lockdownError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html($lockdownError); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($lockdownWarning !== '') : ?>
                    <div class="notice notice-warning is-dismissible">
                        <p><?php echo esc_html($lockdownWarning); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($hardeningError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html($hardeningError); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($recoveryRotated) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Emergency recovery URL was regenerated.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($settingsImported) : ?>
                    <div class="notice notice-success is-dismissible">
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <p><?php echo esc_html(sprintf(__('SHIELD-X settings imported: %s.', 'xerotact-shield-x-security'), $settingsImportModules !== '' ? $settingsImportModules : __('selected modules', 'xerotact-shield-x-security'))); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($settingsImportError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html($settingsImportError); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($notificationsSaved) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X notification settings saved.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($licenseActivated) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X activation is active for this site.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($licenseChecked) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X activation status was checked.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($licenseDeactivated) : ?>
                    <div class="notice notice-warning is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X activation was removed from this site.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($licenseError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html($licenseError); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($activityBlocked !== '') : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html($activityBlocked === 'browser' ? __('Browser signature added to the WAF blacklist.', 'xerotact-shield-x-security') : __('IP address added to the WAF blacklist.', 'xerotact-shield-x-security')); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($activityBlockError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p>
                            <?php
                            if ($activityBlockError === 'invalid_ip') {
                                echo esc_html__('SHIELD-X could not add that visitor because the IP address is invalid.', 'xerotact-shield-x-security');
                            } elseif ($activityBlockError === 'current_client') {
                                echo esc_html__('SHIELD-X will not block the current administrator IP or browser from this shortcut.', 'xerotact-shield-x-security');
                            } else {
                                echo esc_html__('SHIELD-X could not add an empty blacklist value.', 'xerotact-shield-x-security');
                            }
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($logRotationSaved) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Log rotation settings saved.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($uninstallSettingsSaved) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Plugin deletion policy saved.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($logRotationRun) : ?>
                    <?php $logRotationHasWarnings = $logRotationStatus !== 'completed' || $logRotationFailedFiles > 0; ?>
                    <div class="<?php echo esc_attr($logRotationHasWarnings ? 'notice notice-warning is-dismissible' : 'notice notice-success is-dismissible'); ?>" data-shieldx-toast-notice>
                        <p><?php echo esc_html($logRotationHasWarnings ? __('Log rotation finished with warnings.', 'xerotact-shield-x-security') : __('Log rotation completed.', 'xerotact-shield-x-security')); ?></p>
                        <?php if ($logRotationFailedFiles > 0) : ?>
                            <?php /* translators: %d: number of log files that could not be processed. */ ?>
                            <p><?php echo esc_html(sprintf(_n('%d log file could not be processed. Check filesystem ownership and permissions.', '%d log files could not be processed. Check filesystem ownership and permissions.', $logRotationFailedFiles, 'xerotact-shield-x-security'), $logRotationFailedFiles)); ?></p>
                        <?php endif; ?>
                        <?php if (!$logRotationHasWarnings && $logRotationDeletedFiles === 0 && $logRotationDeletedRows === 0) : ?>
                            <p><?php echo esc_html__('No logs met the configured retention or file-count policy, so nothing was removed.', 'xerotact-shield-x-security'); ?></p>
                        <?php endif; ?>
                    </div>
                <?php endif; ?>
                <?php if ($tempCleanupRun) : ?>
                    <div class="<?php echo esc_attr($tempCleanupFailed > 0 || $tempCleanupTruncated ? 'notice notice-warning is-dismissible' : 'notice notice-success is-dismissible'); ?>" data-shieldx-toast-notice>
                        <p>
                            <?php
                            /* translators: 1: deleted file count, 2: deleted byte size, 3: deleted directory count, 4: failed delete count. */
                            echo esc_html(sprintf(__('Temporary cleanup finished: %1$d files, %2$s cleared, %3$d empty folders, %4$d warnings.', 'xerotact-shield-x-security'), $tempCleanupFiles, $this->formatBytes($tempCleanupBytes), $tempCleanupDirectories, $tempCleanupFailed));
                            ?>
                        </p>
                        <?php if ($tempCleanupTruncated) : ?>
                            <p><?php echo esc_html__('The cleanup scan reached the safety limit. Run it again to continue.', 'xerotact-shield-x-security'); ?></p>
                        <?php endif; ?>
                    </div>
                <?php endif; ?>
                <?php if ($logRotationScanRunning && $logRotationScanId !== '') : ?>
                    <?php $logScanContinueUrl = $this->logRotationScanContinueUrl($logRotationScanId); ?>
                    <div class="notice notice-info" data-shieldx-auto-redirect-url="<?php echo esc_url($logScanContinueUrl); ?>" data-shieldx-auto-redirect-delay="750">
                        <?php /* translators: 1: scanned filesystem entry count, 2: discovered log file count. */ ?>
                        <p><?php echo esc_html(sprintf(__('Log discovery is scanning in small batches: %1$d filesystem entries checked, %2$d log files found.', 'xerotact-shield-x-security'), $logRotationScanSeen, $logRotationScanFound)); ?></p>
                        <p><a class="button" href="<?php echo esc_url($logScanContinueUrl); ?>"><?php echo esc_html__('Continue log scan now', 'xerotact-shield-x-security'); ?></a></p>
                    </div>
                <?php endif; ?>
                <?php if ($logRotationScanned) : ?>
                    <div class="notice notice-success is-dismissible">
                        <?php /* translators: 1: marked log count, 2: total discovered log count. */ ?>
                        <p><?php echo esc_html(sprintf(__('%1$d of %2$d discovered log files are marked for rotation.', 'xerotact-shield-x-security'), $logRotationScanMarked, $logRotationScanTotal)); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($restoreCreated !== '') : ?>
                    <div class="notice notice-success is-dismissible">
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <p><?php echo esc_html(sprintf(__('Restore point created: %s.', 'xerotact-shield-x-security'), $restoreCreated)); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($restoreRestored !== '') : ?>
                    <div class="notice notice-success is-dismissible">
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <p><?php echo esc_html(sprintf(__('Restore point restored: %s.', 'xerotact-shield-x-security'), $restoreRestored)); ?></p>
                        <?php if ($restoreProtectedSkipped > 0) : ?>
                            <?php /* translators: %d: number of protected SHIELD-X plugin files skipped during restore. */ ?>
                            <p><?php echo esc_html(sprintf(_n('%d SHIELD-X plugin file was skipped so the active plugin is not overwritten during restore.', '%d SHIELD-X plugin files were skipped so the active plugin is not overwritten during restore.', $restoreProtectedSkipped, 'xerotact-shield-x-security'), $restoreProtectedSkipped)); ?></p>
                        <?php endif; ?>
                    </div>
                <?php endif; ?>
                <?php if ($restoreError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html($restoreError); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($restoreActionFailed === 'started') : ?>
                    <div class="notice notice-warning is-dismissible">
                        <p><?php echo esc_html__('The isolated restore started, but SHIELD-X could not open the protected Recovery controller. Open the active restore from Backup or Recovery to continue safely.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php elseif ($restoreActionFailed === 'failed') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html__('SHIELD-X stopped the restore action safely because the administrator response could not be completed. No additional restore steps were started.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($restorePointDeleted) : ?>
                    <div class="notice notice-success is-dismissible">
                        <p><?php echo esc_html__('Local restore point deleted. Linked database backup files and remote copies were retained.', 'xerotact-shield-x-security'); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($restorePointsCleaned) : ?>
                    <div class="<?php echo esc_attr($restorePointCleanupFailed > 0 ? 'notice notice-warning is-dismissible' : 'notice notice-success is-dismissible'); ?>">
                        <p>
                            <?php
                            /* translators: 1: deleted restore point count, 2: selected before date, 3: deleted file count, 4: formatted deleted bytes. */
                            echo esc_html(sprintf(_n('%1$d local restore point older than %2$s was deleted (%3$d files, %4$s).', '%1$d local restore points older than %2$s were deleted (%3$d files, %4$s).', $restorePointsDeleted, 'xerotact-shield-x-security'), $restorePointsDeleted, $restorePointCleanupBefore !== '' ? $restorePointCleanupBefore : __('selected date', 'xerotact-shield-x-security'), $restorePointCleanupFiles, $this->formatBytes($restorePointCleanupBytes)));
                            ?>
                        </p>
                        <?php if ($restorePointCleanupFailed > 0) : ?>
                            <?php /* translators: %d: number of restore point files that could not be deleted. */ ?>
                            <p><?php echo esc_html(sprintf(_n('%d restore point file could not be deleted. Check filesystem ownership and permissions.', '%d restore point files could not be deleted. Check filesystem ownership and permissions.', $restorePointCleanupFailed, 'xerotact-shield-x-security'), $restorePointCleanupFailed)); ?></p>
                        <?php endif; ?>
                        <?php if ($restorePointCleanupSkippedActive > 0) : ?>
                            <?php /* translators: %d: number of in-progress restore points retained during cleanup. */ ?>
                            <p><?php echo esc_html(sprintf(_n('%d in-progress restore point was kept.', '%d in-progress restore points were kept.', $restorePointCleanupSkippedActive, 'xerotact-shield-x-security'), $restorePointCleanupSkippedActive)); ?></p>
                        <?php endif; ?>
                        <?php if ($restorePointCleanupSkippedRequired > 0) : ?>
                            <?php /* translators: %d: number of restore points retained because newer incremental restore points depend on them. */ ?>
                            <p><?php echo esc_html(sprintf(_n('%d older restore point was kept because a newer incremental restore point depends on it.', '%d older restore points were kept because newer incremental restore points depend on them.', $restorePointCleanupSkippedRequired, 'xerotact-shield-x-security'), $restorePointCleanupSkippedRequired)); ?></p>
                        <?php endif; ?>
                        <?php if ($restorePointCleanupSkipped > ($restorePointCleanupSkippedActive + $restorePointCleanupSkippedRequired)) : ?>
                            <?php $restorePointCleanupSkippedOther = $restorePointCleanupSkipped - $restorePointCleanupSkippedActive - $restorePointCleanupSkippedRequired; ?>
                            <?php /* translators: %d: number of restore points retained for another safety reason. */ ?>
                            <p><?php echo esc_html(sprintf(_n('%d restore point was kept for safety.', '%d restore points were kept for safety.', $restorePointCleanupSkippedOther, 'xerotact-shield-x-security'), $restorePointCleanupSkippedOther)); ?></p>
                        <?php endif; ?>
                    </div>
                <?php endif; ?>
                <?php if ($databaseBackupCreated !== '') : ?>
                    <div class="notice notice-success is-dismissible">
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <p><?php echo esc_html(sprintf(__('Database backup created: %s.', 'xerotact-shield-x-security'), $databaseBackupCreated)); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($databaseBackupRestored !== '') : ?>
                    <div class="notice notice-success is-dismissible">
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <p><?php echo esc_html(sprintf(__('Database backup restored: %s.', 'xerotact-shield-x-security'), $databaseBackupRestored)); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($databaseBackupsCleaned) : ?>
                    <div class="<?php echo esc_attr($databaseBackupCleanupFailed > 0 ? 'notice notice-warning is-dismissible' : 'notice notice-success is-dismissible'); ?>">
                        <p>
                            <?php
                            /* translators: 1: deleted backup count, 2: selected before date, 3: deleted file count, 4: formatted deleted bytes. */
                            echo esc_html(sprintf(_n('%1$d local database backup older than %2$s was deleted (%3$d files, %4$s).', '%1$d local database backups older than %2$s were deleted (%3$d files, %4$s).', $databaseBackupsDeleted, 'xerotact-shield-x-security'), $databaseBackupsDeleted, $databaseBackupCleanupBefore !== '' ? $databaseBackupCleanupBefore : __('selected date', 'xerotact-shield-x-security'), $databaseBackupCleanupFiles, $this->formatBytes($databaseBackupCleanupBytes)));
                            ?>
                        </p>
                        <?php if ($databaseBackupCleanupSafetyBackup !== '') : ?>
                            <?php /* translators: %s: safety database backup ID created before cleanup. */ ?>
                            <p><?php echo esc_html(sprintf(__('Safety database backup created before cleanup: %s.', 'xerotact-shield-x-security'), $databaseBackupCleanupSafetyBackup)); ?></p>
                        <?php endif; ?>
                        <?php if ($databaseBackupCleanupFailed > 0) : ?>
                            <?php /* translators: %d: number of backup files that could not be deleted. */ ?>
                            <p><?php echo esc_html(sprintf(_n('%d backup file could not be deleted. Check filesystem ownership and permissions.', '%d backup files could not be deleted. Check filesystem ownership and permissions.', $databaseBackupCleanupFailed, 'xerotact-shield-x-security'), $databaseBackupCleanupFailed)); ?></p>
                        <?php endif; ?>
                    </div>
                <?php endif; ?>
                <?php if ($shadowRestored) : ?>
                    <div class="notice notice-success is-dismissible">
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <p><?php echo esc_html(sprintf(__('Shadow copy restored for %s.', 'xerotact-shield-x-security'), $actionPath !== '' ? $actionPath : __('selected file', 'xerotact-shield-x-security'))); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($shadowConfirmed) : ?>
                    <div class="notice notice-success is-dismissible">
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <p><?php echo esc_html(sprintf(__('Current file confirmed and shadow copy updated for %s.', 'xerotact-shield-x-security'), $actionPath !== '' ? $actionPath : __('selected file', 'xerotact-shield-x-security'))); ?></p>
                    </div>
                <?php endif; ?>
                <?php if ($shadowSessionConfirmed) : ?>
                    <div class="<?php echo esc_attr($shadowSessionFailed > 0 ? 'notice notice-warning is-dismissible' : 'notice notice-success is-dismissible'); ?>">
                        <p>
                            <?php
                            echo esc_html(sprintf(
                                /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                                __('Shadow session accepted: %1$d files trusted, %2$d failed or skipped.', 'xerotact-shield-x-security'),
                                $shadowSessionOk,
                                $shadowSessionFailed
                            ));
                            if ($shadowSessionHasMore) {
                                echo ' ' . esc_html__('More changed files remain; review and confirm the next batch when ready.', 'xerotact-shield-x-security');
                            }
                            ?>
                        </p>
                    </div>
                <?php endif; ?>
                <?php if ($backupError !== '') : ?>
                    <div class="notice notice-error is-dismissible">
                        <p><?php echo esc_html($backupError); ?></p>
                    </div>
                <?php endif; ?>
                <?php if (!$lightweight) : ?>
                    <?php $this->renderSafetyBlockerNotices($status); ?>
                <?php endif; ?>

                <?php
                $headerWarnings = $lightweight ? [] : $this->subnavWarnings($status);
                $systemWarningState = $this->navigationWarningState($headerWarnings, 'shield-x-system', $activePage === 'shield-x-system');
                $lostAuthenticatorHeaderLabel = __('Lost authenticator?', 'xerotact-shield-x-security');
                $systemHeaderLabel = __('System / Configuration', 'xerotact-shield-x-security');
                $systemHeaderTitle = (string) $systemWarningState['label'] !== '' ? (string) $systemWarningState['label'] : $systemHeaderLabel;
                $systemHeaderClass = trim('shieldx-header-button shieldx-header-button--icon ' . (string) $systemWarningState['class']);
                ?>
                <header class="shieldx-header">
                    <div>
                        <span class="shieldx-kicker">
                            <?php echo esc_html($this->dashboardGreeting($activePage)); ?>
                            <?php if ($activePage !== 'shield-x' && defined('SHIELDX_VERSION')) : ?>
                                <span class="shieldx-kicker-version"><?php echo esc_html('v' . (string) SHIELDX_VERSION); ?></span>
                            <?php endif; ?>
                        </span>
                        <h1 class="shieldx-title-row">
                            <?php if ($activePage === 'shield-x-waf-access' || $activePage === 'shield-x-backup-restore') : ?>
                                <?php
                                $backUrl = $activePage === 'shield-x-backup-restore'
                                    ? admin_url('admin.php?page=shield-x-backup#shieldx-restore-points')
                                    : admin_url('admin.php?page=shield-x-waf');
                                $backLabel = $activePage === 'shield-x-backup-restore'
                                    ? __('Back to Backup', 'xerotact-shield-x-security')
                                    : __('Back to Firewall', 'xerotact-shield-x-security');
                                ?>
                                <a class="shieldx-title-back" href="<?php echo esc_url($backUrl); ?>" aria-label="<?php echo esc_attr($backLabel); ?>">
                                    <?php echo wp_kses(Iconoir::svg('nav-arrow-left', 'shieldx-icon shieldx-title-back-icon'), $this->allowedIconSvg()); ?>
                                </a>
                            <?php endif; ?>
                            <span><?php echo esc_html($this->pageHeading($activePage)); ?></span>
                            <?php $this->renderLicenseBadge($status); ?>
                            <?php $this->renderExtensionsHeaderAction($activePage); ?>
                        </h1>
                        <p><?php echo esc_html($this->pageSubtitle($activePage, $status)); ?></p>
                    </div>
                    <div class="shieldx-header-meta">
                        <?php if ($activePage === 'shield-x-login-security') : ?>
                            <button
                                type="button"
                                class="shieldx-header-button shieldx-header-button--button shieldx-header-button--icon"
                                data-shieldx-disclosure-toggle
                                aria-expanded="false"
                                aria-controls="shieldx-lost-authenticator-panel"
                                title="<?php echo esc_attr($lostAuthenticatorHeaderLabel); ?>"
                            >
                                <?php echo wp_kses(Iconoir::svg('fingerprint-scan', 'shieldx-icon shieldx-header-icon'), $this->allowedIconSvg()); ?>
                                <span class="shieldx-screen-reader-text"><?php echo esc_html($lostAuthenticatorHeaderLabel); ?></span>
                            </button>
                        <?php endif; ?>
                        <div class="shieldx-header-async-actions" data-shieldx-header-actions>
                            <?php if (!$lightweight) : ?>
                                <?php $this->renderAddonHeaderActions($activePage, $status); ?>
                            <?php endif; ?>
                        </div>
                        <a class="<?php echo esc_attr($systemHeaderClass); ?>" href="<?php echo esc_url(admin_url('admin.php?page=shield-x-system')); ?>"<?php if ($activePage === 'shield-x-system') : ?> aria-current="page"<?php endif; ?> title="<?php echo esc_attr($systemHeaderTitle); ?>">
                            <?php echo wp_kses(Iconoir::svg('tools', 'shieldx-icon shieldx-header-icon'), $this->allowedIconSvg()); ?>
                            <span class="shieldx-screen-reader-text"><?php echo esc_html($systemHeaderLabel); ?></span>
                            <?php if (!empty($systemWarningState['needs'])) : ?>
                                <span class="shieldx-header-warning" aria-hidden="true"></span>
                                <span class="shieldx-screen-reader-text"><?php echo esc_html((string) $systemWarningState['label']); ?></span>
                            <?php endif; ?>
                        </a>
                    </div>
                </header>

                <?php $this->renderLicenseModal($status, $activePage); ?>
                <?php if ($activePage !== 'shield-x-waf-access' && $activePage !== 'shield-x-backup-restore') : ?>
                    <?php $this->renderSubnav($activePage, $status, $headerWarnings); ?>
                <?php endif; ?>
        <?php
    }

    private function renderAddonHeaderActions(string $activePage, array $status): void
    {
        foreach (AddonRegistry::fromWordPress()->headerActions($activePage) as $action) {
            $classes = ['shieldx-header-button', 'shieldx-header-button--icon'];
            if (!empty($action['active'])) {
                $classes[] = 'is-active';
            }
            if (!empty($action['warning'])) {
                $classes[] = 'has-warning';
            } elseif (!empty($action['notice'])) {
                $classes[] = 'has-notice';
            }

            $label = (string) ($action['label'] ?? '');
            $screenReaderText = (string) ($action['screen_reader_text'] ?? $label);
            $modalId = (string) ($action['modal_id'] ?? '');
            if ($modalId !== '') :
            ?>
            <button
                type="button"
                class="<?php echo esc_attr(implode(' ', array_merge($classes, ['shieldx-header-button--button']))); ?>"
                data-shieldx-form-modal-open
                aria-controls="<?php echo esc_attr($modalId); ?>"
                title="<?php echo esc_attr($label); ?>"
            >
                <?php echo wp_kses(Iconoir::svg((string) ($action['icon'] ?? 'activity'), 'shieldx-icon shieldx-header-icon'), $this->allowedIconSvg()); ?>
                <span class="shieldx-screen-reader-text"><?php echo esc_html($screenReaderText); ?></span>
                <?php if (!empty($action['warning']) || !empty($action['notice'])) : ?>
                    <span class="shieldx-header-warning" aria-hidden="true"></span>
                <?php endif; ?>
            </button>
            <?php else : ?>
            <a class="<?php echo esc_attr(implode(' ', $classes)); ?>" href="<?php echo esc_url((string) ($action['href'] ?? '')); ?>"<?php echo !empty($action['active']) ? ' aria-current="page"' : ''; ?> title="<?php echo esc_attr($label); ?>">
                <?php echo wp_kses(Iconoir::svg((string) ($action['icon'] ?? 'activity'), 'shieldx-icon shieldx-header-icon'), $this->allowedIconSvg()); ?>
                <span class="shieldx-screen-reader-text"><?php echo esc_html($screenReaderText); ?></span>
                <?php if (!empty($action['warning']) || !empty($action['notice'])) : ?>
                    <span class="shieldx-header-warning" aria-hidden="true"></span>
                <?php endif; ?>
            </a>
            <?php endif;
        }
    }

    /**
     * @param array<string,mixed> $status
     */
    private function renderLicenseBadge(array $status): void
    {
        $license = isset($status['licensing']) && is_array($status['licensing']) ? $status['licensing'] : LicenseManager::fromWordPress()->summaryForCurrentSession();
        $statusKey = LicenseSettings::sanitizeStatus((string) ($license['status'] ?? LicenseSettings::STATUS_INACTIVE));
        $tone = (string) ($license['tone'] ?? LicenseSettings::statusTone($statusKey));
        $label = (string) ($license['label'] ?? LicenseSettings::statusLabel($statusKey));
        $icon = $statusKey === LicenseSettings::STATUS_ACTIVE ? 'badge-check' : ($statusKey === LicenseSettings::STATUS_ERROR ? 'message-alert' : 'lock');
        $title = (string) ($license['last_message'] ?? '');
        if ($title === '') {
            $title = __('Open activation status', 'xerotact-shield-x-security');
        }
        ?>
        <button
            type="button"
            class="<?php echo esc_attr('shieldx-license-badge shieldx-license-badge--' . $tone); ?>"
            data-shieldx-form-modal-open
            aria-controls="shieldx-license-modal"
            title="<?php echo esc_attr($title); ?>"
        >
            <?php echo wp_kses(Iconoir::svg($icon, 'shieldx-icon shieldx-license-icon'), $this->allowedIconSvg()); ?>
            <span><?php echo esc_html($label); ?></span>
        </button>
        <?php
    }

    private function renderExtensionsHeaderAction(string $activePage): void
    {
        $label = __('Extensions', 'xerotact-shield-x-security');
        ?>
        <a
            class="shieldx-license-badge shieldx-license-badge--idle shieldx-extensions-header-action"
            href="<?php echo esc_url(admin_url('admin.php?page=shield-x-addons')); ?>"
            title="<?php echo esc_attr($label); ?>"
            <?php if ($activePage === 'shield-x-addons') : ?>aria-current="page"<?php endif; ?>
        >
            <?php echo wp_kses(Iconoir::svg('grid-plus', 'shieldx-icon shieldx-license-icon'), $this->allowedIconSvg()); ?>
            <span><?php echo esc_html($label); ?></span>
        </a>
        <?php
    }

    /**
     * @param array<string,mixed> $status
     */
    private function renderLicenseModal(array $status, string $activePage): void
    {
        $license = isset($status['licensing']) && is_array($status['licensing']) ? $status['licensing'] : LicenseManager::fromWordPress()->summaryForCurrentSession();
        $statusKey = LicenseSettings::sanitizeStatus((string) ($license['status'] ?? LicenseSettings::STATUS_INACTIVE));
        $label = (string) ($license['label'] ?? LicenseSettings::statusLabel($statusKey));
        $instance = (string) ($license['instance'] ?? '');
        if ($instance === '') {
            $instance = LicenseSettings::currentInstance();
        }
        $maskedKey = (string) ($license['masked_key'] ?? '');
        $activatedAt = (string) ($license['activated_at'] ?? '');
        $lastChecked = (string) ($license['last_checked_at'] ?? '');
        $expires = (string) ($license['expires'] ?? '');
        $returnPage = $this->allowedReturnPage($activePage);
        $activationFormId = 'shieldx-license-activation-form';
        $statusCheckFormId = 'shieldx-license-status-check-form';
        $hasStoredKey = !empty($license['has_stored_key']);
        $showSupportIntro = in_array($statusKey, [LicenseSettings::STATUS_INACTIVE, LicenseSettings::STATUS_INVALID], true);
        $showActivationForm = $statusKey !== LicenseSettings::STATUS_ACTIVE && !$hasStoredKey;
        $showLicenseActions = $hasStoredKey || $statusKey === LicenseSettings::STATUS_ACTIVE;
        $showStatusCheck = $hasStoredKey;
        ?>
                <div id="shieldx-license-modal" class="shieldx-form-modal shieldx-license-modal" data-shieldx-form-modal hidden>
                    <div class="shieldx-form-modal-backdrop" data-shieldx-form-modal-close></div>
                    <section class="shieldx-form-modal-card<?php echo $showSupportIntro ? ' shieldx-license-support-card' : ''; ?>" role="dialog" aria-modal="true" aria-labelledby="<?php echo esc_attr($showSupportIntro ? 'shieldx-license-support-title' : 'shieldx-license-title'); ?>" aria-describedby="<?php echo esc_attr($showSupportIntro ? 'shieldx-license-support-intro' : 'shieldx-license-intro'); ?>">
                        <button type="button" class="shieldx-modal-close" data-shieldx-form-modal-close aria-label="<?php echo esc_attr__('Close activation status', 'xerotact-shield-x-security'); ?>">
                            <span aria-hidden="true">&times;</span>
                        </button>

                        <?php if ($showSupportIntro) : ?>
                            <div class="shieldx-license-step shieldx-license-support" data-shieldx-license-support-intro>
                                <h2 id="shieldx-license-support-title"><?php echo esc_html__('Help keep SHIELD-X moving', 'xerotact-shield-x-security'); ?></h2>
                                <div id="shieldx-license-support-intro" class="shieldx-license-support-copy">
                                    <p><?php echo esc_html__("We hope you're enjoying the plugin as much as we enjoyed creating it for you! We believe in keeping our core tools open and accessible, which is why there are never any limits on what you can do with the free version.", 'xerotact-shield-x-security'); ?></p>
                                    <p><?php echo esc_html__('By purchasing a license, you help fund SHIELD-X development. In return, you receive access to our add-ons and priority support.', 'xerotact-shield-x-security'); ?></p>
                                    <div class="shieldx-license-support-groups">
                                        <section>
                                            <div class="shieldx-license-support-group-heading">
                                                <h3><?php echo esc_html__('Add-ons', 'xerotact-shield-x-security'); ?></h3>
                                                <span><?php echo esc_html__('Optional capabilities', 'xerotact-shield-x-security'); ?></span>
                                            </div>
                                            <ul class="shieldx-license-support-features">
                                                <li><strong><?php echo esc_html__('Notifications', 'xerotact-shield-x-security'); ?></strong><span><?php echo esc_html__('Route important security events to the right people with instant alerts or email digests.', 'xerotact-shield-x-security'); ?></span></li>
                                                <li><strong><?php echo esc_html__('Pulse', 'xerotact-shield-x-security'); ?></strong><span><?php echo esc_html__('Monitor uptime, speed, content changes, traffic, mail, and cron health from one place.', 'xerotact-shield-x-security'); ?></span></li>
                                                <li><strong><?php echo esc_html__('Recovery', 'xerotact-shield-x-security'); ?></strong><span><?php echo esc_html__('Create restore points and prepare safe rollback candidates for when something critical breaks.', 'xerotact-shield-x-security'); ?></span></li>
                                                <li><strong><?php echo esc_html__('Scheduler', 'xerotact-shield-x-security'); ?></strong><span><?php echo esc_html__('Automate recurring SHIELD-X jobs with WP-Cron and system cron support.', 'xerotact-shield-x-security'); ?></span></li>
                                                <li><strong><?php echo esc_html__('AI Assistant (coming soon)', 'xerotact-shield-x-security'); ?></strong><span><?php echo esc_html__('Get a practical AI helper to interpret findings and suggest next steps without leaving WordPress.', 'xerotact-shield-x-security'); ?></span></li>
                                            </ul>
                                        </section>
                                        <section>
                                            <div class="shieldx-license-support-group-heading">
                                                <h3><?php echo esc_html__('License benefits', 'xerotact-shield-x-security'); ?></h3>
                                                <span><?php echo esc_html__('Included with your license', 'xerotact-shield-x-security'); ?></span>
                                            </div>
                                            <ul class="shieldx-license-support-features">
                                                <li><strong><?php echo esc_html__('Protected Updates', 'xerotact-shield-x-security'); ?></strong><span><?php echo esc_html__('Receive protected add-on updates directly inside WordPress.', 'xerotact-shield-x-security'); ?></span></li>
                                                <li><strong><?php echo esc_html__('Priority VIP Support', 'xerotact-shield-x-security'); ?></strong><span><?php echo esc_html__('Get fast, direct help when you need a hand.', 'xerotact-shield-x-security'); ?></span></li>
                                                <li><strong><?php echo esc_html__('Future Releases', 'xerotact-shield-x-security'); ?></strong><span><?php echo esc_html__('Get instant access to new pro features, enhancements, and security improvements as they roll out.', 'xerotact-shield-x-security'); ?></span></li>
                                            </ul>
                                        </section>
                                    </div>
                                    <p><?php echo esc_html__("Your support goes directly toward maintaining and improving the software for everyone. We'd love to have you as part of our extended family!", 'xerotact-shield-x-security'); ?></p>
                                </div>
                                <div class="shieldx-modal-actions shieldx-license-support-actions">
                                    <button type="button" class="button button-primary" data-shieldx-license-support-next>
                                        <?php echo esc_html__('I would love to support', 'xerotact-shield-x-security'); ?>
                                    </button>
                                    <button type="button" class="button" data-shieldx-form-modal-close><?php echo esc_html__('Close', 'xerotact-shield-x-security'); ?></button>
                                </div>
                            </div>
                        <?php endif; ?>

                        <div class="shieldx-license-step" data-shieldx-license-activation-step<?php if ($showSupportIntro) : ?> hidden<?php endif; ?>>
                            <p class="shieldx-modal-kicker"><?php echo esc_html__('Activation', 'xerotact-shield-x-security'); ?></p>
                            <h2 id="shieldx-license-title"><?php echo esc_html__('SHIELD-X activation', 'xerotact-shield-x-security'); ?></h2>
                            <p id="shieldx-license-intro" class="shieldx-muted">
                                <?php echo esc_html__('Connect this site to the Xerotact subscription license server.', 'xerotact-shield-x-security'); ?>
                            </p>

                            <dl class="shieldx-detail-list shieldx-detail-list--compact shieldx-license-summary">
                                <dt><?php echo esc_html__('Status', 'xerotact-shield-x-security'); ?></dt>
                                <dd class="shieldx-license-status-cell">
                                    <span class="<?php echo esc_attr('shieldx-pill shieldx-pill--' . (string) ($license['tone'] ?? 'idle')); ?>"><?php echo esc_html($label); ?></span>
                                    <?php if ($showStatusCheck) : ?>
                                        <button type="submit" form="<?php echo esc_attr($statusCheckFormId); ?>" class="shieldx-license-icon-button" title="<?php echo esc_attr__('Check current key', 'xerotact-shield-x-security'); ?>" aria-label="<?php echo esc_attr__('Check current key', 'xerotact-shield-x-security'); ?>">
                                            <?php echo wp_kses(Iconoir::svg('clock-rotate-right', 'shieldx-icon shieldx-license-action-icon'), $this->allowedIconSvg()); ?>
                                        </button>
                                    <?php endif; ?>
                                </dd>
                                <dt><?php echo esc_html__('Instance', 'xerotact-shield-x-security'); ?></dt>
                                <dd><code><?php echo esc_html($instance !== '' ? $instance : __('Unknown', 'xerotact-shield-x-security')); ?></code></dd>
                                <dt><?php echo esc_html__('Key', 'xerotact-shield-x-security'); ?></dt>
                                <dd><?php echo esc_html($maskedKey !== '' ? $maskedKey : __('Not stored', 'xerotact-shield-x-security')); ?></dd>
                                <?php if ($statusKey === LicenseSettings::STATUS_ACTIVE) : ?>
                                    <dt><?php echo esc_html__('Activation date', 'xerotact-shield-x-security'); ?></dt>
                                    <dd><?php echo esc_html($activatedAt !== '' ? $this->formatTime($activatedAt) : __('Not recorded yet', 'xerotact-shield-x-security')); ?></dd>
                                <?php endif; ?>
                                <dt><?php echo esc_html__('Expires', 'xerotact-shield-x-security'); ?></dt>
                                <dd><?php echo esc_html($expires !== '' ? $expires : __('Not reported', 'xerotact-shield-x-security')); ?></dd>
                                <dt><?php echo esc_html__('Last checked', 'xerotact-shield-x-security'); ?></dt>
                                <dd><?php echo esc_html($lastChecked !== '' ? $this->formatTime($lastChecked) : __('Not checked yet', 'xerotact-shield-x-security')); ?></dd>
                            </dl>

                            <?php if ($showStatusCheck) : ?>
                                <form id="<?php echo esc_attr($statusCheckFormId); ?>" class="shieldx-license-status-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                                    <input type="hidden" name="action" value="shieldx_validate_license">
                                    <input type="hidden" name="return_page" value="<?php echo esc_attr($returnPage); ?>">
                                    <?php wp_nonce_field('shieldx_validate_license'); ?>
                                </form>
                            <?php endif; ?>

                            <?php if ($showActivationForm) : ?>
                                <form id="<?php echo esc_attr($activationFormId); ?>" class="shieldx-modal-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                                    <input type="hidden" name="action" value="shieldx_activate_license">
                                    <input type="hidden" name="return_page" value="<?php echo esc_attr($returnPage); ?>">
                                    <?php wp_nonce_field('shieldx_activate_license'); ?>
                                    <label class="shieldx-field">
                                        <span><?php echo esc_html__('Activation code', 'xerotact-shield-x-security'); ?></span>
                                        <input type="password" name="license_key" value="" autocomplete="off" spellcheck="false" required>
                                        <small><?php echo esc_html__('The code is sent to xerotact.com with this site instance.', 'xerotact-shield-x-security'); ?></small>
                                    </label>
                                    <div class="shieldx-modal-actions">
                                        <a class="button shieldx-license-buy-link" href="<?php echo esc_url('https://xerotact.com/'); ?>" target="_blank" rel="noopener noreferrer">
                                            <?php echo esc_html__("Don't have a license? Buy here", 'xerotact-shield-x-security'); ?>
                                        </a>
                                        <button type="submit" class="button button-primary"><?php echo esc_html__('Activate', 'xerotact-shield-x-security'); ?></button>
                                        <button type="button" class="button" data-shieldx-form-modal-close><?php echo esc_html__('Close', 'xerotact-shield-x-security'); ?></button>
                                    </div>
                                </form>
                            <?php endif; ?>

                            <?php if ($showLicenseActions) : ?>
                                <div class="shieldx-license-actions">
                                    <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                                        <input type="hidden" name="action" value="shieldx_validate_license">
                                        <input type="hidden" name="return_page" value="<?php echo esc_attr($returnPage); ?>">
                                        <?php wp_nonce_field('shieldx_validate_license'); ?>
                                        <button type="submit" class="button"><?php echo esc_html__('Check status', 'xerotact-shield-x-security'); ?></button>
                                    </form>
                                    <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                                        <input type="hidden" name="action" value="shieldx_deactivate_license">
                                        <input type="hidden" name="return_page" value="<?php echo esc_attr($returnPage); ?>">
                                        <?php wp_nonce_field('shieldx_deactivate_license'); ?>
                                        <button type="submit" class="button"><?php echo esc_html__('Deactivate', 'xerotact-shield-x-security'); ?></button>
                                    </form>
                                </div>
                            <?php endif; ?>
                        </div>
                    </section>
                </div>
        <?php
    }

    private function closePage(): void
    {
        if ($this->renderingAjaxContent) {
            return;
        }

        ?>
                <?php do_action('shieldx_admin_modals', $this->queryKey('page', 'xerotact-shield-x-security'), $this); ?>
                <?php $this->renderScanProgressModal(); ?>
                <div class="shieldx-modal" data-shieldx-modal hidden>
                    <div class="shieldx-modal-backdrop" data-shieldx-modal-close></div>
                    <section class="shieldx-modal-card" role="dialog" aria-modal="true" aria-labelledby="shieldx-modal-title" aria-describedby="shieldx-modal-intro">
                        <button type="button" class="shieldx-modal-close" data-shieldx-modal-close aria-label="<?php echo esc_attr__('Close dialog', 'xerotact-shield-x-security'); ?>">
                            <span aria-hidden="true">&times;</span>
                        </button>
                        <p class="shieldx-modal-kicker" data-shieldx-modal-kicker></p>
                        <h2 id="shieldx-modal-title" data-shieldx-modal-title></h2>
                        <p id="shieldx-modal-intro" class="shieldx-muted" data-shieldx-modal-intro></p>
                        <ol class="shieldx-modal-steps" data-shieldx-modal-steps></ol>
                        <div class="shieldx-modal-commands" data-shieldx-modal-commands-container hidden></div>
                        <div class="shieldx-modal-actions">
                            <a class="button button-primary" href="#" data-shieldx-modal-action></a>
                            <button type="button" class="button" data-shieldx-modal-close><?php echo esc_html__('Close', 'xerotact-shield-x-security'); ?></button>
                        </div>
                    </section>
                </div>
            </div>
        </div>
        <?php
    }

    /**
     * Keep recovery-state warnings visible on every SHIELD-X admin page. These
     * markers are intentionally simple files, so the notice must name the exact
     * path an administrator or host support can remove.
     *
     * @param array<string,mixed> $status
     */
    private function renderSafetyBlockerNotices(array $status): void
    {
        $lockdown = isset($status['lockdown']) && is_array($status['lockdown']) ? $status['lockdown'] : [];

        if (!empty($lockdown['killswitch_active'])) {
            $path = (string) ($lockdown['killswitch_path'] ?? ($health['lockdown_killswitch_path'] ?? 'uploads/.shield-x/lockdown/disable.json'));
            ?>
            <div class="notice notice-warning shieldx-safety-notice">
                <p>
                    <strong><?php echo esc_html__('SHIELD-X Lockdown recovery marker is active.', 'xerotact-shield-x-security'); ?></strong>
                    <?php echo esc_html__('Filesystem Lockdown is intentionally suspended until this file is removed or Lockdown is reapplied.', 'xerotact-shield-x-security'); ?>
                </p>
                <p>
                    <?php echo esc_html__('Recovery marker:', 'xerotact-shield-x-security'); ?>
                    <code><?php echo esc_html($path); ?></code>
                </p>
            </div>
            <?php
        }

    }

    private function renderScanProgressModal(): void
    {
        ?>
                <div
                    class="shieldx-scan-modal"
                    data-shieldx-scan-modal
                    data-running-title="<?php echo esc_attr__('Scanning in progress', 'xerotact-shield-x-security'); ?>"
                    data-running-intro="<?php echo esc_attr__('SHIELD-X is processing this scan in small batches so large sites stay below PHP and server timeout limits.', 'xerotact-shield-x-security'); ?>"
                    data-complete-title="<?php echo esc_attr__('Scan complete', 'xerotact-shield-x-security'); ?>"
                    data-complete-intro="<?php echo esc_attr__('The scan finished. Open the results when you are ready.', 'xerotact-shield-x-security'); ?>"
                    data-failed-title="<?php echo esc_attr__('Scan stopped', 'xerotact-shield-x-security'); ?>"
                    data-failed-intro="<?php echo esc_attr__('SHIELD-X could not continue this scan session.', 'xerotact-shield-x-security'); ?>"
                    data-result-label="<?php echo esc_attr__('View results', 'xerotact-shield-x-security'); ?>"
                    data-back-label="<?php echo esc_attr__('Back to scanner', 'xerotact-shield-x-security'); ?>"
                    data-default-scope="<?php echo esc_attr__('Selected scan scopes', 'xerotact-shield-x-security'); ?>"
                    data-request-error="<?php echo esc_attr__('Scan request failed.', 'xerotact-shield-x-security'); ?>"
                    data-json-error="<?php echo esc_attr__('SHIELD-X expected a JSON scan response.', 'xerotact-shield-x-security'); ?>"
                    data-timeout-error="<?php echo esc_attr__('Scan took too long. Please try again.', 'xerotact-shield-x-security'); ?>"
                    data-failure-title="<?php echo esc_attr__('Failed download', 'xerotact-shield-x-security'); ?>"
                    data-failure-title-plural="<?php echo esc_attr__('Failed downloads', 'xerotact-shield-x-security'); ?>"
                    hidden
                >
                    <div class="shieldx-scan-modal-backdrop" data-shieldx-scan-modal-hide></div>
                    <section class="shieldx-scan-modal-card" role="dialog" aria-modal="true" aria-labelledby="shieldx-scan-modal-title" aria-describedby="shieldx-scan-modal-intro">
                        <button type="button" class="shieldx-modal-close" data-shieldx-scan-modal-hide aria-label="<?php echo esc_attr__('Hide scan progress', 'xerotact-shield-x-security'); ?>">
                            <span aria-hidden="true">&times;</span>
                        </button>
                        <h2 id="shieldx-scan-modal-title" data-shieldx-scan-modal-title><?php echo esc_html__('Scanning in progress', 'xerotact-shield-x-security'); ?></h2>
                        <p id="shieldx-scan-modal-intro" class="shieldx-muted" data-shieldx-scan-modal-intro><?php echo esc_html__('SHIELD-X is processing this scan in small batches so large sites stay below PHP and server timeout limits.', 'xerotact-shield-x-security'); ?></p>
                        <p class="shieldx-scan-modal-stage" data-shieldx-scan-modal-stage hidden></p>
                        <div class="shieldx-scan-modal-bar" role="progressbar" aria-label="<?php echo esc_attr__('Scan progress', 'xerotact-shield-x-security'); ?>" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" data-shieldx-scan-modal-progressbar>
                            <span data-shieldx-scan-modal-fill></span>
                        </div>
                        <div class="shieldx-scan-modal-meta">
                            <span><small data-shieldx-scan-modal-percent-label><?php echo esc_html__('Progress', 'xerotact-shield-x-security'); ?></small><strong data-shieldx-scan-modal-percent>0%</strong></span>
                            <span><small data-shieldx-scan-modal-files-label><?php echo esc_html__('Files', 'xerotact-shield-x-security'); ?></small><strong data-shieldx-scan-modal-files>0 / 0</strong></span>
                            <span><small data-shieldx-scan-modal-batches-label><?php echo esc_html__('Batches', 'xerotact-shield-x-security'); ?></small><strong data-shieldx-scan-modal-batches>0</strong></span>
                        </div>
                        <p class="shieldx-scan-modal-scope" data-shieldx-scan-modal-scope><?php echo esc_html__('Selected scan scopes', 'xerotact-shield-x-security'); ?></p>
                        <div class="shieldx-scan-modal-details" data-shieldx-scan-modal-details hidden></div>
                        <p class="shieldx-scan-modal-error" data-shieldx-scan-modal-error hidden></p>
                        <div class="shieldx-modal-actions">
                            <a class="button button-primary" href="#" data-shieldx-scan-modal-result hidden><?php echo esc_html__('View results', 'xerotact-shield-x-security'); ?></a>
                            <a class="button" href="#" data-shieldx-scan-modal-continue hidden><?php echo esc_html__('Continue scan now', 'xerotact-shield-x-security'); ?></a>
                            <button type="button" class="button" data-shieldx-scan-modal-hide><?php echo esc_html__('Hide', 'xerotact-shield-x-security'); ?></button>
                        </div>
                    </section>
                </div>
        <?php
    }

    /**
     * @param array<string,mixed> $state
     */
    private function officialCoreReadyForScan(array $state): bool
    {
        return !empty($state['prepared']) && (int) ($state['hash_count'] ?? 0) > 0;
    }

    /**
     * @param array<string,mixed> $state
     */
    private function renderOfficialCorePromptModal(array $state): void
    {
        $version = (string) ($state['version'] ?? '');
        $archiveUrl = (string) ($state['archive_url'] ?? '');
        ?>
                <div class="shieldx-form-modal shieldx-core-prompt-modal" data-shieldx-core-prompt-modal hidden>
                    <div class="shieldx-form-modal-backdrop" data-shieldx-core-prompt-close></div>
                    <section class="shieldx-form-modal-card" role="dialog" aria-modal="true" aria-labelledby="shieldx-core-prompt-title" aria-describedby="shieldx-core-prompt-intro">
                        <button type="button" class="shieldx-modal-close" data-shieldx-core-prompt-close aria-label="<?php echo esc_attr__('Close official core prompt', 'xerotact-shield-x-security'); ?>">
                            <span aria-hidden="true">&times;</span>
                        </button>
                        <p class="shieldx-modal-kicker"><?php echo esc_html__('Recommended before first file scan', 'xerotact-shield-x-security'); ?></p>
                        <h2 id="shieldx-core-prompt-title"><?php echo esc_html__('Download official WordPress core?', 'xerotact-shield-x-security'); ?></h2>
                        <p id="shieldx-core-prompt-intro" class="shieldx-muted">
                            <?php echo esc_html__('SHIELD-X can download the matching official WordPress release and build clean file hashes before scanning. This improves core-file comparisons and reduces false alarms.', 'xerotact-shield-x-security'); ?>
                        </p>
                        <dl class="shieldx-detail-list shieldx-detail-list--compact">
                            <?php $this->renderDetail(__('Installed version', 'xerotact-shield-x-security'), $version !== '' ? $version : __('Unknown', 'xerotact-shield-x-security')); ?>
                            <?php $this->renderDetail(__('Release source', 'xerotact-shield-x-security'), $archiveUrl !== '' ? $archiveUrl : __('WordPress.org release archive', 'xerotact-shield-x-security')); ?>
                        </dl>
                        <div class="shieldx-modal-actions shieldx-core-prompt-actions">
                            <form
                                method="post"
                                action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
                                data-shieldx-core-progress-form
                                data-shieldx-progress-poll-delay="250"
                                data-shieldx-progress-refresh-selector="#shieldx-core-compare"
                                data-shieldx-progress-refresh-url="<?php echo esc_url(admin_url('admin.php?page=shield-x-scan')); ?>"
                                data-shieldx-progress-running-title="<?php echo esc_attr__('Official core download in progress', 'xerotact-shield-x-security'); ?>"
                                data-shieldx-progress-running-intro="<?php echo esc_attr__('SHIELD-X is downloading, verifying, extracting, and hashing the official WordPress core package in safe stages.', 'xerotact-shield-x-security'); ?>"
                                data-shieldx-progress-complete-title="<?php echo esc_attr__('Official core ready', 'xerotact-shield-x-security'); ?>"
                                data-shieldx-progress-complete-intro="<?php echo esc_attr__('Official WordPress core hashes are ready. Core files that match the clean package are trusted by hash.', 'xerotact-shield-x-security'); ?>"
                            >
                                <input type="hidden" name="action" value="shieldx_prepare_core_release">
                                <?php wp_nonce_field('shieldx_prepare_core_release'); ?>
                                <button type="submit" class="button button-primary" data-shieldx-core-progress-button data-running-label="<?php echo esc_attr__('Downloading...', 'xerotact-shield-x-security'); ?>"><?php echo esc_html__('Download official core', 'xerotact-shield-x-security'); ?></button>
                            </form>
                            <button type="button" class="button" data-shieldx-core-prompt-scan-anyway><?php echo esc_html__('Run scan without core', 'xerotact-shield-x-security'); ?></button>
                            <button type="button" class="button" data-shieldx-core-prompt-close><?php echo esc_html__('Cancel', 'xerotact-shield-x-security'); ?></button>
                        </div>
                    </section>
                </div>
        <?php
    }

    /**
     * @param array<string,mixed> $status
     */
    private function renderSubnav(string $activePage, array $status, ?array $warnings = null): void
    {
        $items = [
            'shield-x-setup' => ['label' => __('Setup', 'xerotact-shield-x-security'), 'icon' => 'badge-check'],
            'shield-x' => ['label' => __('Overview', 'xerotact-shield-x-security'), 'icon' => 'health-shield'],
            'shield-x-waf' => ['label' => __('Firewall', 'xerotact-shield-x-security'), 'icon' => 'pc-firewall'],
            'shield-x-login-security' => ['label' => __('Login Security', 'xerotact-shield-x-security'), 'icon' => 'fingerprint-scan'],
            'shield-x-scan' => ['label' => __('Malware/Virus', 'xerotact-shield-x-security'), 'icon' => 'cube-scan'],
            'shield-x-backup' => ['label' => __('Backup', 'xerotact-shield-x-security'), 'icon' => 'database-stats'],
            'shield-x-hardening' => ['label' => __('Hardening', 'xerotact-shield-x-security'), 'icon' => 'badge-check'],
            'shield-x-lockdown' => ['label' => __('Lockdown', 'xerotact-shield-x-security'), 'icon' => 'lock'],
            'shield-x-activity' => ['label' => __('Incident/Activities', 'xerotact-shield-x-security'), 'icon' => 'activity'],
            'shield-x-log-rotation' => ['label' => __('Cleanup', 'xerotact-shield-x-security'), 'icon' => 'tools'],
        ];
        $warnings = is_array($warnings) ? $warnings : $this->subnavWarnings($status);
        ?>
        <nav class="shieldx-subnav" aria-label="<?php echo esc_attr__('SHIELD-X sections', 'xerotact-shield-x-security'); ?>">
            <?php foreach ($items as $page => $item) : ?>
                <?php
                $warningState = $this->navigationWarningState($warnings, $page, $page === $activePage);
                ?>
                <a class="<?php echo esc_attr((string) $warningState['class']); ?>" href="<?php echo esc_url(admin_url('admin.php?page=' . $page)); ?>"<?php if ($page === $activePage) : ?> aria-current="page"<?php endif; ?><?php if ((string) $warningState['label'] !== '') : ?> title="<?php echo esc_attr((string) $warningState['label']); ?>"<?php endif; ?>>
                    <?php echo wp_kses(Iconoir::svg((string) $item['icon'], 'shieldx-icon shieldx-subnav-icon'), $this->allowedIconSvg()); ?>
                    <span><?php echo esc_html((string) $item['label']); ?></span>
                    <?php if (!empty($warningState['needs'])) : ?>
                        <span class="shieldx-subnav-warning" aria-hidden="true">
                            <?php echo wp_kses(Iconoir::svg('warning-triangle', 'shieldx-icon shieldx-subnav-warning-icon'), $this->allowedIconSvg()); ?>
                        </span>
                        <span class="shieldx-screen-reader-text"><?php echo esc_html((string) $warningState['label']); ?></span>
                    <?php endif; ?>
                </a>
            <?php endforeach; ?>
        </nav>
        <?php
    }

    /**
     * @param array<string,array{warning?:array<int,string>,notice?:array<int,string>}> $warnings
     * @return array{class:string,needs:bool,label:string}
     */
    private function navigationWarningState(array $warnings, string $page, bool $active): array
    {
        $pageWarnings = isset($warnings[$page]) && is_array($warnings[$page]) ? $warnings[$page] : [];
        $hardWarnings = isset($pageWarnings['warning']) && is_array($pageWarnings['warning']) ? $pageWarnings['warning'] : [];
        $softWarnings = isset($pageWarnings['notice']) && is_array($pageWarnings['notice']) ? $pageWarnings['notice'] : [];
        $needsSetup = !empty($hardWarnings) || !empty($softWarnings);
        $classes = [];

        if ($active) {
            $classes[] = 'is-active';
        }

        if (!empty($hardWarnings)) {
            $classes[] = 'has-warning';
        } elseif (!empty($softWarnings)) {
            $classes[] = 'has-notice';
        }

        $labelItems = !empty($hardWarnings) ? $hardWarnings : $softWarnings;
        $warningLabel = $needsSetup
            ? sprintf(
                /* translators: %s: comma-separated setup items that need attention. */
                empty($hardWarnings) ? __('Needs attention: %s', 'xerotact-shield-x-security') : __('Needs setup: %s', 'xerotact-shield-x-security'),
                implode(', ', array_slice(array_map('strval', $labelItems), 0, 3))
            )
            : '';

        return [
            'class' => implode(' ', $classes),
            'needs' => $needsSetup,
            'label' => $warningLabel,
        ];
    }

    /**
     * @param array<string,mixed> $status
     * @return array<string,array{warning:array<int,string>,notice:array<int,string>}>
     */
    private function subnavWarnings(array $status): array
    {
        $setup = $this->setupAssistant($status);
        $maintenance = isset($status['maintenance']) && is_array($status['maintenance']) ? $status['maintenance'] : [];
        $groups = [
            'shield-x-system' => ['system'],
            'shield-x-waf' => ['waf'],
            'shield-x-login-security' => ['login-security', 'recovery'],
            'shield-x-scan' => ['first-scan', 'scan-schedule'],
            'shield-x-backup' => ['restore-point'],
            'shield-x-hardening' => ['hardening'],
            'shield-x-lockdown' => ['lockdown'],
            'shield-x-activity' => ['profiler'],
            'shield-x-log-rotation' => ['log-rotation'],
        ];
        $incomplete = [];
        $requiredIncomplete = [];

        foreach (['steps', 'advanced'] as $bucket) {
            $setupSteps = isset($setup[$bucket]) && is_array($setup[$bucket]) ? $setup[$bucket] : [];
            foreach ($setupSteps as $step) {
                if (!is_array($step) || !empty($step['complete'])) {
                    continue;
                }

                $id = (string) ($step['id'] ?? '');
                if ($id !== '') {
                    $item = [
                        'title' => (string) ($step['title'] ?? $id),
                        'severity' => $this->setupStepSeverity($id),
                    ];
                    $incomplete[$id] = $item;

                    if ($bucket === 'steps') {
                        $requiredIncomplete[$id] = $item;
                    }
                }
            }
        }

        $warnings = [];
        foreach ($groups as $page => $stepIds) {
            foreach ($stepIds as $stepId) {
                if (isset($incomplete[$stepId])) {
                    $severity = (string) ($incomplete[$stepId]['severity'] ?? 'warning');
                    $bucket = $severity === 'notice' ? 'notice' : 'warning';
                    $warnings[$page][$bucket][] = (string) ($incomplete[$stepId]['title'] ?? $stepId);
                }
            }
        }

        if (!empty($requiredIncomplete)) {
            $warningCount = 0;
            foreach ($requiredIncomplete as $step) {
                if ((string) ($step['severity'] ?? 'warning') !== 'notice') {
                    $warningCount++;
                }
            }

            $warnings['shield-x-setup'][$warningCount > 0 ? 'warning' : 'notice'][] = __('Setup incomplete', 'xerotact-shield-x-security');
        }

        return $warnings;
    }

    /**
     */
    private function setupStepSeverity(string $stepId): string
    {
        unset($stepId);
        return 'warning';
    }

    /**
     * @param array<string,mixed> $status
     * @return array<string,mixed>
     */
    private function setupAssistant(array $status): array
    {
        $health = isset($status['health']) && is_array($status['health']) ? $status['health'] : [];
        $maintenance = isset($status['maintenance']) && is_array($status['maintenance']) ? $status['maintenance'] : [];
        $scanner = isset($status['scanner']) && is_array($status['scanner']) ? $status['scanner'] : [];
        $schedule = isset($scanner['schedule']) && is_array($scanner['schedule']) ? $scanner['schedule'] : [];
        $loginSecurity = isset($status['login_security']) && is_array($status['login_security']) ? $status['login_security'] : [];
        $posture = isset($status['posture']) && is_array($status['posture']) ? $status['posture'] : [];
        $hardening = isset($status['hardening']) && is_array($status['hardening']) ? $status['hardening'] : [];
        $waf = isset($status['waf']) && is_array($status['waf']) ? $status['waf'] : [];
        $restorePoints = isset($maintenance['restore_points']) && is_array($maintenance['restore_points']) ? $maintenance['restore_points'] : [];
        $databaseBackups = isset($maintenance['database_backups']) && is_array($maintenance['database_backups']) ? $maintenance['database_backups'] : [];
        $logRotation = isset($maintenance['log_rotation']) && is_array($maintenance['log_rotation']) ? $maintenance['log_rotation'] : [];
        $profiler = isset($status['user_profiler']) && is_array($status['user_profiler']) ? $status['user_profiler'] : [];
        $lockdown = isset($status['lockdown']) && is_array($status['lockdown']) ? $status['lockdown'] : [];

        $systemReady = !empty($health['storage_ready'])
            && !empty($health['schema_ready'])
            && !empty($health['rate_limit_storage_ready'])
            && !empty($health['compatibility_ready'])
            && !empty($health['login_secret_storage_ready']);
        $wafReady = (int) ($waf['rules_enabled'] ?? 0) > 0 && (int) ($waf['rules_loaded'] ?? 0) > 0 && (string) ($waf['mode'] ?? '') !== '';
        $loginRequired = !empty($loginSecurity['two_factor_required_roles']);
        $loginReady = !empty($loginSecurity['enabled'])
            && (!$loginRequired || (int) ($loginSecurity['required_users_pending'] ?? 0) === 0);
        $recoveryReady = !empty($loginSecurity['emergency_recovery_available']);
        $scanReady = !empty($scanner['last_scan']);
        $scheduleReady = $this->scanScheduleReadyCount($schedule) > 0;
        $hardeningReady = !empty($hardening['hide_wordpress_signature'])
            && !empty($hardening['disable_file_edit'])
            && HardeningSettings::defaultResponseHeadersEnabled(
                isset($hardening['response_headers']) && is_array($hardening['response_headers'])
                    ? $hardening['response_headers']
                    : []
            );
        $restoreReady = !empty($restorePoints['recent']) || !empty($databaseBackups['recent']);
        $logRotationReady = !empty($logRotation['enabled']);

        $steps = [
            $this->setupStep(
                'system',
                __('System check', 'xerotact-shield-x-security'),
                __('Confirm storage, schema, compatibility, and runtime state are ready.', 'xerotact-shield-x-security'),
                $systemReady,
                $systemReady ? __('Ready', 'xerotact-shield-x-security') : __('Needs review', 'xerotact-shield-x-security'),
                __('Open System', 'xerotact-shield-x-security'),
                admin_url('admin.php?page=shield-x-system'),
                'tools',
                __('Review System', 'xerotact-shield-x-security')
            ),
            $this->setupStep(
                'waf',
                __('WAF baseline', 'xerotact-shield-x-security'),
                __('Review WAF mode, bundled rule coverage, allowlists, and per-rule actions.', 'xerotact-shield-x-security'),
                $wafReady,
                $wafReady ? $this->formatLabel((string) ($waf['mode'] ?? 'log_only')) : __('Rules unavailable', 'xerotact-shield-x-security'),
                __('Review WAF', 'xerotact-shield-x-security'),
                admin_url('admin.php?page=shield-x-waf'),
                'pc-firewall',
                __('Review WAF', 'xerotact-shield-x-security')
            ),
            $this->setupStep(
                'login-security',
                __('Login security', 'xerotact-shield-x-security'),
                __('Keep authenticator 2FA available while role-based enrollment stays optional unless enforcement is enabled.', 'xerotact-shield-x-security'),
                $loginReady,
                /* translators: %d is the number of users in required roles who are not enrolled in 2FA. */
                $loginReady ? ($loginRequired ? __('Required roles protected', 'xerotact-shield-x-security') : __('Optional', 'xerotact-shield-x-security')) : sprintf(__('%d pending', 'xerotact-shield-x-security'), (int) ($loginSecurity['required_users_pending'] ?? 0)),
                __('Set up login security', 'xerotact-shield-x-security'),
                admin_url('admin.php?page=shield-x-login-security'),
                'fingerprint-scan',
                __('Review login security', 'xerotact-shield-x-security')
            ),
            $this->setupStep(
                'recovery',
                __('Emergency recovery', 'xerotact-shield-x-security'),
                __('Keep a tokenized recovery URL available for 2FA-protected safe-mode recovery.', 'xerotact-shield-x-security'),
                $recoveryReady,
                $recoveryReady ? __('URL ready', 'xerotact-shield-x-security') : __('URL missing', 'xerotact-shield-x-security'),
                __('Open recovery settings', 'xerotact-shield-x-security'),
                admin_url('admin.php?page=shield-x-login-security'),
                'lock',
                __('Review recovery', 'xerotact-shield-x-security')
            ),
            $this->setupStep(
                'first-scan',
                __('First scan baseline', 'xerotact-shield-x-security'),
                __('Run file, database, and vulnerability scans so SHIELD-X can compare future changes.', 'xerotact-shield-x-security'),
                $scanReady,
                $scanReady ? __('Baseline started', 'xerotact-shield-x-security') : __('No scan yet', 'xerotact-shield-x-security'),
                __('Run scan', 'xerotact-shield-x-security'),
                admin_url('admin.php?page=shield-x-scan'),
                'cube-scan',
                __('Review scans', 'xerotact-shield-x-security')
            ),
            $this->setupStep(
                'scan-schedule',
                __('Scheduled scans', 'xerotact-shield-x-security'),
                __('Enable at least one recurring scan or external protected scan URL.', 'xerotact-shield-x-security'),
                $scheduleReady,
                $this->scanScheduleSetupLabel($schedule),
                __('Configure schedules', 'xerotact-shield-x-security'),
                admin_url('admin.php?page=shield-x-scan#shieldx-file-scanner'),
                'calendar',
                __('Review schedules', 'xerotact-shield-x-security')
            ),
            $this->setupStep(
                'hardening',
                __('Hardening baseline', 'xerotact-shield-x-security'),
                __('Hide noisy WordPress signatures, disable file editing, and send browser security headers.', 'xerotact-shield-x-security'),
                $hardeningReady,
                $hardeningReady ? __('Baseline active', 'xerotact-shield-x-security') : __('Review settings', 'xerotact-shield-x-security'),
                __('Open Hardening', 'xerotact-shield-x-security'),
                admin_url('admin.php?page=shield-x-hardening'),
                'badge-check',
                __('Review Hardening', 'xerotact-shield-x-security')
            ),
            $this->setupStep(
                'restore-point',
                __('Restore point', 'xerotact-shield-x-security'),
                __('Create an initial restore point or database backup before using remediation and lockdown tools.', 'xerotact-shield-x-security'),
                $restoreReady,
                $restoreReady ? __('Backup available', 'xerotact-shield-x-security') : __('No restore point', 'xerotact-shield-x-security'),
                __('Create restore point', 'xerotact-shield-x-security'),
                admin_url('admin.php?page=shield-x-backup'),
                'database',
                __('Manage backups', 'xerotact-shield-x-security')
            ),
            $this->setupStep(
                'log-rotation',
                __('Log rotation', 'xerotact-shield-x-security'),
                __('Set retention for file logs, visitor profiler trails, WAF counters, and scan history.', 'xerotact-shield-x-security'),
                $logRotationReady,
                $logRotationReady ? __('Enabled', 'xerotact-shield-x-security') : __('Disabled', 'xerotact-shield-x-security'),
                __('Configure rotation', 'xerotact-shield-x-security'),
                admin_url('admin.php?page=shield-x-log-rotation'),
                'clock-rotate-right',
                __('Review rotation', 'xerotact-shield-x-security')
            ),
        ];

        $advanced = [
            $this->setupStep(
                'lockdown',
                __('Lockdown mode', 'xerotact-shield-x-security'),
                __('Apply filesystem lockdown only after scans, restore points, and update safety are ready.', 'xerotact-shield-x-security'),
                !empty($lockdown['enabled']) && empty($lockdown['killswitch_active']),
                !empty($lockdown['enabled']) ? __('Enabled', 'xerotact-shield-x-security') : __('Optional', 'xerotact-shield-x-security'),
                __('Open Lockdown', 'xerotact-shield-x-security'),
                admin_url('admin.php?page=shield-x-lockdown'),
                'lock',
                __('Review Lockdown', 'xerotact-shield-x-security')
            ),
            $this->setupStep(
                'profiler',
                __('Visitor profiler', 'xerotact-shield-x-security'),
                __('Optionally collect first-party visitor trails for investigation and blocking workflows.', 'xerotact-shield-x-security'),
                !empty($profiler['enabled']),
                !empty($profiler['enabled']) ? __('Enabled', 'xerotact-shield-x-security') : __('Opt-in', 'xerotact-shield-x-security'),
                __('Open Incident/Activities', 'xerotact-shield-x-security'),
                admin_url('admin.php?page=shield-x-activity'),
                'group',
                __('Review Incident/Activities', 'xerotact-shield-x-security')
            ),
        ];

        $firstIncomplete = '';
        foreach ($steps as $index => $step) {
            if (empty($step['complete'])) {
                $firstIncomplete = (string) $step['id'];
                $steps[$index]['current'] = true;
                break;
            }
        }

        $complete = 0;
        $remaining = [];
        foreach ($steps as $step) {
            if (!empty($step['complete'])) {
                $complete++;
            } else {
                $remaining[] = $step;
            }
        }

        $total = count($steps);

        return [
            'steps' => $steps,
            'advanced' => $advanced,
            'complete' => $complete,
            'total' => $total,
            'remaining' => $remaining,
            'percent' => $total > 0 ? (int) round(($complete / $total) * 100) : 100,
            'first_incomplete' => $firstIncomplete,
        ];
    }

    /**
     * @return array<string,mixed>
     */
    private function setupStep(string $id, string $title, string $description, bool $complete, string $state, string $action, string $url, string $icon, string $completeAction = ''): array
    {
        return [
            'id' => $id,
            'title' => $title,
            'description' => $description,
            'complete' => $complete,
            'state' => $state,
            'action' => $action,
            'complete_action' => $completeAction,
            'url' => $url,
            'icon' => $icon,
            'current' => false,
        ];
    }

    /**
     * @param array<string,mixed> $status
     */
    private function renderAddonStatusPanel(array $status, string $context = 'overview'): void
    {
        $addons = isset($status['addons']) && is_array($status['addons']) ? $status['addons'] : [];
        $items = isset($addons['items']) && is_array($addons['items']) ? $addons['items'] : [];
        if ($items === []) {
            return;
        }

        $extensions = isset($addons['extensions']) && is_array($addons['extensions']) ? $addons['extensions'] : [];
        $activePage = $context === 'setup' ? 'shield-x-setup' : 'shield-x';
        $actionsById = [];
        foreach (AddonRegistry::fromWordPress()->headerActions($activePage) as $action) {
            if (!is_array($action)) {
                continue;
            }

            $actionId = sanitize_key((string) ($action['id'] ?? ''));
            if ($actionId !== '') {
                $actionsById[$actionId] = $action;
            }
        }
        /* translators: %d: SHIELD-X module API version. */
        $apiLabel = sprintf(__('API v%d', 'xerotact-shield-x-security'), (int) ($addons['api_version'] ?? SHIELDX_ADDON_API_VERSION));
        ?>
        <section class="shieldx-panel shieldx-panel--full shieldx-addon-status">
            <div class="shieldx-section-head">
                <div>
                    <h2><?php echo esc_html__('Extensions', 'xerotact-shield-x-security'); ?></h2>
                    <span><?php echo esc_html($context === 'setup' ? __('Private extensions loaded by SHIELD-X', 'xerotact-shield-x-security') : __('Registered private extensions', 'xerotact-shield-x-security')); ?></span>
                </div>
                <span><?php echo esc_html($apiLabel); ?></span>
            </div>
            <div class="shieldx-rule-table-wrap">
                <table class="widefat striped shieldx-addon-status-table shieldx-table--compact">
                    <thead>
                        <tr>
                            <th><?php echo esc_html__('Extension', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Status', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Signal', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Last update', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Action', 'xerotact-shield-x-security'); ?></th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ($items as $item) : ?>
                            <?php
                            $item = is_array($item) ? $item : [];
                            $id = (string) ($item['id'] ?? '');
                            $extension = isset($extensions[$id]) && is_array($extensions[$id]) ? $extensions[$id] : [];
                            $enabled = array_key_exists('enabled', $item) ? !empty($item['enabled']) : true;
                            $extension['enabled'] = $enabled;
                            $openAnomalies = $enabled ? (int) ($extension['open_anomalies'] ?? 0) : 0;
                            if (!$enabled) {
                                $statusLabel = __('Paused', 'xerotact-shield-x-security');
                            } elseif ($openAnomalies > 0) {
                                /* translators: %d: number of open module anomalies. */
                                $statusLabel = sprintf(_n('%d anomaly', '%d anomalies', $openAnomalies, 'xerotact-shield-x-security'), $openAnomalies);
                            } else {
                                $statusLabel = __('Loaded', 'xerotact-shield-x-security');
                            }
                            $statusClass = $openAnomalies > 0 ? 'warn' : ($enabled ? 'good' : 'info');
                            $adminPage = (string) ($item['admin_page'] ?? '');
                            $href = $adminPage !== '' ? admin_url('admin.php?page=' . $adminPage) : '';
                            $action = isset($actionsById[$id]) && is_array($actionsById[$id]) ? $actionsById[$id] : [];
                            $modalId = (string) ($action['modal_id'] ?? '');
                            $modalLabel = (string) ($action['label'] ?? __('Open extension settings', 'xerotact-shield-x-security'));
                            $lastUpdateAt = $this->addonLastUpdateTime($item, $extension);
                            ?>
                            <tr>
                                <td>
                                    <strong><?php echo esc_html((string) ($item['name'] ?? $id)); ?></strong>
                                    <span class="shieldx-muted"><?php echo esc_html((string) ($item['version'] ?? '')); ?></span>
                                </td>
                                <td><span class="<?php echo esc_attr('shieldx-status-pill shieldx-status-pill--' . $statusClass); ?>"><?php echo esc_html($statusLabel); ?></span></td>
                                <td><?php echo esc_html($this->addonStatusSignal($extension)); ?></td>
                                <td>
                                    <?php if ($lastUpdateAt !== '') : ?>
                                        <time datetime="<?php echo esc_attr(gmdate('c', (int) strtotime($lastUpdateAt))); ?>"><?php echo esc_html($this->formatTime($lastUpdateAt)); ?></time>
                                    <?php else : ?>
                                        <span class="shieldx-muted" aria-label="<?php echo esc_attr__('Last update not recorded', 'xerotact-shield-x-security'); ?>">&mdash;</span>
                                    <?php endif; ?>
                                </td>
                                <td>
                                    <?php if ($href !== '') : ?>
                                        <a class="button button-small" href="<?php echo esc_url($href); ?>"><?php echo esc_html__('Open', 'xerotact-shield-x-security'); ?></a>
                                    <?php elseif ($modalId !== '') : ?>
                                        <button
                                            type="button"
                                            class="button button-small"
                                            data-shieldx-form-modal-open
                                            aria-controls="<?php echo esc_attr($modalId); ?>"
                                            title="<?php echo esc_attr($modalLabel); ?>"
                                        >
                                            <?php echo esc_html__('Open', 'xerotact-shield-x-security'); ?>
                                        </button>
                                    <?php else : ?>
                                        <span class="shieldx-muted"><?php echo esc_html__('No admin screen', 'xerotact-shield-x-security'); ?></span>
                                    <?php endif; ?>
                                </td>
                            </tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
            <?php if (!empty($addons['rejected'])) : ?>
                <?php
                /* translators: %d: number of modules rejected by manifest validation. */
                $rejectedLabel = sprintf(__('Rejected modules: %d', 'xerotact-shield-x-security'), (int) $addons['rejected']);
                ?>
                <p class="shieldx-muted"><?php echo esc_html($rejectedLabel); ?></p>
            <?php endif; ?>
        </section>
        <?php
    }

    /**
     * @param array<string,mixed> $extension
     */
    private function addonLastUpdateTime(array $item, array $extension): string
    {
        foreach (['updated_at', 'last_updated_at'] as $key) {
            $time = trim((string) ($item[$key] ?? ''));
            if ($time !== '' && strtotime($time)) {
                return $time;
            }
        }

        foreach (['updated_at', 'last_updated_at'] as $key) {
            $time = trim((string) ($extension[$key] ?? ''));
            if ($time !== '' && strtotime($time)) {
                return $time;
            }
        }

        return '';
    }

    /**
     * @param array<string,mixed> $extension
     */
    private function addonStatusSignal(array $extension): string
    {
        if ($extension === []) {
            return __('Registered', 'xerotact-shield-x-security');
        }

        if ((int) ($extension['open_anomalies'] ?? 0) > 0) {
            return __('Needs review', 'xerotact-shield-x-security');
        }

        if (!empty($extension['last_run_at'])) {
            /* translators: %s: last module run time. */
            return sprintf(__('Last run %s', 'xerotact-shield-x-security'), $this->formatTime((string) $extension['last_run_at']));
        }

        if (array_key_exists('enabled', $extension) && empty($extension['enabled'])) {
            return __('Ready when enabled', 'xerotact-shield-x-security');
        }

        return __('Ready', 'xerotact-shield-x-security');
    }

    /**
     * @param array<string,mixed> $status
     */
    private function renderSetupProgressPanel(array $status, ?array $setup = null): void
    {
        $setup = is_array($setup) ? $setup : $this->setupAssistant($status);
        $remaining = isset($setup['remaining']) && is_array($setup['remaining']) ? $setup['remaining'] : [];
        $percent = (int) ($setup['percent'] ?? 0);
        ?>
        <section class="shieldx-panel shieldx-panel--full shieldx-setup-progress">
            <div class="shieldx-setup-progress-main">
                <span class="shieldx-kicker"><?php echo esc_html__('Setup Assistant', 'xerotact-shield-x-security'); ?></span>
                <h2><?php echo esc_html($percent >= 100 ? __('Baseline setup complete', 'xerotact-shield-x-security') : __('Finish your SHIELD-X setup', 'xerotact-shield-x-security')); ?></h2>
                <p>
                    <?php
                    echo esc_html(sprintf(
                        /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                        __('%1$d of %2$d baseline steps are complete.', 'xerotact-shield-x-security'),
                        (int) ($setup['complete'] ?? 0),
                        (int) ($setup['total'] ?? 0)
                    ));
                    ?>
                </p>
                <div class="shieldx-setup-bar" aria-hidden="true"><span style="<?php echo esc_attr('width:' . (string) max(0, min(100, $percent)) . '%;'); ?>"></span></div>
            </div>
            <div class="shieldx-setup-progress-side">
                <strong><?php echo esc_html((string) $percent); ?>%</strong>
                <span><?php echo esc_html__('complete', 'xerotact-shield-x-security'); ?></span>
                <a class="button button-primary" href="<?php echo esc_url(admin_url('admin.php?page=shield-x-setup')); ?>"><?php echo esc_html($percent >= 100 ? __('Review setup', 'xerotact-shield-x-security') : __('Continue setup', 'xerotact-shield-x-security')); ?></a>
            </div>
            <?php if (!empty($remaining)) : ?>
                <div class="shieldx-setup-remaining">
                    <span><?php echo esc_html__('Remaining', 'xerotact-shield-x-security'); ?></span>
                    <?php foreach (array_slice($remaining, 0, 4) as $step) : ?>
                        <?php if (is_array($step)) : ?>
                            <a href="<?php echo esc_url((string) ($step['url'] ?? '')); ?>"><?php echo esc_html((string) ($step['title'] ?? '')); ?></a>
                        <?php endif; ?>
                    <?php endforeach; ?>
                </div>
            <?php endif; ?>
        </section>
        <?php
    }

    /**
     * @param array<string,mixed> $step
     */
    private function renderSetupStepCard(array $step, bool $optional = false): void
    {
        $complete = !empty($step['complete']);
        $current = !empty($step['current']) && !$complete;
        $tone = $complete ? 'good' : ($current ? 'warn' : 'info');
        $state = $complete ? __('Complete', 'xerotact-shield-x-security') : ($current ? __('Next step', 'xerotact-shield-x-security') : ($optional ? __('Optional', 'xerotact-shield-x-security') : __('To do', 'xerotact-shield-x-security')));
        $action = $complete && !empty($step['complete_action']) ? (string) $step['complete_action'] : (string) ($step['action'] ?? __('Open', 'xerotact-shield-x-security'));
        ?>
        <article class="<?php echo esc_attr('shieldx-setup-step shieldx-setup-step--' . $tone . ($current ? ' is-current' : '')); ?>">
            <div class="shieldx-setup-step-copy">
                <div class="shieldx-setup-step-title">
                    <h3><?php echo esc_html((string) ($step['title'] ?? '')); ?></h3>
                    <em class="<?php echo esc_attr('shieldx-pill shieldx-pill--' . $tone); ?>"><?php echo esc_html($state); ?></em>
                </div>
                <p><?php echo esc_html((string) ($step['description'] ?? '')); ?></p>
                <small><?php echo esc_html((string) ($step['state'] ?? '')); ?></small>
            </div>
            <a class="button" href="<?php echo esc_url((string) ($step['url'] ?? '')); ?>">
                <?php echo esc_html($action); ?>
            </a>
        </article>
        <?php
    }

    /**
     * @param array<string,mixed> $status
     * @param array{open:int,percent:int,items:array<int,string>} $readiness
     * @return array<int,array<string,mixed>>
     */
    private function overviewAttentionCards(array $status, array $readiness, array $incidentTimeline = []): array
    {
        $waf = isset($status['waf']) && is_array($status['waf']) ? $status['waf'] : [];
        $wafStats = isset($waf['stats']) && is_array($waf['stats']) ? $waf['stats'] : [];
        $wafSettings = isset($waf['settings']) && is_array($waf['settings']) ? $waf['settings'] : [];
        $rateLimit = isset($status['rate_limit']) && is_array($status['rate_limit']) ? $status['rate_limit'] : [];
        $rateRequest = isset($rateLimit['request']) && is_array($rateLimit['request']) ? $rateLimit['request'] : [];
        $rateLogin = isset($rateLimit['login']) && is_array($rateLimit['login']) ? $rateLimit['login'] : [];
        $scanner = isset($status['scanner']) && is_array($status['scanner']) ? $status['scanner'] : [];
        $loginSecurity = isset($status['login_security']) && is_array($status['login_security']) ? $status['login_security'] : [];
        $posture = isset($status['posture']) && is_array($status['posture']) ? $status['posture'] : [];
        $lockdown = isset($status['lockdown']) && is_array($status['lockdown']) ? $status['lockdown'] : [];
        $hardening = isset($status['hardening']) && is_array($status['hardening']) ? $status['hardening'] : [];
        $license = isset($status['licensing']) && is_array($status['licensing']) ? $status['licensing'] : [];
        $maintenance = isset($status['maintenance']) && is_array($status['maintenance']) ? $status['maintenance'] : [];
        $databaseBackups = isset($maintenance['database_backups']) && is_array($maintenance['database_backups']) ? $maintenance['database_backups'] : [];
        $restorePoints = isset($maintenance['restore_points']) && is_array($maintenance['restore_points']) ? $maintenance['restore_points'] : [];
        $dbBackupRecent = isset($databaseBackups['recent']) && is_array($databaseBackups['recent']) ? $databaseBackups['recent'] : [];
        $restoreRecent = isset($restorePoints['recent']) && is_array($restorePoints['recent']) ? $restorePoints['recent'] : [];
        $dbBackupSchedule = isset($databaseBackups['schedule']) && is_array($databaseBackups['schedule']) ? $databaseBackups['schedule'] : [];
        $restoreSchedule = isset($restorePoints['schedule']) && is_array($restorePoints['schedule']) ? $restorePoints['schedule'] : [];
        $scanSchedule = isset($scanner['schedule']) && is_array($scanner['schedule']) ? $scanner['schedule'] : [];
        $rulesLoaded = (int) ($waf['rules_loaded'] ?? 0);
        $rulesEnabled = (int) ($waf['rules_enabled'] ?? $rulesLoaded);
        $windowHits = (int) ($wafStats['window_hits'] ?? 0);
        $blockedHits = (int) ($wafStats['blocked_hits'] ?? 0);
        $wouldBlockHits = (int) ($wafStats['would_block_hits'] ?? 0);
        $wafMode = (string) ($waf['mode'] ?? ($wafSettings['mode'] ?? 'log_only'));
        $recentLimitedClients = (int) ($rateRequest['recent_limited_clients'] ?? 0);
        $trackedRequestClients = (int) ($rateRequest['tracked_clients'] ?? 0);
        $temporaryBlocks = $this->overviewListCount($wafSettings['temporary_blocks'] ?? []);
        $manualAccessEntries = $this->overviewListCount($wafSettings['ip_blocklist'] ?? [])
            + $this->overviewListCount($wafSettings['country_blocklist'] ?? [])
            + $this->overviewListCount($wafSettings['user_agent_blocklist'] ?? [])
            + $this->overviewListCount($wafSettings['referrer_blocklist'] ?? [])
            + $this->overviewListCount($wafSettings['hostname_blocklist'] ?? [])
            + $this->overviewListCount($wafSettings['reputation_ip_blocklist'] ?? [])
            + $this->overviewListCount($wafSettings['reputation_hostname_blocklist'] ?? [])
            + $this->overviewListCount($wafSettings['reputation_user_agent_blocklist'] ?? []);
        $currentIp = $this->overviewServerIp('REMOTE_ADDR');
        $serverIp = $this->overviewServerIp('SERVER_ADDR');
        $currentIpAllowlisted = $currentIp !== '' && $this->overviewListContainsExact((array) ($wafSettings['ip_allowlist'] ?? []), $currentIp);
        $serverIpBlocked = $serverIp !== '' && $this->overviewListContainsExact((array) ($wafSettings['ip_blocklist'] ?? []), $serverIp);
        $activeIssues = (int) ($scanner['active_issues'] ?? 0);
        $vulnerability = isset($scanner['vulnerability']) && is_array($scanner['vulnerability']) ? $scanner['vulnerability'] : [];
        $vulnerabilityFindings = max(
            0,
            (int) ($vulnerability['issues_found'] ?? 0),
            (int) ($vulnerability['open_findings'] ?? 0),
            (int) ($vulnerability['findings'] ?? 0),
            (int) ($vulnerability['issues'] ?? 0)
        );
        $lockedIdentities = (int) ($rateLogin['locked_identities'] ?? 0);
        $trackedLoginIdentities = (int) ($rateLogin['tracked_identities'] ?? 0);
        $adminsPending = (int) ($loginSecurity['admins_pending'] ?? 0);
        $adminsEnrolled = (int) ($loginSecurity['admins_enrolled'] ?? 0);
        $adminsTotal = (int) ($loginSecurity['admins_total'] ?? 0);
        $loginFailures = $this->overviewTimelineEventCount($incidentTimeline, ['login.failure'], 14);
        $twoFactorFailures = $this->overviewTimelineEventCount($incidentTimeline, ['login_security.2fa_reset_code_failed', 'login_security.2fa_setup_email_failed'], 14);
        $captchaFailures = $this->overviewTimelineEventCount($incidentTimeline, ['human_challenge.verify_failed'], 14);
        $hardeningWarnings = $this->overviewHardeningWarningCount($hardening);
        $licenseStatus = LicenseSettings::sanitizeStatus((string) ($license['status'] ?? LicenseSettings::STATUS_INACTIVE));
        $licenseLabel = (string) ($license['label'] ?? LicenseSettings::statusLabel($licenseStatus));
        $licenseNeedsAction = $licenseStatus !== LicenseSettings::STATUS_ACTIVE || empty($license['has_stored_key']);
        $cards = [];

        $cards[] = $this->overviewAttentionCard(
            'security_posture',
            __('Security posture', 'xerotact-shield-x-security'),
            (string) (int) ($posture['attention'] ?? 0),
            (int) ($posture['attention'] ?? 0) > 0 ? 'warn' : 'good',
            'health-shield',
            (int) ($posture['attention'] ?? 0) > 0 ? __('Read-only posture checks found items that need review.', 'xerotact-shield-x-security') : __('Read-only posture checks are clear.', 'xerotact-shield-x-security'),
            __('Review posture', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-system#shieldx-security-posture'),
            (int) ($posture['attention'] ?? 0) > 0,
            9
        );

        $cards[] = $this->overviewAttentionCard(
            'critical_security_issues',
            __('Critical security issues', 'xerotact-shield-x-security'),
            (string) ($activeIssues + $vulnerabilityFindings),
            ($activeIssues + $vulnerabilityFindings) > 0 ? 'warn' : 'good',
            'message-alert',
            ($activeIssues + $vulnerabilityFindings) > 0 ? __('Scanner, database, or vulnerability findings need review.', 'xerotact-shield-x-security') : __('No active scanner findings are waiting.', 'xerotact-shield-x-security'),
            __('Review findings', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-scan'),
            ($activeIssues + $vulnerabilityFindings) > 0,
            10
        );

        $cards[] = $this->overviewAttentionCard(
            'firewall_action',
            __('Firewall action needed', 'xerotact-shield-x-security'),
            $windowHits > 0 ? (string) $windowHits : $this->formatLabel($wafMode),
            ($windowHits > 0 || $wafMode === 'log_only' || ($rulesLoaded > 0 && $rulesEnabled < $rulesLoaded)) ? 'warn' : 'good',
            'pc-firewall',
            sprintf(
                /* translators: 1: WAF mode, 2: enabled WAF rules, 3: loaded WAF rules. */
                __('Mode %1$s. Rules %2$d/%3$d. Recent hits stay below in the activity chart.', 'xerotact-shield-x-security'),
                $this->formatLabel($wafMode),
                $rulesEnabled,
                $rulesLoaded
            ),
            __('Review WAF', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-waf'),
            $windowHits > 0 || $wafMode === 'log_only' || ($rulesLoaded > 0 && $rulesEnabled < $rulesLoaded),
            20
        );

        $activeAttackCount = (int) ($rateRequest['recent_limited_clients'] ?? 0) + $lockedIdentities + $temporaryBlocks;
        $cards[] = $this->overviewAttentionCard(
            'active_attack_sources',
            __('Active attack sources', 'xerotact-shield-x-security'),
            (string) $activeAttackCount,
            $activeAttackCount > 0 ? 'warn' : 'good',
            'dashboard-speed',
            $activeAttackCount > 0 ? __('Temporary blocks, request throttles, or login lockouts are active.', 'xerotact-shield-x-security') : __('No active throttled or blocked identities.', 'xerotact-shield-x-security'),
            __('Review access', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-waf-access'),
            $activeAttackCount > 0,
            30
        );

        $cards[] = $this->overviewAttentionCard(
            'access_control_review',
            __('Access control review', 'xerotact-shield-x-security'),
            (string) ($manualAccessEntries + $temporaryBlocks),
            ($manualAccessEntries + $temporaryBlocks) > 0 ? 'warn' : 'good',
            'list',
            ($manualAccessEntries + $temporaryBlocks) > 0 ? __('Manual or temporary access controls are currently active.', 'xerotact-shield-x-security') : __('No manual block or temporary access rules are active.', 'xerotact-shield-x-security'),
            __('Manage access', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-waf-access'),
            ($manualAccessEntries + $temporaryBlocks) > 0,
            40
        );

        $wafEnforced = in_array($wafMode, ['temporary', 'enforce'], true);
        $cards[] = $this->overviewAttentionCard(
            'current_ip_safety',
            __('Current IP safety', 'xerotact-shield-x-security'),
            $currentIpAllowlisted ? __('Allowlisted', 'xerotact-shield-x-security') : ($currentIp !== '' ? __('Detected', 'xerotact-shield-x-security') : __('Unknown', 'xerotact-shield-x-security')),
            ($wafEnforced && $currentIp !== '' && !$currentIpAllowlisted) ? 'warn' : 'good',
            'badge-check',
            $currentIpAllowlisted ? __('This admin IP is already on the WAF allowlist.', 'xerotact-shield-x-security') : __('Whitelist the current admin IP if you want explicit protection from accidental blocks.', 'xerotact-shield-x-security'),
            __('Whitelist IP', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-waf-access'),
            $wafEnforced && $currentIp !== '' && !$currentIpAllowlisted,
            50
        );

        $cards[] = $this->overviewAttentionCard(
            'server_ip_safety',
            __('Server IP safety', 'xerotact-shield-x-security'),
            $serverIpBlocked ? __('Blocked', 'xerotact-shield-x-security') : __('Automatic', 'xerotact-shield-x-security'),
            $serverIpBlocked ? 'warn' : 'good',
            'health-shield',
            $serverIpBlocked ? __('The server IP appears in the manual blocklist and should be reviewed.', 'xerotact-shield-x-security') : __('Same-server requests are protected by the automatic server-IP bypass.', 'xerotact-shield-x-security'),
            __('Review server IP', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-waf-access'),
            $serverIpBlocked,
            60
        );

        $loginGapCount = $adminsPending + $lockedIdentities + (empty($loginSecurity['secret_storage']['ready']) ? 1 : 0);
        $cards[] = $this->overviewAttentionCard(
            'login_security_gaps',
            __('Login security gaps', 'xerotact-shield-x-security'),
            (string) $loginGapCount,
            $loginGapCount > 0 ? 'warn' : 'good',
            'lock',
            $loginGapCount > 0 ? __('2FA enrollment, secret storage, or lockout state needs attention.', 'xerotact-shield-x-security') : __('Login security protections are ready.', 'xerotact-shield-x-security'),
            __('Fix login security', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-login-security'),
            $loginGapCount > 0,
            70
        );

        $cards[] = $this->overviewAttentionCard(
            'two_factor_enrollment',
            __('2FA enrollment', 'xerotact-shield-x-security'),
            (string) $adminsPending,
            $adminsPending > 0 ? 'warn' : 'good',
            'fingerprint-scan',
            $adminsPending > 0 ? __('Administrators still need authenticator enrollment.', 'xerotact-shield-x-security') : __('All required administrators are enrolled.', 'xerotact-shield-x-security'),
            __('Enforce 2FA', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-login-security'),
            $adminsPending > 0,
            80
        );

        $recoveryReady = !empty($loginSecurity['owner_recovery_available']) || !empty($loginSecurity['emergency_recovery_available']);
        $cards[] = $this->overviewAttentionCard(
            'emergency_recovery',
            __('Emergency recovery', 'xerotact-shield-x-security'),
            $recoveryReady ? __('Ready', 'xerotact-shield-x-security') : __('Missing', 'xerotact-shield-x-security'),
            $recoveryReady ? 'good' : 'warn',
            'lifebelt',
            $recoveryReady ? __('A recovery path is configured without exposing the token.', 'xerotact-shield-x-security') : __('Configure an emergency recovery path before strict blocking.', 'xerotact-shield-x-security'),
            __('Configure recovery', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-login-security'),
            !$recoveryReady,
            90
        );

        $dbBackupStale = $dbBackupRecent === [] || $this->overviewRecentItemsStale($dbBackupRecent, 7);
        $cards[] = $this->overviewAttentionCard(
            'backup_protection',
            $dbBackupStale ? __('Backup protection missing', 'xerotact-shield-x-security') : __('Backup protection', 'xerotact-shield-x-security'),
            $dbBackupRecent === [] ? __('Missing', 'xerotact-shield-x-security') : (string) count($dbBackupRecent),
            $dbBackupStale ? 'warn' : 'good',
            'database',
            $dbBackupStale ? __('Create or refresh a database backup before risky changes.', 'xerotact-shield-x-security') : __('Recent database backups are available.', 'xerotact-shield-x-security'),
            __('Create backup', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-backup#shieldx-database-backups'),
            $dbBackupStale,
            100
        );

        $restorePointStale = $restoreRecent === [] || $this->overviewRecentItemsStale($restoreRecent, 7);
        $cards[] = $this->overviewAttentionCard(
            'restore_point_missing',
            $restorePointStale ? __('Restore point missing', 'xerotact-shield-x-security') : __('Restore point protection', 'xerotact-shield-x-security'),
            $restoreRecent === [] ? __('Missing', 'xerotact-shield-x-security') : (string) count($restoreRecent),
            $restorePointStale ? 'warn' : 'good',
            'database-stats',
            $restorePointStale ? __('Create a restore point so file changes have a rollback path.', 'xerotact-shield-x-security') : __('Recent restore points are available.', 'xerotact-shield-x-security'),
            __('Create restore point', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-backup#shieldx-restore-points'),
            $restorePointStale,
            110
        );

        $backupDirInsideRoot = $this->overviewPathInsideWordPressRoot((string) ($databaseBackups['directory'] ?? ''))
            || $this->overviewPathInsideWordPressRoot((string) ($restorePoints['directory'] ?? ''));
        $cards[] = $this->overviewAttentionCard(
            'backup_storage_warning',
            __('Backup storage warning', 'xerotact-shield-x-security'),
            $backupDirInsideRoot ? __('Review', 'xerotact-shield-x-security') : __('OK', 'xerotact-shield-x-security'),
            $backupDirInsideRoot ? 'warn' : 'good',
            'folder-settings',
            $backupDirInsideRoot ? __('Local backup storage appears to be inside the WordPress tree.', 'xerotact-shield-x-security') : __('Backup storage paths do not need immediate attention.', 'xerotact-shield-x-security'),
            __('Review storage', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-backup'),
            $backupDirInsideRoot,
            120
        );

        $schedulerAddonEnabled = $this->overviewAddonEnabled($status, 'shieldx-scheduler');
        $scanScheduleIssues = $schedulerAddonEnabled ? $this->overviewScheduleIssueCount($scanSchedule) : 0;
        $dbBackupScheduleIssues = $schedulerAddonEnabled ? $this->overviewScheduleIssueCount($dbBackupSchedule) : 0;
        $restoreScheduleIssues = $schedulerAddonEnabled ? $this->overviewScheduleIssueCount($restoreSchedule) : 0;
        $scheduleIssueCount = $scanScheduleIssues + $dbBackupScheduleIssues + $restoreScheduleIssues;
        $cards[] = $this->overviewAttentionCard(
            'scheduler_problems',
            __('Scheduler problems', 'xerotact-shield-x-security'),
            $schedulerAddonEnabled ? (string) $scheduleIssueCount : __('Disabled', 'xerotact-shield-x-security'),
            ($schedulerAddonEnabled && $scheduleIssueCount > 0) ? 'warn' : ($schedulerAddonEnabled ? 'good' : 'info'),
            'calendar-check',
            !$schedulerAddonEnabled
                ? __('The Scheduler extension is disabled, so schedule-specific warnings are hidden until it is enabled.', 'xerotact-shield-x-security')
                : ($scheduleIssueCount > 0 ? __('One or more enabled schedules are missing a next run.', 'xerotact-shield-x-security') : __('Enabled schedules have a next run or external runner.', 'xerotact-shield-x-security')),
            $schedulerAddonEnabled ? __('Fix scheduler', 'xerotact-shield-x-security') : __('Enable Scheduler', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-scheduler'),
            $schedulerAddonEnabled && $scheduleIssueCount > 0,
            130
        );

        $cards[] = $this->overviewAttentionCard(
            'license_attention',
            __('License attention', 'xerotact-shield-x-security'),
            $licenseLabel,
            $licenseNeedsAction ? 'warn' : 'good',
            'badge-check',
            $licenseNeedsAction ? __('License status needs validation for remote services and support.', 'xerotact-shield-x-security') : __('License is active for this site session.', 'xerotact-shield-x-security'),
            __('Validate license', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-setup#shieldx-license-modal'),
            $licenseNeedsAction,
            140
        );

        $cards[] = $this->overviewAttentionCard(
            'hardening_gaps',
            __('Hardening gaps', 'xerotact-shield-x-security'),
            (string) $hardeningWarnings,
            $hardeningWarnings > 0 ? 'warn' : 'good',
            'tools',
            $hardeningWarnings > 0 ? __('Hardening measures still have warnings or host-review items.', 'xerotact-shield-x-security') : __('Hardening baseline is ready.', 'xerotact-shield-x-security'),
            __('Apply hardening', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-hardening'),
            $hardeningWarnings > 0,
            160
        );

        $lockdownNeedsAction = !empty($lockdown['killswitch_active']);
        $cards[] = $this->overviewAttentionCard(
            'lockdown_gaps',
            __('Lockdown gaps', 'xerotact-shield-x-security'),
            $lockdownNeedsAction ? __('Killswitch', 'xerotact-shield-x-security') : (!empty($lockdown['enabled']) ? __('Enabled', 'xerotact-shield-x-security') : __('Optional', 'xerotact-shield-x-security')),
            $lockdownNeedsAction ? 'warn' : 'good',
            'lock',
            $lockdownNeedsAction ? __('Lockdown killswitch is active and should be reviewed.', 'xerotact-shield-x-security') : __('Lockdown has no active emergency warning.', 'xerotact-shield-x-security'),
            __('Apply lockdown', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-lockdown'),
            $lockdownNeedsAction,
            170
        );

        $cardStats = [
            'critical_security_issues' => [
                $this->overviewStat(__('Scanner', 'xerotact-shield-x-security'), (string) $activeIssues, 'cube-scan'),
                $this->overviewStat(__('Vulnerable', 'xerotact-shield-x-security'), (string) $vulnerabilityFindings, 'warning-triangle'),
            ],
            'firewall_action' => [
                $this->overviewStat(__('WAF hits', 'xerotact-shield-x-security'), (string) $windowHits, 'pc-firewall'),
                $this->overviewStat(__('Blocked', 'xerotact-shield-x-security'), (string) $blockedHits, 'lock'),
                $this->overviewStat(__('Would block', 'xerotact-shield-x-security'), (string) $wouldBlockHits, 'eye'),
                $this->overviewStat(__('Challenge fails', 'xerotact-shield-x-security'), (string) $captchaFailures, 'fingerprint-scan'),
            ],
            'active_attack_sources' => [
                $this->overviewStat(__('Rate limited', 'xerotact-shield-x-security'), (string) $recentLimitedClients, 'dashboard-speed'),
                $this->overviewStat(__('Login locks', 'xerotact-shield-x-security'), (string) $lockedIdentities, 'lock'),
                $this->overviewStat(__('Temp blocks', 'xerotact-shield-x-security'), (string) $temporaryBlocks, 'clock'),
                $this->overviewStat(__('Tracked IPs', 'xerotact-shield-x-security'), (string) $trackedRequestClients, 'group'),
            ],
            'access_control_review' => [
                $this->overviewStat(__('Manual rules', 'xerotact-shield-x-security'), (string) $manualAccessEntries, 'list'),
                $this->overviewStat(__('Temp blocks', 'xerotact-shield-x-security'), (string) $temporaryBlocks, 'clock'),
            ],
            'current_ip_safety' => [
                $this->overviewStat(__('Current IP', 'xerotact-shield-x-security'), $currentIp !== '' ? $currentIp : __('Unknown', 'xerotact-shield-x-security'), 'fingerprint-scan'),
                $this->overviewStat(__('Allowlist', 'xerotact-shield-x-security'), $currentIpAllowlisted ? __('Yes', 'xerotact-shield-x-security') : __('No', 'xerotact-shield-x-security'), 'badge-check'),
            ],
            'server_ip_safety' => [
                $this->overviewStat(__('Server IP', 'xerotact-shield-x-security'), $serverIp !== '' ? $serverIp : __('Unknown', 'xerotact-shield-x-security'), 'pc-firewall'),
                $this->overviewStat(__('Blocked', 'xerotact-shield-x-security'), $serverIpBlocked ? __('Yes', 'xerotact-shield-x-security') : __('No', 'xerotact-shield-x-security'), 'lock'),
            ],
            'login_security_gaps' => [
                $this->overviewStat(__('Login fails', 'xerotact-shield-x-security'), (string) $loginFailures, 'warning-triangle'),
                $this->overviewStat(__('Login locks', 'xerotact-shield-x-security'), (string) $lockedIdentities, 'lock'),
                $this->overviewStat(__('Tracked IDs', 'xerotact-shield-x-security'), (string) $trackedLoginIdentities, 'group'),
                $this->overviewStat(__('2FA fails', 'xerotact-shield-x-security'), (string) $twoFactorFailures, 'fingerprint-scan'),
            ],
            'two_factor_enrollment' => [
                $this->overviewStat(__('Enrolled', 'xerotact-shield-x-security'), (string) $adminsEnrolled, 'badge-check'),
                $this->overviewStat(__('Pending', 'xerotact-shield-x-security'), (string) $adminsPending, 'clock'),
                $this->overviewStat(__('Admins', 'xerotact-shield-x-security'), (string) $adminsTotal, 'group'),
            ],
            'emergency_recovery' => [
                $this->overviewStat(__('Owner URL', 'xerotact-shield-x-security'), !empty($loginSecurity['owner_recovery_available']) ? __('Ready', 'xerotact-shield-x-security') : __('Missing', 'xerotact-shield-x-security'), 'lifebelt'),
                $this->overviewStat(__('Emergency', 'xerotact-shield-x-security'), !empty($loginSecurity['emergency_recovery_available']) ? __('Ready', 'xerotact-shield-x-security') : __('Missing', 'xerotact-shield-x-security'), 'warning-triangle'),
            ],
            'backup_protection' => [
                $this->overviewStat(__('Backups', 'xerotact-shield-x-security'), (string) count($dbBackupRecent), 'database'),
                $this->overviewStat(__('Schedule', 'xerotact-shield-x-security'), $dbBackupScheduleIssues > 0 ? __('Issue', 'xerotact-shield-x-security') : __('OK', 'xerotact-shield-x-security'), 'calendar-check'),
            ],
            'restore_point_missing' => [
                $this->overviewStat(__('Restore pts', 'xerotact-shield-x-security'), (string) count($restoreRecent), 'clock-rotate-right'),
                $this->overviewStat(__('Schedule', 'xerotact-shield-x-security'), $restoreScheduleIssues > 0 ? __('Issue', 'xerotact-shield-x-security') : __('OK', 'xerotact-shield-x-security'), 'calendar-check'),
            ],
            'backup_storage_warning' => [
                $this->overviewStat(__('Path', 'xerotact-shield-x-security'), $backupDirInsideRoot ? __('Inside root', 'xerotact-shield-x-security') : __('Private', 'xerotact-shield-x-security'), 'folder-settings'),
            ],
            'scheduler_problems' => [
                $this->overviewStat(__('Scan', 'xerotact-shield-x-security'), (string) $scanScheduleIssues, 'cube-scan'),
                $this->overviewStat(__('Backup', 'xerotact-shield-x-security'), (string) $dbBackupScheduleIssues, 'database'),
                $this->overviewStat(__('Restore', 'xerotact-shield-x-security'), (string) $restoreScheduleIssues, 'clock-rotate-right'),
            ],
            'license_attention' => [
                $this->overviewStat(__('Status', 'xerotact-shield-x-security'), $licenseLabel, 'badge-check'),
                $this->overviewStat(__('Key stored', 'xerotact-shield-x-security'), !empty($license['has_stored_key']) ? __('Yes', 'xerotact-shield-x-security') : __('No', 'xerotact-shield-x-security'), 'lock'),
            ],
            'hardening_gaps' => [
                $this->overviewStat(__('Warnings', 'xerotact-shield-x-security'), (string) $hardeningWarnings, 'warning-triangle'),
            ],
            'lockdown_gaps' => [
                $this->overviewStat(__('Enabled', 'xerotact-shield-x-security'), !empty($lockdown['enabled']) ? __('Yes', 'xerotact-shield-x-security') : __('No', 'xerotact-shield-x-security'), 'lock'),
                $this->overviewStat(__('Killswitch', 'xerotact-shield-x-security'), $lockdownNeedsAction ? __('Active', 'xerotact-shield-x-security') : __('Off', 'xerotact-shield-x-security'), 'warning-triangle'),
            ],
        ];

        foreach ($cards as &$card) {
            $cardId = (string) ($card['id'] ?? '');
            if ($cardId !== '' && isset($cardStats[$cardId])) {
                $card['stats'] = $cardStats[$cardId];
            }
        }
        unset($card);

        usort($cards, static function (array $left, array $right): int {
            $leftActive = !empty($left['is_active']) ? 1 : 0;
            $rightActive = !empty($right['is_active']) ? 1 : 0;

            if ($leftActive !== $rightActive) {
                return $rightActive <=> $leftActive;
            }

            return (int) ($left['priority'] ?? 999) <=> (int) ($right['priority'] ?? 999);
        });

        $activeCards = 0;
        foreach ($cards as $card) {
            if (!empty($card['is_active'])) {
                $activeCards++;
            }
        }

        $wafMode = $this->formatLabel((string) ($status['waf']['mode'] ?? 'log_only'));
        $summaryCard = $this->overviewAttentionCard(
            'needs_attention',
            __('Needs attention', 'xerotact-shield-x-security'),
            $activeCards > 0 ? (string) $activeCards : __('None', 'xerotact-shield-x-security'),
            $activeCards > 0 ? 'warn' : 'good',
            'health-shield',
            $activeCards > 0
                ? sprintf(
                    /* translators: %d: number of overview cards that currently need action. */
                    _n('%d area needs action.', '%d areas need action.', $activeCards, 'xerotact-shield-x-security'),
                    $activeCards
                )
                : __('No action needed right now.', 'xerotact-shield-x-security'),
            __('View report', 'xerotact-shield-x-security'),
            admin_url('admin.php?page=shield-x-system'),
            $activeCards > 0,
            0,
            true,
            (int) ($readiness['percent'] ?? 100)
        );
        $summaryCard['status_label'] = __('Protection mode', 'xerotact-shield-x-security');
        $summaryCard['status_note'] = sprintf(
            /* translators: %s is the current WAF mode label. */
            __('The WAF is running in %s mode. Login lockouts are enforced when enabled.', 'xerotact-shield-x-security'),
            $wafMode
        );

        array_unshift($cards, $summaryCard);

        return array_slice($cards, 0, count(self::OVERVIEW_CARD_IDS));
    }

    /**
     * @return array<string,mixed>
     */
    private function overviewAttentionCard(string $id, string $title, string $value, string $tone, string $icon, string $description, string $actionLabel, string $targetUrl, bool $isActive, int $priority, bool $summary = false, int $scorePercent = 100): array
    {
        return [
            'id' => $id,
            'title' => $title,
            'value' => $value,
            'tone' => in_array($tone, ['good', 'warn', 'info'], true) ? $tone : 'info',
            'icon' => $icon,
            'description' => $description,
            'action_label' => $actionLabel,
            'target_url' => $targetUrl,
            'is_active' => $isActive,
            'priority' => $priority,
            'summary' => $summary,
            'score_percent' => max(0, min(100, $scorePercent)),
        ];
    }

    /**
     * @return array{label:string,value:string,icon:string}
     */
    private function overviewStat(string $label, string $value, string $icon = 'activity'): array
    {
        return [
            'label' => $label,
            'value' => $value,
            'icon' => $icon,
        ];
    }

    /**
     * @param array<string,mixed> $incidentTimeline
     * @param array<int,string>   $sourceEvents
     */
    private function overviewTimelineEventCount(array $incidentTimeline, array $sourceEvents, int $days): int
    {
        $summaries = isset($incidentTimeline['daily_summaries']) && is_array($incidentTimeline['daily_summaries'])
            ? $incidentTimeline['daily_summaries']
            : [];
        if ($summaries === [] || $sourceEvents === []) {
            return 0;
        }

        $days = max(1, min(365, $days));
        $today = strtotime(gmdate('Y-m-d 00:00:00')) ?: time();
        $cutoff = strtotime('-' . (string) ($days - 1) . ' days', $today) ?: ($today - (($days - 1) * 86400));
        $cutoffDate = gmdate('Y-m-d', $cutoff);
        $sourceMap = array_fill_keys($sourceEvents, true);
        $count = 0;

        foreach ($summaries as $summary) {
            if (!is_array($summary)) {
                continue;
            }

            $date = (string) ($summary['summary_date'] ?? '');
            $event = (string) ($summary['source_event'] ?? '');
            if ($date === '' || $date < $cutoffDate || !isset($sourceMap[$event])) {
                continue;
            }

            $count += max(0, (int) ($summary['count'] ?? 0));
        }

        return $count;
    }

    /**
     * @param mixed $value
     */
    private function overviewListCount($value): int
    {
        return is_array($value) ? count($value) : 0;
    }

    /**
     * @param array<int,mixed> $list
     */
    private function overviewListContainsExact(array $list, string $value): bool
    {
        foreach ($list as $item) {
            if (is_scalar($item) && trim((string) $item) === $value) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param array<string,mixed> $status
     */
    private function overviewAddonEnabled(array $status, string $addonId): bool
    {
        $addons = isset($status['addons']) && is_array($status['addons']) ? $status['addons'] : [];
        $items = isset($addons['items']) && is_array($addons['items']) ? $addons['items'] : [];

        foreach ($items as $item) {
            if (!is_array($item) || (string) ($item['id'] ?? '') !== $addonId) {
                continue;
            }

            return array_key_exists('enabled', $item) ? !empty($item['enabled']) : true;
        }

        return false;
    }

    private function overviewServerIp(string $key): string
    {
        $value = '';
        if (isset($_SERVER[$key]) && is_scalar($_SERVER[$key])) {
            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Server value is unslashed and accepted only after FILTER_VALIDATE_IP below.
            $raw = function_exists('wp_unslash') ? wp_unslash($_SERVER[$key]) : $_SERVER[$key];
            $value = is_scalar($raw) ? trim((string) $raw) : '';
        }

        return filter_var($value, FILTER_VALIDATE_IP) ? $value : '';
    }

    /**
     * @param array<int,array<string,mixed>> $items
     */
    private function overviewRecentItemsStale(array $items, int $days): bool
    {
        $newest = 0;
        foreach ($items as $item) {
            if (!is_array($item)) {
                continue;
            }

            $timestamp = strtotime((string) ($item['created_at'] ?? $item['updated_at'] ?? ''));
            if ($timestamp !== false && $timestamp > $newest) {
                $newest = $timestamp;
            }
        }

        $daySeconds = defined('DAY_IN_SECONDS') ? (int) DAY_IN_SECONDS : 86400;

        return $newest <= 0 || $newest < time() - ($days * $daySeconds);
    }

    private function overviewPathInsideWordPressRoot(string $path): bool
    {
        if ($path === '' || !defined('ABSPATH')) {
            return false;
        }

        $root = realpath((string) ABSPATH);
        if (!is_string($root) || $root === '') {
            return false;
        }

        $target = realpath($path);
        $target = is_string($target) && $target !== '' ? $target : $path;
        $root = rtrim(str_replace('\\', '/', $root), '/');
        $target = rtrim(str_replace('\\', '/', $target), '/');

        return $target === $root || strpos($target . '/', $root . '/') === 0;
    }

    /**
     * @param array<string,mixed> $schedule
     */
    private function overviewScheduleIssueCount(array $schedule): int
    {
        $count = 0;
        $sections = isset($schedule['sections']) && is_array($schedule['sections']) ? $schedule['sections'] : [];
        if ($sections !== []) {
            foreach ($sections as $section) {
                if (is_array($section) && $this->overviewSingleScheduleNeedsAttention($section)) {
                    $count++;
                }
            }

            return $count;
        }

        return $this->overviewSingleScheduleNeedsAttention($schedule) ? 1 : 0;
    }

    /**
     * @param array<string,mixed> $schedule
     */
    private function overviewSingleScheduleNeedsAttention(array $schedule): bool
    {
        $enabled = !empty($schedule['enabled']);
        $external = !empty($schedule['external_runner_enabled']);
        if (!$enabled || $external) {
            return false;
        }

        return empty($schedule['next_run']) && empty($schedule['next_run_timestamp']);
    }

    /**
     * @param array<string,mixed> $hardening
     */
    private function overviewHardeningWarningCount(array $hardening): int
    {
        $measures = isset($hardening['security_measures']) && is_array($hardening['security_measures']) ? $hardening['security_measures'] : [];
        $warnings = 0;

        foreach ($measures as $measure) {
            if (!is_array($measure)) {
                continue;
            }

            if ((string) ($measure['status'] ?? '') !== 'ready') {
                $warnings++;
            }
        }

        return $warnings;
    }

    /**
     * The overview card should be explainable. Report concrete attention items
    /**
     * The overview card should be explainable. Report concrete attention items
     * instead of a synthetic "site health" score that looks more precise than it is.
     *
     * @param array<string,mixed> $status
     * @param array<string,mixed>|null $setup
     * @return array{open:int,percent:int,items:array<int,string>}
     */
    private function readinessReport(array $status, ?array $setup = null): array
    {
        $setup = is_array($setup) ? $setup : $this->setupAssistant($status);
        $health = isset($status['health']) && is_array($status['health']) ? $status['health'] : [];
        $scanner = isset($status['scanner']) && is_array($status['scanner']) ? $status['scanner'] : [];
        $humanChallenge = isset($status['human_challenge']) && is_array($status['human_challenge']) ? $status['human_challenge'] : [];
        $lockdown = isset($status['lockdown']) && is_array($status['lockdown']) ? $status['lockdown'] : [];
        $remaining = isset($setup['remaining']) && is_array($setup['remaining']) ? $setup['remaining'] : [];
        $items = [];

        foreach ($remaining as $step) {
            if (is_array($step)) {
                $title = trim((string) ($step['title'] ?? ''));
                if ($title !== '') {
                    $items[$title] = $title;
                }
            }
        }

        $activeIssues = (int) ($scanner['active_issues'] ?? 0);
        if ($activeIssues > 0) {
            $label = sprintf(
                /* translators: %d: number of active scanner findings. */
                _n('%d active scanner finding', '%d active scanner findings', $activeIssues, 'xerotact-shield-x-security'),
                $activeIssues
            );
            $items[$label] = $label;
        }

        $challengeRequested = !empty($humanChallenge['login_enabled'])
            || !empty($humanChallenge['login_fullscreen_enabled'])
            || !empty($humanChallenge['password_reset_enabled'])
            || !empty($humanChallenge['waf_enabled']);
        if ($challengeRequested && empty($humanChallenge['configured'])) {
            $label = __('Human Challenge needs configuration', 'xerotact-shield-x-security');
            $items[$label] = $label;
        }

        if (!empty($lockdown['enabled']) && !empty($lockdown['killswitch_active'])) {
            $label = __('Lockdown killswitch active', 'xerotact-shield-x-security');
            $items[$label] = $label;
        }

        return [
            'open' => count($items),
            'percent' => max(0, min(100, (int) ($setup['percent'] ?? 100))),
            'items' => array_values($items),
        ];
    }

    private function formatLabel(string $value): string
    {
        return ucwords(str_replace('_', ' ', $value));
    }

    private function humanChallengeProviderLabel(string $provider): string
    {
        if (strtolower($provider) === 'cap') {
            return __('Cap.js Challenge (Captcha)', 'xerotact-shield-x-security');
        }

        return __('Disabled', 'xerotact-shield-x-security');
    }

    private function scanTypeLabel(string $type): string
    {
        $type = ScheduledScanSettings::sanitizeType($type);

        if ($type === ScheduledScanSettings::TYPE_DATABASE) {
            return __('Database', 'xerotact-shield-x-security');
        }

        if ($type === ScheduledScanSettings::TYPE_VULNERABILITY) {
            return __('Vulnerability', 'xerotact-shield-x-security');
        }

        return __('File', 'xerotact-shield-x-security');
    }

    private function scanAnchorForType(string $type): string
    {
        $type = ScheduledScanSettings::sanitizeType($type);

        if ($type === ScheduledScanSettings::TYPE_DATABASE) {
            return 'shieldx-database-scanner';
        }

        if ($type === ScheduledScanSettings::TYPE_VULNERABILITY) {
            return 'shieldx-vulnerability-scanner';
        }

        return 'shieldx-file-scanner';
    }

    /**
     * @param array<string,mixed> $schedule
     * @return array<string,mixed>
     */
    private function scanScheduleForType(array $schedule, string $type): array
    {
        $type = ScheduledScanSettings::sanitizeType($type);
        $sections = isset($schedule['sections']) && is_array($schedule['sections']) ? $schedule['sections'] : [];

        if (isset($sections[$type]) && is_array($sections[$type])) {
            return $sections[$type];
        }

        if ($type === ScheduledScanSettings::TYPE_FILE) {
            return $schedule;
        }

        return (new ScheduledScanSettings())->section($type);
    }

    /**
     * @param array<string,mixed> $schedule
     */
    private function scanScheduleEnabledCount(array $schedule): int
    {
        $count = 0;

        foreach (ScheduledScanSettings::types() as $type) {
            $section = $this->scanScheduleForType($schedule, $type);
            if (!empty($section['enabled'])) {
                $count++;
            }
        }

        return $count;
    }

    /**
     * @param array<string,mixed> $schedule
     */
    private function scanScheduleReadyCount(array $schedule): int
    {
        $count = 0;

        foreach (ScheduledScanSettings::types() as $type) {
            $section = $this->scanScheduleForType($schedule, $type);
            if (!empty($section['enabled']) || !empty($section['external_runner_enabled'])) {
                $count++;
            }
        }

        return $count;
    }

    /**
     * @param array<string,mixed> $schedule
     */
    private function scanScheduleExternalCount(array $schedule): int
    {
        $count = 0;

        foreach (ScheduledScanSettings::types() as $type) {
            $section = $this->scanScheduleForType($schedule, $type);
            if (!empty($section['external_runner_enabled'])) {
                $count++;
            }
        }

        return $count;
    }

    /**
     * @param array<string,mixed> $schedule
     */
    private function scanScheduleSetupLabel(array $schedule): string
    {
        $wpCronCount = $this->scanScheduleEnabledCount($schedule);
        $externalCount = $this->scanScheduleExternalCount($schedule);

        if ($externalCount > 0 && $wpCronCount === 0) {
            return sprintf(
                /* translators: 1: number of protected external scan URLs enabled, 2: total scanner types. */
                __('%1$d/%2$d external URLs ready', 'xerotact-shield-x-security'),
                $externalCount,
                count(ScheduledScanSettings::types())
            );
        }

        return sprintf(
            /* translators: 1: number of WP-Cron scanner schedules enabled, 2: total scanner types. */
            __('%1$d/%2$d schedules on', 'xerotact-shield-x-security'),
            $wpCronCount,
            count(ScheduledScanSettings::types())
        );
    }

    private function dashboardGreeting(string $activePage): string
    {
        if ($activePage !== 'shield-x') {
            return __('SHIELD-X Security Center', 'xerotact-shield-x-security');
        }

        $timestamp = function_exists('current_time') ? (int) current_time('timestamp') : time();
        $hour = (int) gmdate('G', $timestamp);

        if ($hour < 12) {
            return __('Good morning,', 'xerotact-shield-x-security');
        }

        if ($hour < 18) {
            return __('Good afternoon,', 'xerotact-shield-x-security');
        }

        return __('Good evening,', 'xerotact-shield-x-security');
    }

    private function pageHeading(string $activePage): string
    {
        $headings = [
            'shield-x' => __('Dashboard', 'xerotact-shield-x-security'),
            'shield-x-setup' => __('Setup Assistant', 'xerotact-shield-x-security'),
            'shield-x-addons' => __('Extensions', 'xerotact-shield-x-security'),
            'shield-x-waf' => __('Firewall', 'xerotact-shield-x-security'),
            'shield-x-waf-access' => __('WAF Access', 'xerotact-shield-x-security'),
            'shield-x-login-security' => __('Login Security', 'xerotact-shield-x-security'),
            'shield-x-scan' => __('Malware/Virus', 'xerotact-shield-x-security'),
            'shield-x-backup' => __('Backup', 'xerotact-shield-x-security'),
            'shield-x-backup-restore' => __('Restore Point Contents', 'xerotact-shield-x-security'),
            'shield-x-hardening' => __('Hardening', 'xerotact-shield-x-security'),
            'shield-x-lockdown' => __('Lockdown', 'xerotact-shield-x-security'),
            'shield-x-activity' => __('Incident/Activities', 'xerotact-shield-x-security'),
            'shield-x-log-rotation' => __('Cleanup', 'xerotact-shield-x-security'),
            'shield-x-system' => __('System', 'xerotact-shield-x-security'),
        ];

        return (string) ($headings[$activePage] ?? __('SHIELD-X', 'xerotact-shield-x-security'));
    }

    /**
     * @param array<string,mixed> $status
     */
    private function pageSubtitle(string $activePage, array $status): string
    {
        if ($activePage === 'shield-x') {
            return __('Here is what is happening with your site today.', 'xerotact-shield-x-security');
        }

        if ($activePage === 'shield-x-setup') {
            return __('Step-by-step baseline setup for SHIELD-X protection.', 'xerotact-shield-x-security');
        }

        if ($activePage === 'shield-x-addons') {
            return __('Enable, disable, and open private SHIELD-X extensions.', 'xerotact-shield-x-security');
        }

        if ($activePage === 'shield-x-log-rotation') {
            return __('Clean stale temporary files and rotate file/database logs by selected retention targets.', 'xerotact-shield-x-security');
        }

        if ($activePage === 'shield-x-backup') {
            return __('Restore points, database backups, quarantine recovery, and file shadow copies.', 'xerotact-shield-x-security');
        }

        if ($activePage === 'shield-x-backup-restore') {
            return __('Review the files and database captured by this restore point.', 'xerotact-shield-x-security');
        }

        if ($activePage === 'shield-x-waf-access') {
            return __('Search and manage WAF blocklists, allowlists, reputation entries, and recent blocked clients.', 'xerotact-shield-x-security');
        }

        $wordpressVersion = isset($status['wordpress']['version']) ? (string) $status['wordpress']['version'] : '';
        $phpVersion = isset($status['environment']['php_version']) ? (string) $status['environment']['php_version'] : '';

        if ($wordpressVersion === '' && function_exists('get_bloginfo')) {
            $wordpressVersion = (string) get_bloginfo('version');
        }

        if ($phpVersion === '') {
            $phpVersion = PHP_VERSION;
        }

        return sprintf(
            /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
            __('WordPress %1$s on PHP %2$s', 'xerotact-shield-x-security'),
            $wordpressVersion,
            $phpVersion
        );
    }

    /**
     * @param array<int,array<string,mixed>> $cards
     * @param array<string,string> $disabledCardIds
     */
    private function renderOverviewAttentionCards(array $cards, array $disabledCardIds): void
    {
        $scoreCard = null;
        $visibleCards = [];
        $disabledCards = [];

        foreach ($cards as $card) {
            $id = (string) ($card['id'] ?? '');
            if ($id === '') {
                continue;
            }

            if (isset($disabledCardIds[$id])) {
                $disabledCards[] = $card;
                continue;
            }

            if (!empty($card['summary'])) {
                $scoreCard = $card;
                continue;
            }

            $visibleCards[] = $card;
        }

        $hasScore = is_array($scoreCard);
        if ($hasScore) {
            $activeVisibleCards = 0;
            foreach ($visibleCards as $card) {
                if (!empty($card['is_active'])) {
                    $activeVisibleCards++;
                }
            }

            $scoreCard['value'] = $activeVisibleCards > 0 ? (string) $activeVisibleCards : __('None', 'xerotact-shield-x-security');
            $scoreCard['tone'] = $activeVisibleCards > 0 ? 'warn' : 'good';
            $scoreCard['is_active'] = $activeVisibleCards > 0;
            $scoreCard['description'] = $activeVisibleCards > 0
                ? sprintf(
                    /* translators: %d: number of visible overview cards that currently need action. */
                    _n('%d visible area needs action.', '%d visible areas need action.', $activeVisibleCards, 'xerotact-shield-x-security'),
                    $activeVisibleCards
                )
                : __('No visible action needed right now.', 'xerotact-shield-x-security');
        }

        $containerClass = 'shieldx-metrics shieldx-metrics--overview shieldx-overview-attention';
        if (!$hasScore) {
            $containerClass .= ' shieldx-overview-attention--no-score';
        }
        ?>
        <section
            class="<?php echo esc_attr($containerClass); ?>"
            aria-label="<?php echo esc_attr__('SHIELD-X overview', 'xerotact-shield-x-security'); ?>"
            data-shieldx-overview-cards
            data-shieldx-overview-card-url="<?php echo esc_url(admin_url('admin-ajax.php')); ?>"
            data-shieldx-overview-card-nonce="<?php echo esc_attr(wp_create_nonce('shieldx_overview_card_visibility')); ?>"
        >
            <div class="shieldx-overview-score-slot" data-shieldx-overview-score-slot<?php if (!$hasScore) : ?> hidden<?php endif; ?>>
                <?php if ($hasScore) : ?>
                    <?php $this->renderOverviewAttentionCard($scoreCard, false); ?>
                <?php endif; ?>
            </div>
            <div class="shieldx-overview-metric-grid" data-shieldx-overview-visible-grid>
                <?php foreach ($visibleCards as $card) : ?>
                    <?php $this->renderOverviewAttentionCard($card, false); ?>
                <?php endforeach; ?>
            </div>
        </section>
        <section class="shieldx-overview-disabled" data-shieldx-overview-disabled-section<?php if ($disabledCards === []) : ?> hidden<?php endif; ?>>
            <button type="button" class="button shieldx-overview-disabled-toggle" data-shieldx-overview-show-disabled aria-expanded="false">
                <?php echo esc_html__('Show all', 'xerotact-shield-x-security'); ?>
            </button>
            <div class="shieldx-overview-disabled-grid" data-shieldx-overview-disabled-grid hidden>
                <?php foreach ($disabledCards as $card) : ?>
                    <?php $this->renderOverviewAttentionCard($card, true); ?>
                <?php endforeach; ?>
            </div>
        </section>
        <?php
    }

    /**
     * @param array<string,mixed> $card
     */
    private function renderOverviewAttentionCard(array $card, bool $disabled): void
    {
        $id = sanitize_key((string) ($card['id'] ?? ''));
        $title = (string) ($card['title'] ?? '');
        $value = (string) ($card['value'] ?? '');
        $description = (string) ($card['description'] ?? '');
        $tone = (string) ($card['tone'] ?? 'info');
        $icon = (string) ($card['icon'] ?? 'activity');
        $actionLabel = (string) ($card['action_label'] ?? __('Open', 'xerotact-shield-x-security'));
        $targetUrl = (string) ($card['target_url'] ?? '');
        $isSummary = !empty($card['summary']);
        $stats = isset($card['stats']) && is_array($card['stats']) ? $card['stats'] : [];
        $statusLabel = (string) ($card['status_label'] ?? '');
        $statusNote = (string) ($card['status_note'] ?? '');
        $class = 'shieldx-metric shieldx-overview-attention-card shieldx-metric--' . $tone;
        if ($isSummary) {
            $class .= ' shieldx-metric--score shieldx-overview-attention-card--summary';
        }
        if (!$isSummary && !empty($card['is_active'])) {
            $class .= ' is-active';
        }
        if ($disabled) {
            $class .= ' is-disabled';
        }
        ?>
        <article
            class="<?php echo esc_attr($class); ?>"
            data-shieldx-overview-card
            data-shieldx-overview-card-id="<?php echo esc_attr($id); ?>"
            data-shieldx-overview-card-disabled="<?php echo esc_attr($disabled ? '1' : '0'); ?>"
            data-shieldx-overview-card-summary="<?php echo esc_attr($isSummary ? '1' : '0'); ?>"
        >
            <?php if (!$isSummary) : ?>
                <div class="shieldx-metric-head">
                    <span><?php echo esc_html($title); ?></span>
                    <span class="shieldx-metric-icon"><?php echo wp_kses(Iconoir::svg($icon), $this->allowedIconSvg()); ?></span>
                </div>
            <?php endif; ?>
            <?php if ($isSummary) : ?>
                <div class="shieldx-score-ring" title="<?php echo esc_attr($description); ?>" style="<?php echo esc_attr('--shieldx-score:' . (string) ((int) ($card['score_percent'] ?? 100)) . ';'); ?>">
                    <strong><?php echo esc_html($value); ?></strong>
                    <small><?php echo esc_html($description); ?></small>
                </div>
                <div class="shieldx-overview-summary-copy">
                    <small class="shieldx-overview-summary-detail"><?php echo esc_html($description); ?></small>
                    <?php if ($statusLabel !== '' && $statusNote !== '') : ?>
                        <span class="shieldx-overview-summary-status">
                            <strong><?php echo esc_html($statusLabel); ?></strong>
                            <span><?php echo esc_html($statusNote); ?></span>
                        </span>
                    <?php endif; ?>
                </div>
            <?php else : ?>
                <strong><?php echo esc_html($value); ?></strong>
                <?php if ($description !== '') : ?>
                    <small class="shieldx-metric-detail"><?php echo esc_html($description); ?></small>
                <?php endif; ?>
                <?php if ($stats !== []) : ?>
                    <div class="shieldx-overview-card-stats" aria-label="<?php echo esc_attr__('Overview card statistics', 'xerotact-shield-x-security'); ?>">
                        <?php foreach (array_slice($stats, 0, 4) as $stat) : ?>
                            <?php
                            $statLabel = is_array($stat) ? (string) ($stat['label'] ?? '') : '';
                            $statValue = is_array($stat) ? (string) ($stat['value'] ?? '') : '';
                            $statIcon = is_array($stat) ? (string) ($stat['icon'] ?? 'activity') : 'activity';
                            if ($statLabel === '' || $statValue === '') {
                                continue;
                            }
                            ?>
                            <span class="shieldx-overview-card-stat" title="<?php echo esc_attr($statLabel . ': ' . $statValue); ?>">
                                <?php echo wp_kses(Iconoir::svg($statIcon, 'shieldx-icon shieldx-overview-card-stat-icon'), $this->allowedIconSvg()); ?>
                                <span class="shieldx-screen-reader-text"><?php echo esc_html($statLabel . ': '); ?></span>
                                <span class="shieldx-overview-card-stat-value"><?php echo esc_html($statValue); ?></span>
                            </span>
                        <?php endforeach; ?>
                    </div>
                <?php endif; ?>
            <?php endif; ?>
            <?php if ($targetUrl !== '') : ?>
                <a class="shieldx-card-link" href="<?php echo esc_url($targetUrl); ?>">
                    <?php echo esc_html($actionLabel); ?>
                    <?php echo wp_kses(Iconoir::svg('nav-arrow-right', 'shieldx-icon shieldx-card-link-icon'), $this->allowedIconSvg()); ?>
                </a>
            <?php endif; ?>
            <?php if (!$isSummary) : ?>
                <button
                    type="button"
                    class="shieldx-overview-card-toggle"
                    data-shieldx-overview-card-toggle
                    aria-pressed="<?php echo esc_attr($disabled ? 'true' : 'false'); ?>"
                >
                    <span class="shieldx-screen-reader-text"><?php echo esc_html($disabled ? sprintf(
                        /* translators: %s: overview card title. */
                        __('Enable %s overview card', 'xerotact-shield-x-security'),
                        $title
                    ) : sprintf(
                        /* translators: %s: overview card title. */
                        __('Disable %s overview card', 'xerotact-shield-x-security'),
                        $title
                    )); ?></span>
                </button>
            <?php endif; ?>
        </article>
        <?php
    }

    /**
     * @param array{open:int,percent:int,items:array<int,string>} $readiness
     */
    private function renderReadinessMetric(array $readiness): void
    {
        $open = max(0, (int) ($readiness['open'] ?? 0));
        $percent = max(0, min(100, (int) ($readiness['percent'] ?? 0)));
        $items = isset($readiness['items']) && is_array($readiness['items']) ? $readiness['items'] : [];
        $tone = $open === 0 ? 'good' : 'warn';
        $headline = $open === 0
            ? __('Ready', 'xerotact-shield-x-security')
            : (string) $open;
        $label = $open === 0
            ? __('No action needed', 'xerotact-shield-x-security')
            : _n('item needs review', 'items need review', $open, 'xerotact-shield-x-security');
        $title = !empty($items)
            ? implode(', ', array_slice(array_map('strval', $items), 0, 3))
            : __('Baseline setup complete', 'xerotact-shield-x-security');
        ?>
        <article class="<?php echo esc_attr('shieldx-metric shieldx-metric--score shieldx-metric--' . $tone); ?>" style="<?php echo esc_attr('--shieldx-score:' . (string) $percent . ';'); ?>">
            <div class="shieldx-metric-head">
                <span><?php echo esc_html__('SHIELD-X Readiness', 'xerotact-shield-x-security'); ?></span>
                <span class="shieldx-metric-icon"><?php echo wp_kses(Iconoir::svg('health-shield'), $this->allowedIconSvg()); ?></span>
            </div>
            <div class="shieldx-score-ring" title="<?php echo esc_attr($title); ?>">
                <strong><?php echo esc_html($headline); ?></strong>
                <small><?php echo esc_html($label); ?></small>
            </div>
            <a class="shieldx-card-link" href="<?php echo esc_url(admin_url('admin.php?page=shield-x-system')); ?>">
                <?php echo esc_html__('View report', 'xerotact-shield-x-security'); ?>
                <?php echo wp_kses(Iconoir::svg('nav-arrow-right', 'shieldx-icon shieldx-card-link-icon'), $this->allowedIconSvg()); ?>
            </a>
        </article>
        <?php
    }

    private function renderMetric(string $label, string $value, string $tone, string $icon = 'activity', string $detail = '', string $linkUrl = '', string $linkLabel = ''): void
    {
        $class = 'shieldx-metric shieldx-metric--' . $tone . ($linkUrl !== '' ? ' shieldx-metric--linked' : '');
        ?>
        <?php if ($linkUrl !== '') : ?>
            <a class="<?php echo esc_attr($class); ?>" href="<?php echo esc_url($linkUrl); ?>" aria-label="<?php echo esc_attr(sprintf(
                /* translators: %s is the metric label. */
                __('Open %s details', 'xerotact-shield-x-security'),
                $label
            )); ?>">
        <?php else : ?>
            <article class="<?php echo esc_attr($class); ?>">
        <?php endif; ?>
            <div class="shieldx-metric-head">
                <span><?php echo esc_html($label); ?></span>
                <span class="shieldx-metric-icon"><?php echo wp_kses(Iconoir::svg($icon), $this->allowedIconSvg()); ?></span>
            </div>
            <strong><?php echo esc_html($value); ?></strong>
            <?php if ($detail !== '') : ?>
                <small class="shieldx-metric-detail"><?php echo esc_html($detail); ?></small>
            <?php endif; ?>
            <?php if ($linkLabel !== '') : ?>
                <small class="shieldx-metric-link-label"><?php echo esc_html($linkLabel); ?></small>
            <?php endif; ?>
        <?php if ($linkUrl !== '') : ?>
            </a>
        <?php else : ?>
            </article>
        <?php endif; ?>
        <?php
    }

    /**
     * @param array<string,mixed> $timeline
     */
    private function renderOverviewActivityChart(
        array $timeline,
        string $instance = 'overview',
        array $requestedSeries = [],
        string $heading = '',
        string $description = ''
    ): void
    {
        $chart = $this->overviewActivityChartData($timeline);
        $series = isset($chart['series']) && is_array($chart['series']) ? $chart['series'] : [];
        if ($requestedSeries !== []) {
            $allowedSeries = array_fill_keys(array_map('strval', $requestedSeries), true);
            $filteredSeries = array_intersect_key($series, $allowedSeries);
            if ($filteredSeries !== []) {
                $series = $filteredSeries;
            }
        }

        $chart['series'] = $series;
        $this->renderActivityChart($chart, $instance, $heading, $description);
    }

    /**
     * @param array<string,mixed> $timeline
     */
    private function renderWafActivityChart(array $timeline): void
    {
        $this->renderActivityChart(
            $this->wafActivityChartData($timeline),
            'waf',
            __('WAF Trends', 'xerotact-shield-x-security'),
            __('Compare total firewall hits by action, severity, or category.', 'xerotact-shield-x-security'),
            [
                'action' => __('Action taken', 'xerotact-shield-x-security'),
                'severity' => __('Severity', 'xerotact-shield-x-security'),
                'category' => __('Category', 'xerotact-shield-x-security'),
            ]
        );
    }

    /**
     * @param array<string,mixed> $chart
     * @param array<string,string> $groupOptions
     */
    private function renderActivityChart(
        array $chart,
        string $instance,
        string $heading,
        string $description,
        array $groupOptions = []
    ): void
    {
        $series = isset($chart['series']) && is_array($chart['series']) ? $chart['series'] : [];
        $buckets = isset($chart['buckets']) && is_array($chart['buckets']) ? $chart['buckets'] : [];
        $rangeOptions = $this->overviewActivityChartRanges($buckets);

        $width = 1600;
        $height = 300;
        $left = 48;
        $top = 28;
        $chartHeight = 180;
        $bottom = 62;
        $right = 14;
        $instance = preg_replace('/[^a-z0-9_-]/', '', strtolower($instance)) ?: 'overview';
        $controlId = 'shieldx-overview-chart-series-' . $instance;
        $panelId = 'shieldx-' . $instance . '-trends';
        $heading = $heading !== '' ? $heading : __('Security Trends', 'xerotact-shield-x-security');
        $description = $description !== '' ? $description : __('Compare normal visits with WAF, login, scanner, captcha, and system signals', 'xerotact-shield-x-security');
        $hasGroupSelector = $groupOptions !== [];
        $hasSeriesPicker = !$hasGroupSelector && count($series) > 1;
        $chartAriaLabel = ($hasSeriesPicker || $hasGroupSelector)
            ? __('Multi-signal security trend chart', 'xerotact-shield-x-security')
            : sprintf(
                /* translators: %s: trend chart heading. */
                __('%s chart', 'xerotact-shield-x-security'),
                $heading
            );
        ?>
        <section id="<?php echo esc_attr($panelId); ?>" class="shieldx-panel shieldx-panel--full shieldx-overview-chart-panel" data-shieldx-overview-chart data-shieldx-chart-max="<?php echo esc_attr((string) min(5, max(1, count($series)))); ?>">
            <div class="shieldx-section-head">
                <div>
                    <h2><?php echo esc_html($heading); ?></h2>
                    <span><?php echo esc_html($description); ?></span>
                </div>
                <div class="shieldx-chart-actions">
                    <?php if ($hasGroupSelector) : ?>
                        <label class="shieldx-chart-range-control">
                            <span><?php echo esc_html__('Group by', 'xerotact-shield-x-security'); ?></span>
                            <select class="shieldx-chart-range-select" data-shieldx-chart-group-select>
                                <?php foreach ($groupOptions as $key => $label) : ?>
                                    <option value="<?php echo esc_attr((string) $key); ?>"><?php echo esc_html($label); ?></option>
                                <?php endforeach; ?>
                            </select>
                        </label>
                    <?php endif; ?>
                    <label class="shieldx-chart-range-control">
                        <span><?php echo esc_html__('Timeline', 'xerotact-shield-x-security'); ?></span>
                        <select class="shieldx-chart-range-select" data-shieldx-chart-range-select>
                            <?php foreach ($rangeOptions as $key => $range) : ?>
                                <option value="<?php echo esc_attr((string) $key); ?>" <?php selected(!empty($range['default'])); ?>>
                                    <?php echo esc_html((string) $range['label']); ?>
                                </option>
                            <?php endforeach; ?>
                        </select>
                    </label>
                    <?php if ($hasSeriesPicker) : ?>
                        <div class="shieldx-chart-control" data-shieldx-chart-control>
                            <button
                                type="button"
                                class="button shieldx-button-secondary shieldx-chart-control-button"
                                aria-expanded="false"
                                aria-controls="<?php echo esc_attr($controlId); ?>"
                                data-shieldx-chart-toggle
                            >
                                <span data-shieldx-chart-summary><?php echo esc_html__('5 selected', 'xerotact-shield-x-security'); ?></span>
                            </button>
                            <div id="<?php echo esc_attr($controlId); ?>" class="shieldx-chart-menu" data-shieldx-chart-menu hidden>
                                <strong><?php echo esc_html__('Display signals', 'xerotact-shield-x-security'); ?></strong>
                                <small><?php echo esc_html__('Choose up to 5 lines for the chart.', 'xerotact-shield-x-security'); ?></small>
                                <?php foreach ($series as $key => $item) : ?>
                                    <?php
                                    $label = (string) ($item['label'] ?? $key);
                                    $default = !empty($item['default']);
                                    ?>
                                    <label>
                                        <input
                                            type="checkbox"
                                            value="<?php echo esc_attr((string) $key); ?>"
                                            data-shieldx-chart-option
                                            <?php checked($default); ?>
                                        >
                                        <span style="--shieldx-series-color: <?php echo esc_attr((string) ($item['color'] ?? '#111827')); ?>"></span>
                                        <?php echo esc_html($label); ?>
                                    </label>
                                <?php endforeach; ?>
                                <p data-shieldx-chart-message hidden><?php echo esc_html__('You can display up to 5 signals at once.', 'xerotact-shield-x-security'); ?></p>
                            </div>
                        </div>
                    <?php endif; ?>
                </div>
            </div>
            <div class="shieldx-overview-chart-grid">
                <div class="shieldx-overview-chart-wrap">
                    <svg class="shieldx-overview-chart" viewBox="0 0 <?php echo esc_attr((string) $width); ?> <?php echo esc_attr((string) $height); ?>" role="img" aria-label="<?php echo esc_attr($chartAriaLabel); ?>">
                        <?php foreach ($rangeOptions as $rangeKey => $range) : ?>
                            <?php
                            $rangeBuckets = isset($range['buckets']) && is_array($range['buckets']) ? $range['buckets'] : [];
                            $rangeLabels = array_map(static function ($bucket): string {
                                return is_array($bucket) ? (string) ($bucket['label'] ?? '') : '';
                            }, $rangeBuckets);
                            $this->renderOverviewChartSvgRange(
                                (string) $rangeKey,
                                $rangeLabels,
                                $rangeBuckets,
                                $series,
                                $width,
                                $height,
                                $left,
                                $top,
                                $chartHeight,
                                $bottom,
                                $right,
                                !empty($range['default'])
                            );
                            ?>
                        <?php endforeach; ?>
                    </svg>
                    <div class="shieldx-overview-chart-legend">
                        <?php foreach ($series as $key => $item) : ?>
                            <span
                                class="<?php echo esc_attr(empty($item['default']) ? 'is-hidden' : ''); ?>"
                                data-shieldx-chart-legend="<?php echo esc_attr((string) $key); ?>"
                                data-shieldx-chart-series-group="<?php echo esc_attr((string) ($item['group'] ?? '')); ?>"
                            >
                                <i class="shieldx-overview-dot" style="--shieldx-series-color: <?php echo esc_attr((string) ($item['color'] ?? '#111827')); ?>"></i>
                                <?php echo esc_html((string) ($item['label'] ?? $key)); ?>
                            </span>
                        <?php endforeach; ?>
                    </div>
                </div>
                <div class="shieldx-overview-signal-list">
                    <?php foreach ($rangeOptions as $rangeKey => $range) : ?>
                        <?php
                        $rangeBuckets = isset($range['buckets']) && is_array($range['buckets']) ? $range['buckets'] : [];
                        $rangeTotals = $this->overviewRangeTotals($rangeBuckets, $series);
                        ?>
                        <div
                            class="<?php echo esc_attr('shieldx-overview-signal-range' . (empty($range['default']) ? ' is-hidden' : '')); ?>"
                            data-shieldx-chart-range-summary="<?php echo esc_attr((string) $rangeKey); ?>"
                        >
                            <?php foreach ($series as $key => $item) : ?>
                                <?php
                                $detail = (string) ($item['detail'] ?? '');
                                if ((string) $key === 'normal_visits') {
                                    $detail = (string) ($range['label'] ?? $detail);
                                }
                                ?>
                                <div
                                    class="<?php echo esc_attr('shieldx-overview-signal-card' . (empty($item['default']) ? ' is-hidden' : '')); ?>"
                                    data-shieldx-chart-summary-card="<?php echo esc_attr((string) $key); ?>"
                                    data-shieldx-chart-series-group="<?php echo esc_attr((string) ($item['group'] ?? '')); ?>"
                                >
                                    <span><?php echo esc_html((string) ($item['label'] ?? $key)); ?></span>
                                    <strong><?php echo esc_html(number_format_i18n((int) ($rangeTotals[$key] ?? 0))); ?></strong>
                                    <?php if ($detail !== '') : ?>
                                        <small><?php echo esc_html($detail); ?></small>
                                    <?php endif; ?>
                                </div>
                            <?php endforeach; ?>
                        </div>
                    <?php endforeach; ?>
                </div>
            </div>
        </section>
        <?php
    }

    /**
     * @param array<int,array<string,int|string>> $dailyBuckets
     * @return array<string,array<string,mixed>>
     */
    private function overviewActivityChartRanges(array $dailyBuckets): array
    {
        return [
            'daily' => [
                'label' => __('Daily', 'xerotact-shield-x-security'),
                'default' => true,
                'buckets' => array_slice($dailyBuckets, -14),
            ],
            'weekly' => [
                'label' => __('Weekly', 'xerotact-shield-x-security'),
                'default' => false,
                'buckets' => $this->overviewAggregateActivityBuckets($dailyBuckets, 'week', 14),
            ],
            'monthly' => [
                'label' => __('Monthly', 'xerotact-shield-x-security'),
                'default' => false,
                'buckets' => $this->overviewAggregateActivityBuckets($dailyBuckets, 'month', 14),
            ],
        ];
    }

    /**
     * @param array<int,array<string,int|string>> $dailyBuckets
     * @return array<int,array<string,int|string>>
     */
    private function overviewAggregateActivityBuckets(array $dailyBuckets, string $period, int $count): array
    {
        $now = time();
        $todayStart = strtotime(gmdate('Y-m-d 00:00:00', $now)) ?: $now;
        $periods = [];

        if ($period === 'month') {
            $currentStart = strtotime(gmdate('Y-m-01 00:00:00', $now)) ?: $todayStart;
            for ($index = $count - 1; $index >= 0; $index--) {
                $start = strtotime('-' . (string) $index . ' months', $currentStart) ?: $currentStart;
                $end = (strtotime('+1 month', $start) ?: ($start + 31 * 86400)) - 1;
                $periods[] = [$start, $end, gmdate('M Y', $start)];
            }
        } else {
            $currentMonthStart = strtotime(gmdate('Y-m-01 00:00:00', $now)) ?: $todayStart;
            $currentSlot = min(4, (int) ceil(max(1, (int) gmdate('j', $todayStart)) / 7));
            for ($index = $count - 1; $index >= 0; $index--) {
                $slotIndex = ($currentSlot - 1) - $index;
                $monthOffset = (int) floor($slotIndex / 4);
                $slot = (($slotIndex % 4) + 4) % 4 + 1;
                $monthStart = strtotime((string) $monthOffset . ' months', $currentMonthStart) ?: $currentMonthStart;
                $startDay = ($slot - 1) * 7 + 1;
                $start = strtotime(gmdate('Y-m-', $monthStart) . str_pad((string) $startDay, 2, '0', STR_PAD_LEFT) . ' 00:00:00') ?: $monthStart;
                if ($slot < 4) {
                    $end = $start + (7 * 86400) - 1;
                } else {
                    $end = (strtotime('+1 month', $monthStart) ?: ($monthStart + 31 * 86400)) - 1;
                }

                $periods[] = [$start, $end, $this->overviewWeekOfMonthLabel($start, $slot)];
            }
        }

        $buckets = [];
        foreach ($periods as $periodData) {
            $buckets[] = $this->overviewEmptyActivityBucket((string) $periodData[2], (int) $periodData[0]);
        }

        foreach ($dailyBuckets as $dailyBucket) {
            if (!is_array($dailyBucket)) {
                continue;
            }

            $timestamp = (int) ($dailyBucket['timestamp'] ?? 0);
            if ($timestamp <= 0) {
                continue;
            }

            foreach ($periods as $periodIndex => $periodData) {
                $start = (int) $periodData[0];
                $end = (int) $periodData[1];
                if ($timestamp < $start || $timestamp > $end) {
                    continue;
                }

                foreach ($dailyBucket as $key => $value) {
                    if (!is_string($key) || $key === 'timestamp' || $key === 'label' || !is_int($value)) {
                        continue;
                    }

                    $buckets[$periodIndex][$key] = (int) ($buckets[$periodIndex][$key] ?? 0) + $value;
                }

                break;
            }
        }

        return $buckets;
    }

    private function overviewWeekOfMonthLabel(int $timestamp, ?int $week = null): string
    {
        if ($week === null) {
            $day = max(1, min(31, (int) gmdate('j', $timestamp)));
            $week = min(4, (int) ceil($day / 7));
        }

        $month = gmdate('M', $timestamp);

        /* translators: 1: week number within the month, 2: month abbreviation. */
        return sprintf(__('W%1$d %2$s', 'xerotact-shield-x-security'), $week, $month);
    }

    /**
     * @return array<string,int|string>
     */
    private function overviewEmptyActivityBucket(string $label, int $timestamp): array
    {
        return [
            'label' => $label,
            'timestamp' => $timestamp,
            'normal_visits' => 0,
            'waf' => 0,
            'login_attacks' => 0,
            'scanner' => 0,
            'captcha_failed' => 0,
            'database' => 0,
            'lockdown' => 0,
            'backup' => 0,
            'total' => 0,
        ];
    }

    /**
     * @param array<int,string> $labels
     * @param array<int,array<string,int|string>> $buckets
     * @param array<string,array<string,mixed>> $series
     */
    private function renderOverviewChartSvgRange(
        string $rangeKey,
        array $labels,
        array $buckets,
        array $series,
        int $width,
        int $height,
        int $left,
        int $top,
        int $chartHeight,
        int $bottom,
        int $right,
        bool $active
    ): void {
        $max = $this->overviewRangeMax($buckets, $series);
        $plotWidth = $width - $left - $right;
        $labelCount = max(1, count($labels));
        ?>
        <g class="<?php echo esc_attr('shieldx-overview-chart-range' . ($active ? '' : ' is-hidden')); ?>" data-shieldx-chart-range-panel="<?php echo esc_attr($rangeKey); ?>">
            <g class="shieldx-overview-chart-gridlines" aria-hidden="true">
                <?php for ($line = 0; $line <= 4; $line++) : ?>
                    <?php
                    $y = $top + ($chartHeight / 4) * $line;
                    $value = (int) round($max - (($max / 4) * $line));
                    ?>
                    <line x1="<?php echo esc_attr((string) $left); ?>" y1="<?php echo esc_attr(sprintf('%.2F', $y)); ?>" x2="<?php echo esc_attr((string) ($width - $right)); ?>" y2="<?php echo esc_attr(sprintf('%.2F', $y)); ?>"></line>
                    <text x="<?php echo esc_attr((string) ($left - 10)); ?>" y="<?php echo esc_attr(sprintf('%.2F', $y + 4)); ?>" text-anchor="end"><?php echo esc_html(number_format_i18n($value)); ?></text>
                <?php endfor; ?>
            </g>
            <g class="shieldx-overview-chart-lines">
                <?php foreach ($series as $key => $item) : ?>
                    <?php
                    $points = [];
                    $default = !empty($item['default']);
                    $color = (string) ($item['color'] ?? '#111827');

                    foreach ($buckets as $index => $bucket) {
                        $value = is_array($bucket) ? (int) ($bucket[$key] ?? 0) : 0;
                        $x = $left + ($plotWidth / max(1, $labelCount - 1)) * $index;
                        $y = $top + $chartHeight - ((max(0, $value) / $max) * $chartHeight);
                        $points[] = sprintf('%.2F,%.2F', $x, $y);
                    }
                    ?>
                    <polyline
                        class="<?php echo esc_attr('shieldx-overview-line' . ($default ? '' : ' is-hidden')); ?>"
                        data-shieldx-chart-series="<?php echo esc_attr((string) $key); ?>"
                        data-shieldx-chart-series-group="<?php echo esc_attr((string) ($item['group'] ?? '')); ?>"
                        points="<?php echo esc_attr(implode(' ', $points)); ?>"
                        style="--shieldx-series-color: <?php echo esc_attr($color); ?>"
                    ></polyline>
                    <?php foreach ($buckets as $index => $bucket) : ?>
                        <?php
                        $value = is_array($bucket) ? (int) ($bucket[$key] ?? 0) : 0;
                        $x = $left + ($plotWidth / max(1, $labelCount - 1)) * $index;
                        $y = $top + $chartHeight - ((max(0, $value) / $max) * $chartHeight);
                        ?>
                        <circle
                            class="<?php echo esc_attr('shieldx-overview-point' . ($default ? '' : ' is-hidden')); ?>"
                            data-shieldx-chart-series="<?php echo esc_attr((string) $key); ?>"
                            data-shieldx-chart-series-group="<?php echo esc_attr((string) ($item['group'] ?? '')); ?>"
                            cx="<?php echo esc_attr(sprintf('%.2F', $x)); ?>"
                            cy="<?php echo esc_attr(sprintf('%.2F', $y)); ?>"
                            r="4"
                            style="--shieldx-series-color: <?php echo esc_attr($color); ?>"
                        >
                            <title><?php echo esc_html((string) ($item['label'] ?? $key) . ': ' . number_format_i18n($value)); ?></title>
                        </circle>
                    <?php endforeach; ?>
                <?php endforeach; ?>
            </g>
            <g class="shieldx-overview-chart-labels">
                <?php foreach ($labels as $index => $label) : ?>
                    <?php
                    $x = $left + ($plotWidth / max(1, $labelCount - 1)) * $index;
                    $labelParts = preg_split('/\s+/', trim((string) $label), 2);
                    $dayLabel = isset($labelParts[0]) ? (string) $labelParts[0] : (string) $label;
                    $monthLabel = isset($labelParts[1]) ? (string) $labelParts[1] : '';
                    ?>
                    <text x="<?php echo esc_attr(sprintf('%.2F', $x)); ?>" y="<?php echo esc_attr((string) ($height - $bottom + 12)); ?>" text-anchor="middle">
                        <tspan x="<?php echo esc_attr(sprintf('%.2F', $x)); ?>" dy="0"><?php echo esc_html($dayLabel); ?></tspan>
                        <?php if ($monthLabel !== '') : ?>
                            <tspan x="<?php echo esc_attr(sprintf('%.2F', $x)); ?>" dy="14"><?php echo esc_html($monthLabel); ?></tspan>
                        <?php endif; ?>
                    </text>
                <?php endforeach; ?>
            </g>
        </g>
        <?php
    }

    /**
     * @param array<int,array<string,int|string>> $buckets
     * @param array<string,array<string,mixed>> $series
     */
    private function overviewRangeMax(array $buckets, array $series): int
    {
        $max = 1;

        foreach ($buckets as $bucket) {
            foreach ($series as $key => $item) {
                if (!is_array($bucket)) {
                    continue;
                }

                $max = max($max, (int) ($bucket[$key] ?? 0));
            }
        }

        return $max;
    }

    /**
     * @param array<int,array<string,int|string>> $buckets
     * @return array<string,int>
     */
    private function overviewRangeTotals(array $buckets, array $series): array
    {
        $totals = array_fill_keys(array_keys($series), 0);

        foreach ($buckets as $bucket) {
            if (!is_array($bucket)) {
                continue;
            }

            foreach ($totals as $key => $value) {
                $totals[$key] = $value + (int) ($bucket[$key] ?? 0);
            }
        }

        return $totals;
    }

    /**
     * @param array<string,mixed> $timeline
     * @return array{labels:array<int,string>,buckets:array<int,array<string,int|string>>,series:array<string,array<string,mixed>>,totals:array<string,int>,max_value:int,event_count:int}
     */
    private function overviewActivityChartData(array $timeline): array
    {
        $now = time();
        $lookbackDays = 365;
        $start = strtotime('-' . (string) ($lookbackDays - 1) . ' days', strtotime(gmdate('Y-m-d 00:00:00', $now)) ?: $now) ?: ($now - ($lookbackDays - 1) * 86400);
        $buckets = [];
        $labels = [];
        $totals = [
            'normal_visits' => 0,
            'waf' => 0,
            'login_attacks' => 0,
            'scanner' => 0,
            'captcha_failed' => 0,
            'database' => 0,
            'lockdown' => 0,
            'backup' => 0,
        ];

        for ($day = 0; $day < $lookbackDays; $day++) {
            $time = $start + ($day * 86400);
            $key = gmdate('Y-m-d', $time);
            $labels[] = gmdate('j M', $time);
            $buckets[$key] = [
                'label' => $labels[$day],
                'timestamp' => $time,
                'normal_visits' => 0,
                'waf' => 0,
                'login_attacks' => 0,
                'scanner' => 0,
                'captcha_failed' => 0,
                'database' => 0,
                'lockdown' => 0,
                'backup' => 0,
                'total' => 0,
            ];
        }

        $summarySeriesByDate = [];
        $summaryRows = isset($timeline['daily_summaries']) && is_array($timeline['daily_summaries']) ? $timeline['daily_summaries'] : [];
        foreach ($summaryRows as $summary) {
            if (!is_array($summary)) {
                continue;
            }

            $timestamp = (int) ($summary['timestamp'] ?? 0);
            if ($timestamp < $start) {
                continue;
            }

            $bucketKey = (string) ($summary['summary_date'] ?? gmdate('Y-m-d', $timestamp));
            if (!isset($buckets[$bucketKey])) {
                continue;
            }

            $series = $this->overviewSeriesForTimelineSignal(
                (string) ($summary['category'] ?? ''),
                (string) ($summary['source_event'] ?? '')
            );
            if ($series === '') {
                continue;
            }

            $count = max(0, (int) ($summary['count'] ?? 0));
            if ($count <= 0) {
                continue;
            }

            $buckets[$bucketKey][$series] = (int) ($buckets[$bucketKey][$series] ?? 0) + $count;
            $buckets[$bucketKey]['total'] = (int) ($buckets[$bucketKey]['total'] ?? 0) + $count;
            $totals[$series] = (int) ($totals[$series] ?? 0) + $count;
            $summarySeriesByDate[$bucketKey][$series] = true;
        }

        $events = isset($timeline['events']) && is_array($timeline['events']) ? $timeline['events'] : [];
        foreach ($events as $event) {
            if (!is_array($event)) {
                continue;
            }

            $timestamp = (int) ($event['timestamp'] ?? 0);
            if ($timestamp < $start) {
                continue;
            }

            $bucketKey = gmdate('Y-m-d', $timestamp);
            if (!isset($buckets[$bucketKey])) {
                continue;
            }

            $category = (string) ($event['category'] ?? '');
            $sourceEvent = (string) ($event['source_event'] ?? '');
            $series = $this->overviewSeriesForTimelineSignal($category, $sourceEvent);
            if ($series === '') {
                continue;
            }

            // Daily counters are keyed by their signal, not just the date. A
            // partial aggregate must not suppress a different audit signal on
            // the same day while counters are being populated.
            if ((string) ($event['source'] ?? '') === 'audit' && isset($summarySeriesByDate[$bucketKey][$series])) {
                continue;
            }

            $buckets[$bucketKey][$series]++;
            $buckets[$bucketKey]['total']++;
            $totals[$series]++;
        }

        $series = [
            'normal_visits' => [
                'label' => __('Normal visits', 'xerotact-shield-x-security'),
                'color' => '#22c55e',
                'default' => true,
                'detail' => __('front-end page views', 'xerotact-shield-x-security'),
                'values' => [],
            ],
            'waf' => [
                'label' => __('WAF hits', 'xerotact-shield-x-security'),
                'color' => '#2f6df6',
                'default' => true,
                'detail' => __('firewall matches', 'xerotact-shield-x-security'),
                'values' => [],
            ],
            'login_attacks' => [
                'label' => __('Login attacks', 'xerotact-shield-x-security'),
                'color' => '#ef4444',
                'default' => true,
                'detail' => __('failures, lockouts, or recovery signals', 'xerotact-shield-x-security'),
                'values' => [],
            ],
            'scanner' => [
                'label' => __('Scanner findings', 'xerotact-shield-x-security'),
                'color' => '#f59e0b',
                'default' => true,
                'detail' => __('file and vulnerability findings', 'xerotact-shield-x-security'),
                'values' => [],
            ],
            'captcha_failed' => [
                'label' => __('Captcha failed', 'xerotact-shield-x-security'),
                'color' => '#111827',
                'default' => true,
                'detail' => __('challenge failures', 'xerotact-shield-x-security'),
                'values' => [],
            ],
            'database' => [
                'label' => __('Database findings', 'xerotact-shield-x-security'),
                'color' => '#7c3aed',
                'default' => false,
                'detail' => __('database scanner findings', 'xerotact-shield-x-security'),
                'values' => [],
            ],
            'lockdown' => [
                'label' => __('Hardening changes', 'xerotact-shield-x-security'),
                'color' => '#0f766e',
                'default' => false,
                'detail' => __('hardening and lockdown events', 'xerotact-shield-x-security'),
                'values' => [],
            ],
            'backup' => [
                'label' => __('Backup / restore', 'xerotact-shield-x-security'),
                'color' => '#64748b',
                'default' => false,
                'detail' => __('backup and restore events', 'xerotact-shield-x-security'),
                'values' => [],
            ],
        ];

        $max = 1;
        foreach ($buckets as $bucket) {
            foreach ($series as $key => $item) {
                $value = (int) ($bucket[$key] ?? 0);
                $series[$key]['values'][] = $value;
                $series[$key]['total'] = (int) ($series[$key]['total'] ?? 0) + $value;
                $max = max($max, $value);
            }
        }

        return [
            'labels' => $labels,
            'buckets' => array_values($buckets),
            'series' => $series,
            'totals' => $totals,
            'max_value' => $max,
            'event_count' => count($events),
        ];
    }

    /**
     * @param array<string,mixed> $timeline
     * @return array<string,mixed>
     */
    private function wafActivityChartData(array $timeline): array
    {
        $now = time();
        $lookbackDays = 365;
        $start = strtotime('-' . (string) ($lookbackDays - 1) . ' days', strtotime(gmdate('Y-m-d 00:00:00', $now)) ?: $now) ?: ($now - ($lookbackDays - 1) * 86400);
        $buckets = [];
        $groupTotals = [
            'action' => [],
            'severity' => [],
            'category' => [],
        ];

        for ($day = 0; $day < $lookbackDays; $day++) {
            $time = $start + ($day * 86400);
            $key = gmdate('Y-m-d', $time);
            $bucket = $this->overviewEmptyActivityBucket(gmdate('j M', $time), $time);
            $bucket['waf_total'] = 0;
            $buckets[$key] = $bucket;
        }

        $summaries = isset($timeline['waf_summaries']) && is_array($timeline['waf_summaries']) ? $timeline['waf_summaries'] : [];
        foreach ($summaries as $summary) {
            if (!is_array($summary)) {
                continue;
            }

            $date = (string) ($summary['summary_date'] ?? '');
            $dimension = (string) ($summary['dimension'] ?? '');
            $value = (string) ($summary['value'] ?? '');
            $count = max(0, (int) ($summary['count'] ?? 0));
            if (!isset($buckets[$date]) || $count <= 0) {
                continue;
            }

            if ($dimension === 'total') {
                $buckets[$date]['waf_total'] = (int) ($buckets[$date]['waf_total'] ?? 0) + $count;
                continue;
            }

            if (!isset($groupTotals[$dimension]) || preg_match('/^[a-z_]+$/', $value) !== 1) {
                continue;
            }

            $seriesKey = 'waf_' . $dimension . '_' . $value;
            $buckets[$date][$seriesKey] = (int) ($buckets[$date][$seriesKey] ?? 0) + $count;
            $groupTotals[$dimension][$value] = (int) ($groupTotals[$dimension][$value] ?? 0) + $count;
        }

        $series = [
            'waf_total' => [
                'label' => __('WAF hits', 'xerotact-shield-x-security'),
                'color' => '#111827',
                'default' => true,
                'group' => 'baseline',
                'detail' => __('all firewall matches', 'xerotact-shield-x-security'),
            ],
        ];

        foreach ($groupTotals as $group => $values) {
            arsort($values);
            foreach (array_slice(array_keys($values), 0, 4) as $value) {
                $definition = $this->wafTrendSeriesDefinition($group, (string) $value);
                $series['waf_' . $group . '_' . $value] = [
                    'label' => $definition['label'],
                    'color' => $definition['color'],
                    'default' => $group === 'action',
                    'group' => $group,
                    'detail' => $definition['detail'],
                ];
            }
        }

        return [
            'buckets' => array_values($buckets),
            'series' => $series,
        ];
    }

    /**
     * @return array{label:string,color:string,detail:string}
     */
    private function wafTrendSeriesDefinition(string $group, string $value): array
    {
        $definitions = [
            'action' => [
                'blocked' => [__('Blocked', 'xerotact-shield-x-security'), '#dc2626', __('requests stopped by the firewall', 'xerotact-shield-x-security')],
                'would_block' => [__('Would block', 'xerotact-shield-x-security'), '#f59e0b', __('matches recorded before enforcement', 'xerotact-shield-x-security')],
                'logged' => [__('Logged', 'xerotact-shield-x-security'), '#2f6df6', __('monitor-only matches', 'xerotact-shield-x-security')],
            ],
            'severity' => [
                'critical' => [__('Critical', 'xerotact-shield-x-security'), '#dc2626', __('blocked, would-block, or 60+ score matches', 'xerotact-shield-x-security')],
                'high' => [__('High', 'xerotact-shield-x-security'), '#ea580c', __('30+ score matches', 'xerotact-shield-x-security')],
                'medium' => [__('Medium', 'xerotact-shield-x-security'), '#f59e0b', __('lower-score WAF matches', 'xerotact-shield-x-security')],
                'low' => [__('Low', 'xerotact-shield-x-security'), '#2f6df6', __('monitor-only, zero-score matches', 'xerotact-shield-x-security')],
            ],
            'category' => [
                'reputation' => [__('Reputation', 'xerotact-shield-x-security'), '#7c3aed', __('reputation-based matches', 'xerotact-shield-x-security')],
                'ip_block' => [__('IP block', 'xerotact-shield-x-security'), '#dc2626', __('temporary or listed IP blocks', 'xerotact-shield-x-security')],
                'engine_safeguard' => [__('Engine safeguard', 'xerotact-shield-x-security'), '#ea580c', __('engine safety and evaluation limits', 'xerotact-shield-x-security')],
                'score_threshold' => [__('Score threshold', 'xerotact-shield-x-security'), '#f59e0b', __('score-based WAF decisions', 'xerotact-shield-x-security')],
                'rule_match' => [__('Rule match', 'xerotact-shield-x-security'), '#2f6df6', __('rule signature matches', 'xerotact-shield-x-security')],
            ],
        ];
        $definition = $definitions[$group][$value] ?? [
            $this->formatLabel($value),
            '#64748b',
            __('other firewall matches', 'xerotact-shield-x-security'),
        ];

        return [
            'label' => (string) $definition[0],
            'color' => (string) $definition[1],
            'detail' => (string) $definition[2],
        ];
    }

    private function overviewSeriesForTimelineSignal(string $category, string $sourceEvent): string
    {
        if ($category === 'visit') {
            return 'normal_visits';
        }

        if (in_array($category, ['waf', 'blocking'], true)) {
            return 'waf';
        }

        if (in_array($category, ['login', 'recovery'], true)) {
            return 'login_attacks';
        }

        if ($category === 'database') {
            return 'database';
        }

        if ($category === 'scanner') {
            return 'scanner';
        }

        if ($category === 'captcha') {
            return 'captcha_failed';
        }

        if ($category === 'lockdown' || strpos($sourceEvent, 'hardening.') === 0) {
            return 'lockdown';
        }

        if ($category === 'backup' || strpos($sourceEvent, 'backup.') === 0 || strpos($sourceEvent, 'restore') !== false || strpos($sourceEvent, 'shadow') !== false) {
            return 'backup';
        }

        return '';
    }

    private function renderSectionSettingsLink(string $url, string $label): void
    {
        ?>
        <a
            class="shieldx-section-settings-link"
            href="<?php echo esc_url($url); ?>"
            data-shieldx-no-accordion-toggle
            aria-label="<?php echo esc_attr($label); ?>"
            title="<?php echo esc_attr($label); ?>"
        >
            <?php echo wp_kses(Iconoir::svg('tools', 'shieldx-icon shieldx-section-settings-icon'), $this->allowedIconSvg()); ?>
        </a>
        <?php
    }

    /**
     * @param array<string,int> $counts
     */
    private function renderCountPills(string $label, array $counts): void
    {
        ?>
        <div class="shieldx-count-group">
            <strong><?php echo esc_html($label); ?></strong>
            <span>
                <?php if (empty($counts)) : ?>
                    <em class="shieldx-count-pill"><?php echo esc_html__('None', 'xerotact-shield-x-security'); ?></em>
                <?php endif; ?>
                <?php foreach ($counts as $name => $count) : ?>
                    <em class="<?php echo esc_attr('shieldx-count-pill shieldx-count-pill--' . strtolower(str_replace(' ', '-', (string) $name))); ?>">
                        <?php echo esc_html($this->formatLabel((string) $name) . ' ' . (string) $count); ?>
                    </em>
                <?php endforeach; ?>
            </span>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $status
     */
    private function renderSystemHealth(array $status): void
    {
        $health = isset($status['health']) && is_array($status['health']) ? $status['health'] : [];
        $waf = isset($status['waf']) && is_array($status['waf']) ? $status['waf'] : [];
        $rateLimit = isset($status['rate_limit']) && is_array($status['rate_limit']) ? $status['rate_limit'] : [];
        $rateRequest = isset($rateLimit['request']) && is_array($rateLimit['request']) ? $rateLimit['request'] : [];
        $rateLogin = isset($rateLimit['login']) && is_array($rateLimit['login']) ? $rateLimit['login'] : [];
        $scanner = isset($status['scanner']) && is_array($status['scanner']) ? $status['scanner'] : [];
        $schedule = isset($scanner['schedule']) && is_array($scanner['schedule']) ? $scanner['schedule'] : [];
        $loginSecurity = isset($status['login_security']) && is_array($status['login_security']) ? $status['login_security'] : [];
        $hardening = isset($status['hardening']) && is_array($status['hardening']) ? $status['hardening'] : [];
        $maintenance = isset($status['maintenance']) && is_array($status['maintenance']) ? $status['maintenance'] : [];
        $restorePoints = isset($maintenance['restore_points']) && is_array($maintenance['restore_points']) ? $maintenance['restore_points'] : [];
        $databaseBackups = isset($maintenance['database_backups']) && is_array($maintenance['database_backups']) ? $maintenance['database_backups'] : [];
        $logRotation = isset($maintenance['log_rotation']) && is_array($maintenance['log_rotation']) ? $maintenance['log_rotation'] : [];
        $humanChallenge = isset($status['human_challenge']) && is_array($status['human_challenge']) ? $status['human_challenge'] : [];
        $lockdown = isset($status['lockdown']) && is_array($status['lockdown']) ? $status['lockdown'] : [];
        $rulesLoaded = (int) ($waf['rules_loaded'] ?? 0);
        $rulesEnabled = (int) ($waf['rules_enabled'] ?? 0);
        $scheduleReadyCount = $this->scanScheduleReadyCount($schedule);
        $scheduleTotal = count(ScheduledScanSettings::types());
        $adminsPending = (int) ($loginSecurity['admins_pending'] ?? 0);
        $activeIssues = (int) ($scanner['active_issues'] ?? 0);
        $challengeRequested = !empty($humanChallenge['login_enabled'])
            || !empty($humanChallenge['login_fullscreen_enabled'])
            || !empty($humanChallenge['password_reset_enabled'])
            || !empty($humanChallenge['waf_enabled']);
        $hardeningReady = !empty($hardening['hide_wordpress_signature'])
            && !empty($hardening['disable_file_edit'])
            && HardeningSettings::defaultResponseHeadersEnabled(
                isset($hardening['response_headers']) && is_array($hardening['response_headers'])
                    ? $hardening['response_headers']
                    : []
            )
            && !empty($hardening['generic_login_errors'])
            && !empty($hardening['sensitive_file_protection'])
            && !empty($hardening['direct_core_access_protection']);
        ?>
        <section class="shieldx-panel shieldx-system-side-panel">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('System Health', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html__('Plugin-critical checks', 'xerotact-shield-x-security'); ?></span>
            </div>
            <ul class="shieldx-check-list">
                <?php
                $this->renderCheck(__('Storage', 'xerotact-shield-x-security'), !empty($health['storage_ready']), __('Ready', 'xerotact-shield-x-security'), __('Blocked', 'xerotact-shield-x-security'));
                $this->renderCheck(__('Database schema', 'xerotact-shield-x-security'), !empty($health['schema_ready']), __('Ready', 'xerotact-shield-x-security'), __('Pending', 'xerotact-shield-x-security'));
                $this->renderCheck(__('Compatibility', 'xerotact-shield-x-security'), !empty($health['compatibility_ready']), __('Ready', 'xerotact-shield-x-security'), __('Needs attention', 'xerotact-shield-x-security'));
                $this->renderCheck(__('Secret storage', 'xerotact-shield-x-security'), !empty($health['login_secret_storage_ready']), __('Ready', 'xerotact-shield-x-security'), __('Blocked', 'xerotact-shield-x-security'));
                $this->renderCheck(__('WAF rules', 'xerotact-shield-x-security'), $rulesLoaded > 0 && $rulesEnabled > 0, sprintf(
                    /* translators: 1: enabled WAF rule count, 2: loaded WAF rule count. */
                    __('%1$d/%2$d enabled', 'xerotact-shield-x-security'),
                    $rulesEnabled,
                    $rulesLoaded
                ), __('Unavailable', 'xerotact-shield-x-security'));
                $this->renderCheck(__('Rate state', 'xerotact-shield-x-security'), !empty($health['rate_limit_storage_ready']), __('Ready', 'xerotact-shield-x-security'), __('Blocked', 'xerotact-shield-x-security'));
                $this->renderCheck(__('Request monitor', 'xerotact-shield-x-security'), !empty($rateRequest['enabled']), $this->formatLabel((string) ($rateRequest['mode'] ?? 'log_only')), __('Disabled', 'xerotact-shield-x-security'));
                $this->renderCheck(__('Login lockout', 'xerotact-shield-x-security'), !empty($rateLogin['enabled']), __('Enabled', 'xerotact-shield-x-security'), __('Disabled', 'xerotact-shield-x-security'));
                $this->renderCheck(__('Admin enrollment', 'xerotact-shield-x-security'), $adminsPending === 0, __('Ready', 'xerotact-shield-x-security'), sprintf(
                    /* translators: %d is the number of administrator accounts not enrolled in 2FA. */
                    __('%d pending', 'xerotact-shield-x-security'),
                    $adminsPending
                ));
                $this->renderCheck(__('Recovery URL', 'xerotact-shield-x-security'), !empty($loginSecurity['emergency_recovery_available']), __('Available', 'xerotact-shield-x-security'), __('Missing', 'xerotact-shield-x-security'));
                $this->renderCheck(__('Scanner baseline', 'xerotact-shield-x-security'), !empty($scanner['last_scan']), __('Started', 'xerotact-shield-x-security'), __('Not run', 'xerotact-shield-x-security'));
                $this->renderCheck(__('Scanner issues', 'xerotact-shield-x-security'), $activeIssues === 0, __('Clean', 'xerotact-shield-x-security'), sprintf(
                    /* translators: %d is the number of active scanner issues. */
                    __('%d open', 'xerotact-shield-x-security'),
                    $activeIssues
                ));
                $this->renderCheck(__('Scheduled scans', 'xerotact-shield-x-security'), $scheduleReadyCount === $scheduleTotal, sprintf(
                    /* translators: 1: ready scan schedule count, 2: total scan schedule count. */
                    __('%1$d/%2$d ready', 'xerotact-shield-x-security'),
                    $scheduleReadyCount,
                    $scheduleTotal
                ), sprintf(
                    /* translators: 1: ready scan schedule count, 2: total scan schedule count. */
                    __('%1$d/%2$d ready', 'xerotact-shield-x-security'),
                    $scheduleReadyCount,
                    $scheduleTotal
                ));
                $this->renderCheck(__('Hardening baseline', 'xerotact-shield-x-security'), $hardeningReady, __('Ready', 'xerotact-shield-x-security'), __('Review', 'xerotact-shield-x-security'));
                $this->renderCheck(__('Restore safety', 'xerotact-shield-x-security'), !empty($restorePoints['recent']) || !empty($databaseBackups['recent']), __('Available', 'xerotact-shield-x-security'), __('Missing', 'xerotact-shield-x-security'));
                $this->renderCheck(__('Log rotation', 'xerotact-shield-x-security'), !empty($logRotation['enabled']), __('Enabled', 'xerotact-shield-x-security'), __('Disabled', 'xerotact-shield-x-security'));
                if ($challengeRequested) {
                    $this->renderCheck(__('Human challenge', 'xerotact-shield-x-security'), !empty($humanChallenge['configured']), __('Configured', 'xerotact-shield-x-security'), __('Needs config', 'xerotact-shield-x-security'));
                }
                $this->renderCheck(__('Lockdown state', 'xerotact-shield-x-security'), empty($lockdown['killswitch_active']), !empty($lockdown['enabled']) ? __('Enabled', 'xerotact-shield-x-security') : __('Ready', 'xerotact-shield-x-security'), __('Killswitch active', 'xerotact-shield-x-security'));
                $this->renderCheck(__('REST API', 'xerotact-shield-x-security'), $this->restApiAvailable(), __('Registered', 'xerotact-shield-x-security'), __('Unavailable', 'xerotact-shield-x-security'));
                ?>
            </ul>
        </section>
        <?php
    }

    private function renderCheck(string $label, bool $ok, string $okText, string $failText): void
    {
        ?>
        <li>
            <span class="<?php echo esc_attr($ok ? 'shieldx-dot shieldx-dot--good' : 'shieldx-dot shieldx-dot--danger'); ?>" aria-hidden="true"></span>
            <span><?php echo esc_html($label); ?></span>
            <strong><?php echo esc_html($ok ? $okText : $failText); ?></strong>
        </li>
        <?php
    }

    private function restApiAvailable(): bool
    {
        if (!function_exists('rest_url')) {
            return false;
        }

        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- This must consult WordPress core's rest_enabled filter when detecting REST availability.
        if (function_exists('apply_filters') && apply_filters('rest_enabled', true) === false) {
            return false;
        }

        if (function_exists('rest_get_server')) {
            $server = rest_get_server();
            if (is_object($server) && method_exists($server, 'get_routes')) {
                $routes = $server->get_routes();
                if (is_array($routes) && !isset($routes['/shield-x/v2/status'])) {
                    return false;
                }
            }
        }

        return (string) rest_url('shield-x/v2/status') !== '';
    }

    /**
     * @param array<string,mixed> $status
     */
    private function renderDataStoresPanel(array $status): void
    {
        $schema = isset($status['schema']) && is_array($status['schema']) ? $status['schema'] : [];
        $tables = isset($schema['tables']) && is_array($schema['tables']) ? $schema['tables'] : [];
        $rateLimit = isset($status['rate_limit']) && is_array($status['rate_limit']) ? $status['rate_limit'] : [];
        ?>
        <section class="shieldx-panel shieldx-system-side-panel">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Data Stores', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html__('Installed SHIELD-X tables', 'xerotact-shield-x-security'); ?></span>
            </div>
            <div class="shieldx-table-cloud">
                <?php foreach ($tables as $table) : ?>
                    <code><?php echo esc_html((string) $table); ?></code>
                <?php endforeach; ?>
                <?php if (!empty($rateLimit['state_directory'])) : ?>
                    <code><?php echo esc_html((string) $rateLimit['state_directory']); ?></code>
                <?php endif; ?>
            </div>
        </section>
        <?php
    }

    /**
     * @param array<string,mixed> $humanChallenge
     * @param FileTypeSettings    $fileTypes
     */
    private function renderSystemToolSelector(array $humanChallenge, FileTypeSettings $fileTypes): void
    {
        $optionalTypes = array_diff(array_keys(FileTypeSettings::labels()), FileTypeSettings::defaultTypes());
        $optionalSelected = count(array_unique(array_merge(
            array_intersect($fileTypes->selected(FileTypeSettings::CONTEXT_MALWARE), $optionalTypes),
            array_intersect($fileTypes->selected(FileTypeSettings::CONTEXT_BACKUP), $optionalTypes)
        )));

        $items = [
            [
                'id' => 'challenge',
                'href' => '#shieldx-human-challenge',
                'icon' => 'fingerprint',
                'label' => __('Human Challenge', 'xerotact-shield-x-security'),
                'value' => !empty($humanChallenge['configured']) ? __('Ready', 'xerotact-shield-x-security') : __('Optional', 'xerotact-shield-x-security'),
                'note' => __('Login and WAF challenge provider', 'xerotact-shield-x-security'),
                'warning' => false,
                'notice' => false,
                'warning_label' => '',
            ],
            [
                'id' => 'file-types',
                'href' => '#shieldx-file-type-policy',
                'icon' => 'page-search',
                'label' => __('File Type Policy', 'xerotact-shield-x-security'),
                'value' => $optionalSelected > 0 ? __('Optional enabled', 'xerotact-shield-x-security') : __('Optional off', 'xerotact-shield-x-security'),
                'note' => __('Malware and backup extensions', 'xerotact-shield-x-security'),
                'warning' => false,
                'notice' => false,
                'warning_label' => '',
            ],
            [
                'id' => 'settings',
                'href' => '#shieldx-settings-portability',
                'icon' => 'folder-settings',
                'label' => __('Export / Import Settings', 'xerotact-shield-x-security'),
                'value' => __('Settings JSON', 'xerotact-shield-x-security'),
                'note' => __('Move configuration to another site', 'xerotact-shield-x-security'),
                'warning' => false,
                'notice' => false,
                'warning_label' => '',
            ],
            [
                'id' => 'diagnostics',
                'href' => '#shieldx-system-diagnostics',
                'icon' => 'dashboard-speed',
                'label' => __('Diagnostics', 'xerotact-shield-x-security'),
                'value' => __('Health and reports', 'xerotact-shield-x-security'),
                'note' => __('Audit, compatibility, runtime, and data', 'xerotact-shield-x-security'),
                'warning' => false,
                'notice' => false,
                'warning_label' => '',
            ],
        ];
        ?>
        <nav class="shieldx-scan-selector" data-shieldx-system-selector role="tablist" aria-label="<?php echo esc_attr__('System setup sections', 'xerotact-shield-x-security'); ?>">
            <?php foreach ($items as $item) : ?>
                <?php
                $hasWarning = !empty($item['warning']);
                $hasNotice = !empty($item['notice']);
                $warningLabel = (string) ($item['warning_label'] ?? '');
                $stateClass = ($hasWarning ? ' has-warning' : '') . ($hasNotice ? ' has-notice' : '');
                ?>
                <a
                    id="<?php echo esc_attr('shieldx-system-tab-' . (string) $item['id']); ?>"
                    class="<?php echo esc_attr('shieldx-scan-selector-card' . ($item['id'] === 'challenge' ? ' is-active' : '') . $stateClass); ?>"
                    href="<?php echo esc_url((string) $item['href']); ?>"
                    role="tab"
                    data-shieldx-system-tab="<?php echo esc_attr((string) $item['id']); ?>"
                    aria-controls="<?php echo esc_attr(ltrim((string) $item['href'], '#')); ?>"
                    aria-selected="<?php echo esc_attr($item['id'] === 'challenge' ? 'true' : 'false'); ?>"
                    tabindex="<?php echo esc_attr($item['id'] === 'challenge' ? '0' : '-1'); ?>"
                    <?php echo ($hasWarning || $hasNotice) && $warningLabel !== '' ? 'title="' . esc_attr($warningLabel) . '"' : ''; ?>
                >
                    <span class="shieldx-scan-selector-icon"><?php echo wp_kses(Iconoir::svg((string) $item['icon']), $this->allowedIconSvg()); ?></span>
                    <span class="shieldx-scan-selector-copy">
                        <strong><?php echo esc_html((string) $item['label']); ?></strong>
                        <em><?php echo esc_html((string) $item['value']); ?></em>
                        <small><?php echo esc_html((string) $item['note']); ?></small>
                    </span>
                    <?php if ($hasWarning || $hasNotice) : ?>
                        <span class="shieldx-scan-selector-warning" aria-hidden="true">
                            <?php echo wp_kses(Iconoir::svg('message-alert', 'shieldx-icon shieldx-scan-selector-warning-icon'), $this->allowedIconSvg()); ?>
                        </span>
                        <?php if ($warningLabel !== '') : ?>
                            <span class="shieldx-screen-reader-text"><?php echo esc_html($warningLabel); ?></span>
                        <?php endif; ?>
                    <?php endif; ?>
                </a>
            <?php endforeach; ?>
        </nav>
        <?php
    }

    private function renderFileTypePolicyPanel(FileTypeSettings $settings): void
    {
        $malwareSelected = $settings->selected(FileTypeSettings::CONTEXT_MALWARE);
        $backupSelected = $settings->selected(FileTypeSettings::CONTEXT_BACKUP);
        ?>
        <section id="shieldx-file-type-policy" class="shieldx-panel shieldx-panel--full shieldx-system-primary-panel shieldx-file-type-policy-panel" data-shieldx-system-panel="file-types" data-shieldx-accordion-open="1" role="tabpanel" aria-labelledby="shieldx-system-tab-file-types">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('File Type Policy', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html__('Media is unselected by default', 'xerotact-shield-x-security'); ?></span>
            </div>
            <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                <input type="hidden" name="action" value="shieldx_save_file_type_settings">
                <?php wp_nonce_field('shieldx_save_file_type_settings'); ?>

                <div class="shieldx-settings-grid shieldx-settings-grid--two shieldx-file-type-policy-grid">
                    <?php $this->renderFileTypeColumn(__('Malware scan', 'xerotact-shield-x-security'), __('File types inspected by malware and integrity scans.', 'xerotact-shield-x-security'), 'malware_file_types', $malwareSelected); ?>
                    <?php $this->renderFileTypeColumn(__('Backup', 'xerotact-shield-x-security'), __('File types captured by restore points and shadow backup scans.', 'xerotact-shield-x-security'), 'backup_file_types', $backupSelected); ?>
                </div>

                <div class="shieldx-form-actions">
                    <button type="submit" class="button button-primary"><?php echo esc_html__('Save file type policy', 'xerotact-shield-x-security'); ?></button>
                </div>
            </form>
        </section>
        <?php
    }

    /**
     * @param array<int,string> $selected
     */
    private function renderFileTypeColumn(string $title, string $description, string $fieldName, array $selected): void
    {
        ?>
        <div class="shieldx-file-type-column">
            <div class="shieldx-file-type-column-head">
                <h3><?php echo esc_html($title); ?></h3>
                <p><?php echo esc_html($description); ?></p>
            </div>
            <?php foreach (FileTypeSettings::catalog() as $groupId => $group) : ?>
                <?php $this->renderFileTypeGroup((string) $groupId, is_array($group) ? $group : [], $fieldName, $selected); ?>
            <?php endforeach; ?>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $group
     * @param array<int,string>   $selected
     */
    private function renderFileTypeGroup(string $groupId, array $group, string $fieldName, array $selected): void
    {
        $types = isset($group['types']) && is_array($group['types']) ? $group['types'] : [];
        if (empty($types)) {
            return;
        }
        ?>
        <fieldset class="<?php echo esc_attr('shieldx-file-type-group' . (!empty($group['media']) ? ' is-media' : '')); ?>">
            <legend>
                <strong><?php echo esc_html((string) ($group['label'] ?? $groupId)); ?></strong>
                <small><?php echo esc_html((string) ($group['description'] ?? '')); ?></small>
            </legend>
            <div class="shieldx-file-type-checks">
                <?php foreach ($types as $token => $label) : ?>
                    <?php $token = (string) $token; ?>
                    <label class="shieldx-inline-check shieldx-file-type-check">
                        <input type="checkbox" name="<?php echo esc_attr($fieldName); ?>[]" value="<?php echo esc_attr($token); ?>" <?php checked(in_array($token, $selected, true)); ?>>
                        <span><?php echo esc_html((string) $label); ?></span>
                    </label>
                <?php endforeach; ?>
            </div>
        </fieldset>
        <?php
    }

    /**
     * @param array<string,mixed> $humanChallenge
     */
    private function renderHumanChallengeSettingsPanel(array $humanChallenge): void
    {
        $provider = (string) ($humanChallenge['provider'] ?? 'cap');
        ?>
        <section id="shieldx-human-challenge" class="shieldx-panel shieldx-panel--full shieldx-system-primary-panel" data-shieldx-system-panel="challenge" data-shieldx-accordion-open="1" role="tabpanel" aria-labelledby="shieldx-system-tab-challenge">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Human Challenge', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html(!empty($humanChallenge['configured']) ? __('Cap.js Challenge (Captcha) ready', 'xerotact-shield-x-security') : __('Cap.js Challenge (Captcha) setup needed', 'xerotact-shield-x-security')); ?></span>
            </div>
            <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                <input type="hidden" name="action" value="shieldx_save_human_challenge_settings">
                <?php wp_nonce_field('shieldx_save_human_challenge_settings'); ?>

                <div class="shieldx-settings-grid shieldx-settings-grid--three">
                    <label class="shieldx-field">
                        <span><?php echo esc_html__('Provider', 'xerotact-shield-x-security'); ?></span>
                        <select name="human_challenge_provider">
                            <option value="cap" <?php selected($provider, 'cap'); ?>><?php echo esc_html__('Cap.js Challenge (Captcha)', 'xerotact-shield-x-security'); ?></option>
                            <option value="none" <?php selected($provider, 'none'); ?>><?php echo esc_html__('Disabled', 'xerotact-shield-x-security'); ?></option>
                        </select>
                        <small><?php echo esc_html__('SHIELD-X uses Cap.js Challenge (Captcha) only. Auto-generate creates a site-local Cap-compatible endpoint and keys.', 'xerotact-shield-x-security'); ?></small>
                    </label>
                    <label class="shieldx-field">
                        <span><?php echo esc_html__('Public endpoint URL', 'xerotact-shield-x-security'); ?></span>
                        <input type="url" name="human_challenge_endpoint_url" value="<?php echo esc_attr((string) ($humanChallenge['endpoint_url'] ?? '')); ?>" placeholder="https://captcha.example.com" autocomplete="off">
                        <small><?php echo esc_html__('Auto-generated by default. Replace only if you operate a separate Cap.js Challenge (Captcha) endpoint.', 'xerotact-shield-x-security'); ?></small>
                    </label>
                    <label class="shieldx-field">
                        <span><?php echo esc_html__('Site key', 'xerotact-shield-x-security'); ?></span>
                        <input type="text" name="human_challenge_site_key" value="<?php echo esc_attr((string) ($humanChallenge['site_key'] ?? '')); ?>" autocomplete="off">
                        <small><?php echo esc_html__('Generated automatically when blank.', 'xerotact-shield-x-security'); ?></small>
                    </label>
                    <label class="shieldx-field">
                        <span><?php echo esc_html__('Secret key', 'xerotact-shield-x-security'); ?></span>
                        <input type="password" name="human_challenge_secret_key" value="" placeholder="<?php echo esc_attr(!empty($humanChallenge['secret_key_configured']) ? __('Saved; leave blank to keep', 'xerotact-shield-x-security') : __('Auto-generated when blank', 'xerotact-shield-x-security')); ?>" autocomplete="new-password">
                        <small><?php echo esc_html__('Encrypted at rest. Used server-side for Cap.js Challenge (Captcha) /siteverify checks.', 'xerotact-shield-x-security'); ?></small>
                    </label>
                    <?php $this->renderNumberField('human_challenge_timeout_seconds', __('Verify timeout', 'xerotact-shield-x-security'), (int) ($humanChallenge['timeout_seconds'] ?? 5), 2, 20); ?>
                    <label class="shieldx-field">
                        <span><?php echo esc_html__('Endpoint failure behavior for custom contexts', 'xerotact-shield-x-security'); ?></span>
                        <select name="human_challenge_fail_behavior">
                            <option value="fail_closed" <?php selected((string) ($humanChallenge['fail_behavior'] ?? 'fail_closed'), 'fail_closed'); ?>><?php echo esc_html__('Fail closed', 'xerotact-shield-x-security'); ?></option>
                            <option value="fail_open" <?php selected((string) ($humanChallenge['fail_behavior'] ?? 'fail_closed'), 'fail_open'); ?>><?php echo esc_html__('Fail open and log', 'xerotact-shield-x-security'); ?></option>
                        </select>
                        <small><?php echo esc_html__('Login, password-reset, comment, and WAF challenges always fail closed.', 'xerotact-shield-x-security'); ?></small>
                    </label>
                    <label class="shieldx-hardening-option">
                        <input type="checkbox" name="human_challenge_waf_enabled" value="1" <?php checked(!empty($humanChallenge['waf_enabled'])); ?>>
                        <span>
                            <strong><?php echo esc_html__('Allow WAF challenge actions', 'xerotact-shield-x-security'); ?></strong>
                            <small><?php echo esc_html__('Rules set to Challenge show Cap.js Challenge (Captcha) instead of a direct block in enforce mode.', 'xerotact-shield-x-security'); ?></small>
                        </span>
                    </label>
                </div>

                <p class="shieldx-muted"><?php echo esc_html__('Login and password-reset usage is controlled from Login Security. This panel only configures the Cap.js Challenge (Captcha) provider and WAF challenge availability.', 'xerotact-shield-x-security'); ?></p>
                <div class="shieldx-form-actions">
                    <button type="submit" class="button button-primary"><?php echo esc_html__('Save Human Challenge', 'xerotact-shield-x-security'); ?></button>
                    <button type="submit" name="human_challenge_generate_settings" value="1" class="button"><?php echo esc_html__('Auto-generate Cap.js Challenge (Captcha) settings', 'xerotact-shield-x-security'); ?></button>
                </div>
            </form>
        </section>
        <?php
    }

    private function renderSettingsPortability(): void
    {
        ?>
        <section id="shieldx-settings-portability" class="shieldx-panel shieldx-panel--full shieldx-system-primary-panel" data-shieldx-system-panel="settings" data-shieldx-accordion-open="1" role="tabpanel" aria-labelledby="shieldx-system-tab-settings">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Export / Import Settings', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html__('Back up and restore settings on this same site', 'xerotact-shield-x-security'); ?></span>
            </div>
            <section class="shieldx-alert shieldx-alert--info">
                <strong><?php echo esc_html__('Site-bound export', 'xerotact-shield-x-security'); ?></strong>
                <p><?php echo esc_html__('SHIELD-X settings exports are signed with this site\'s WordPress salts. They can be imported back into this same site, but they are not portable to another WordPress install.', 'xerotact-shield-x-security'); ?></p>
            </section>
            <div class="shieldx-portability-grid">
                <form class="shieldx-portability-card" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                    <input type="hidden" name="action" value="shieldx_export_settings">
                    <?php wp_nonce_field('shieldx_export_settings'); ?>
                    <h3><?php echo esc_html__('Export settings', 'xerotact-shield-x-security'); ?></h3>
                    <p><?php echo esc_html__('Downloads WAF, hardening, lockdown policy, rate limit, scan schedule, file type policy, and login-security policy settings as JSON.', 'xerotact-shield-x-security'); ?></p>
                    <label class="shieldx-inline-check">
                        <input type="checkbox" name="include_site_secrets" value="1">
                        <span><?php echo esc_html__('Include site-specific recovery and external cron tokens', 'xerotact-shield-x-security'); ?></span>
                    </label>
                    <p class="shieldx-muted"><?php echo esc_html__('Per-user authenticator secrets, recovery codes, logs, scan history, and WAF events are never exported. Export signatures are site-bound and cannot be verified on another WordPress install.', 'xerotact-shield-x-security'); ?></p>
                    <button type="submit" class="button button-primary"><?php echo esc_html__('Download settings JSON', 'xerotact-shield-x-security'); ?></button>
                </form>

                <form class="shieldx-portability-card" method="post" enctype="multipart/form-data" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                    <input type="hidden" name="action" value="shieldx_import_settings">
                    <?php wp_nonce_field('shieldx_import_settings'); ?>
                    <h3><?php echo esc_html__('Import settings', 'xerotact-shield-x-security'); ?></h3>
                    <label class="shieldx-field shieldx-field--plain">
                        <span><?php echo esc_html__('Settings JSON file', 'xerotact-shield-x-security'); ?></span>
                        <input type="file" name="settings_import_file" accept="application/json,.json">
                    </label>
                    <label class="shieldx-field shieldx-field--plain shieldx-field--textarea">
                        <span><?php echo esc_html__('Or paste JSON', 'xerotact-shield-x-security'); ?></span>
                        <textarea name="settings_import_json" rows="5" spellcheck="false" placeholder="<?php echo esc_attr__('Paste SHIELD-X settings JSON here.', 'xerotact-shield-x-security'); ?>"></textarea>
                    </label>
                    <label class="shieldx-field shieldx-field--plain">
                        <span><?php echo esc_html__('Import mode', 'xerotact-shield-x-security'); ?></span>
                        <select name="settings_import_mode">
                            <option value="merge"><?php echo esc_html__('Merge with current settings', 'xerotact-shield-x-security'); ?></option>
                            <option value="replace"><?php echo esc_html__('Replace imported modules', 'xerotact-shield-x-security'); ?></option>
                        </select>
                    </label>
                    <p class="shieldx-muted"><?php echo esc_html__('Only settings exported from this same WordPress site can be imported. Imported lockdown policy is saved but not applied automatically. Review Lockdown before enabling it on this site.', 'xerotact-shield-x-security'); ?></p>
                    <button type="submit" class="button"><?php echo esc_html__('Import settings', 'xerotact-shield-x-security'); ?></button>
                </form>
            </div>
        </section>
        <?php
    }

    private function renderSystemCodeCard(string $title, string $body): void
    {
        ?>
        <div class="shieldx-system-code-card">
            <strong><?php echo esc_html($title); ?></strong>
            <pre><?php echo esc_html($body); ?></pre>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $status
     */
    private function renderUpdateCoordinationPanel(array $status): void
    {
        ?>
        <section class="shieldx-panel shieldx-system-side-panel" data-shieldx-accordion-open="1">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Update Coordination', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html(!empty($status['active']) ? __('Suspended', 'xerotact-shield-x-security') : __('Ready', 'xerotact-shield-x-security')); ?></span>
            </div>
            <dl class="shieldx-detail-list shieldx-detail-list--compact">
                <?php $this->renderDetail(__('Last event', 'xerotact-shield-x-security'), $this->formatLabel((string) ($status['last_event'] ?? ''))); ?>
                <?php $this->renderDetail(__('Operation', 'xerotact-shield-x-security'), (string) ($status['operation'] ?? '')); ?>
                <?php $this->renderDetail(__('Suspended at', 'xerotact-shield-x-security'), (string) ($status['suspended_at'] ?? '')); ?>
                <?php $this->renderDetail(__('Resumed at', 'xerotact-shield-x-security'), (string) ($status['resumed_at'] ?? '')); ?>
                <?php $this->renderDetail(__('Resume status', 'xerotact-shield-x-security'), $this->formatLabel((string) ($status['resume_status'] ?? ''))); ?>
                <?php $this->renderDetail(__('Restore point', 'xerotact-shield-x-security'), (string) ($status['restore_id'] ?? __('None', 'xerotact-shield-x-security'))); ?>
                <?php $this->renderDetail(__('Restore status', 'xerotact-shield-x-security'), $this->formatLabel((string) ($status['restore_status'] ?? ''))); ?>
                <?php $this->renderDetail(__('Rollback at', 'xerotact-shield-x-security'), (string) ($status['rollback_at'] ?? '')); ?>
            </dl>
            <p class="shieldx-muted"><?php echo esc_html__('When WordPress updates core, plugins, or themes, SHIELD-X creates a restore point, temporarily lifts Lockdown protections, reapplies them after success, and attempts rollback if the update request fails.', 'xerotact-shield-x-security'); ?></p>
        </section>
        <?php
    }

    /**
     * @param array<string,mixed> $report
     */
    private function renderDatabaseTableSizeReport(array $report): void
    {
        $available = !empty($report['available']);
        $tables = isset($report['tables']) && is_array($report['tables']) ? $report['tables'] : [];
        $totals = isset($report['totals']) && is_array($report['totals']) ? $report['totals'] : [];
        ?>
        <section class="shieldx-panel shieldx-panel--wide">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Database Table Size Report', 'xerotact-shield-x-security'); ?></h2>
                <span>
                    <?php
                    echo esc_html(sprintf(
                        /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                        __('%1$s tables / %2$s total', 'xerotact-shield-x-security'),
                        (string) ($totals['tables'] ?? 0),
                        $this->formatBytes((int) ($totals['total_bytes'] ?? 0))
                    ));
                    ?>
                </span>
            </div>

            <?php if (!$available) : ?>
                <p class="shieldx-muted"><?php echo esc_html((string) ($report['error'] ?? __('Database size data is not available on this host.', 'xerotact-shield-x-security'))); ?></p>
            <?php else : ?>
                <dl class="shieldx-detail-list shieldx-detail-list--compact">
                    <?php $this->renderDetail(__('Database', 'xerotact-shield-x-security'), (string) ($report['database'] ?? '')); ?>
                    <?php $this->renderDetail(__('Rows', 'xerotact-shield-x-security'), $this->formatInteger((int) ($totals['rows'] ?? 0))); ?>
                    <?php $this->renderDetail(__('Data / Index', 'xerotact-shield-x-security'), $this->formatBytes((int) ($totals['data_bytes'] ?? 0)) . ' / ' . $this->formatBytes((int) ($totals['index_bytes'] ?? 0))); ?>
                    <?php $this->renderDetail(__('SHIELD-X tables', 'xerotact-shield-x-security'), (string) ($totals['shieldx_tables'] ?? 0) . ' / ' . $this->formatBytes((int) ($totals['shieldx_total_bytes'] ?? 0))); ?>
                </dl>

                <div class="shieldx-compat-table-wrap">
                    <table class="widefat striped shieldx-compat-table shieldx-db-size-table">
                        <thead>
                            <tr>
                                <th><?php echo esc_html__('Table', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Rows', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Data', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Index', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Free', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Total', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Engine', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Updated', 'xerotact-shield-x-security'); ?></th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php foreach ($tables as $table) : ?>
                                <?php if (!is_array($table)) { continue; } ?>
                                <tr>
                                    <td>
                                        <code><?php echo esc_html((string) ($table['name'] ?? '')); ?></code>
                                        <?php if (!empty($table['is_shieldx'])) : ?>
                                            <span class="shieldx-pill shieldx-pill--info"><?php echo esc_html__('SHIELD-X', 'xerotact-shield-x-security'); ?></span>
                                        <?php endif; ?>
                                    </td>
                                    <td><?php echo esc_html($this->formatInteger((int) ($table['rows'] ?? 0))); ?></td>
                                    <td><?php echo esc_html($this->formatBytes((int) ($table['data_bytes'] ?? 0))); ?></td>
                                    <td><?php echo esc_html($this->formatBytes((int) ($table['index_bytes'] ?? 0))); ?></td>
                                    <td><?php echo esc_html($this->formatBytes((int) ($table['free_bytes'] ?? 0))); ?></td>
                                    <td><strong><?php echo esc_html($this->formatBytes((int) ($table['total_bytes'] ?? 0))); ?></strong></td>
                                    <td><?php echo esc_html((string) ($table['engine'] ?? '')); ?></td>
                                    <td><?php echo esc_html((string) ($table['updated_at'] ?? '')); ?></td>
                                </tr>
                            <?php endforeach; ?>
                            <?php if (!$tables) : ?>
                                <tr>
                                    <td colspan="8"><?php echo esc_html__('No WordPress-prefixed tables were returned by information_schema.', 'xerotact-shield-x-security'); ?></td>
                                </tr>
                            <?php endif; ?>
                        </tbody>
                    </table>
                </div>
            <?php endif; ?>
        </section>
        <?php
    }

    /**
     * @param array<string,mixed> $rotation
     */
    private function renderLogRotationPanel(array $rotation): void
    {
        $files = isset($rotation['files']) && is_array($rotation['files']) ? $rotation['files'] : [];
        $logRows = isset($files['items']) && is_array($files['items']) ? $files['items'] : [];
        if (empty($logRows) && isset($files['recent']) && is_array($files['recent'])) {
            $logRows = $files['recent'];
        }
        $logRowVisibleLimit = 8;
        $logRowTotal = count($logRows);
        $excludedFileLogs = isset($rotation['excluded_file_logs']) && is_array($rotation['excluded_file_logs']) ? array_map('strval', $rotation['excluded_file_logs']) : [];
        $fileLogSuggestions = $this->logFileSuggestions($files);
        $lastResult = isset($rotation['last_result']) && is_array($rotation['last_result']) ? $rotation['last_result'] : [];
        $fileScan = isset($rotation['file_scan']) && is_array($rotation['file_scan']) ? $rotation['file_scan'] : [];
        $targetStatus = isset($rotation['target_status']) && is_array($rotation['target_status']) ? $rotation['target_status'] : [];
        $logPreviewKey = $this->queryText('shieldx_log_preview');
        $logPreview = $logPreviewKey !== '' ? $this->buildLogPreview($files, $logPreviewKey) : [];
        $logScanSummary = !empty($fileScan['scanned_at'])
            /* translators: 1: marked log count, 2: total discovered log count, 3: scan time. */
            ? sprintf(__('%1$d of %2$d logs marked for rotation. Last scan %3$s.', 'xerotact-shield-x-security'), (int) ($fileScan['marked_files'] ?? 0), (int) ($fileScan['total_files'] ?? 0), $this->formatTime((string) $fileScan['scanned_at']))
            : __('Scan discovers WordPress runtime log files and marks them for rotation unless excluded below.', 'xerotact-shield-x-security');
        ?>
        <section class="shieldx-panel shieldx-panel--full" data-shieldx-accordion-open="1">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Rotation Policy', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html(!empty($rotation['enabled']) ? __('Enabled', 'xerotact-shield-x-security') : __('Disabled', 'xerotact-shield-x-security')); ?></span>
            </div>

            <form id="shieldx-log-scan-form" class="shieldx-hidden-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" data-shieldx-scan-run-form>
                <input type="hidden" name="action" value="shieldx_scan_log_rotation">
                <?php wp_nonce_field('shieldx_scan_log_rotation'); ?>
            </form>

            <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                <input type="hidden" name="action" value="shieldx_save_log_rotation">
                <?php wp_nonce_field('shieldx_save_log_rotation'); ?>

                <div class="shieldx-settings-grid">
                    <label class="shieldx-hardening-option">
                        <input type="checkbox" name="log_rotation_enabled" value="1" <?php checked(!empty($rotation['enabled'])); ?>>
                        <span>
                            <strong><?php echo esc_html__('Enable log rotation', 'xerotact-shield-x-security'); ?></strong>
                            <small><?php echo esc_html__('Prunes selected SHIELD-X file and database logs during hourly maintenance or when you run it manually.', 'xerotact-shield-x-security'); ?></small>
                        </span>
                    </label>
                    <label class="shieldx-field">
                        <span><?php echo esc_html__('Retention days', 'xerotact-shield-x-security'); ?></span>
                        <input type="number" min="1" max="3650" name="log_retention_days" value="<?php echo esc_attr((string) ($rotation['retention_days'] ?? 90)); ?>">
                    </label>
                    <label class="shieldx-field">
                        <span><?php echo esc_html__('Maximum log files', 'xerotact-shield-x-security'); ?></span>
                        <input type="number" min="1" max="7300" name="log_max_files" value="<?php echo esc_attr((string) ($rotation['max_files'] ?? 90)); ?>">
                    </label>
                </div>

                <h3 class="shieldx-subsection-title"><?php echo esc_html__('Select Rotation Targets', 'xerotact-shield-x-security'); ?></h3>
                <div class="shieldx-rotation-target-grid">
                    <?php foreach ($targetStatus as $target) : ?>
                        <?php
                        if (!is_array($target)) {
                            continue;
                        }
                        $id = (string) ($target['id'] ?? '');
                        $type = (string) ($target['type'] ?? '');
                        $inventory = isset($target['inventory']) && is_array($target['inventory']) ? $target['inventory'] : [];
                        $isFile = $type === 'file';
                        $count = (int) ($inventory['count'] ?? 0);
                        $meta = $isFile
                            /* translators: 1: file count, 2: formatted byte size. */
                            ? sprintf(__('%1$d files / %2$s', 'xerotact-shield-x-security'), $count, $this->formatBytes((int) ($inventory['total_bytes'] ?? 0)))
                            /* translators: %d: number of database rows exported. */
                            : sprintf(__('%d rows', 'xerotact-shield-x-security'), $count);
                        $oldest = (string) ($inventory['oldest_at'] ?? '');
                        $newest = (string) ($inventory['newest_at'] ?? '');
                        ?>
                        <label class="shieldx-hardening-option shieldx-rotation-target">
                            <input type="checkbox" name="log_rotation_targets[]" value="<?php echo esc_attr($id); ?>" <?php checked(!empty($target['selected'])); ?>>
                            <span>
                                <strong><?php echo esc_html((string) ($target['label'] ?? $id)); ?></strong>
                                <small><?php echo esc_html((string) ($target['description'] ?? '')); ?></small>
                                <em><?php echo esc_html($this->formatLabel($type) . ' / ' . $meta); ?></em>
                                <?php if (!$isFile && ($oldest !== '' || $newest !== '')) : ?>
                                    <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                                    <small><?php echo esc_html(sprintf(__('Oldest %1$s, newest %2$s', 'xerotact-shield-x-security'), $oldest !== '' ? $oldest : __('unknown', 'xerotact-shield-x-security'), $newest !== '' ? $newest : __('unknown', 'xerotact-shield-x-security'))); ?></small>
                                <?php endif; ?>
                                <?php if (empty($target['available'])) : ?>
                                    <small><?php echo esc_html__('Database target unavailable on this runtime.', 'xerotact-shield-x-security'); ?></small>
                                <?php endif; ?>
                            </span>
                        </label>
                    <?php endforeach; ?>
                </div>

                <div class="shieldx-log-file-exclusions">
                    <?php $this->renderTokenTextareaField('log_excluded_file_logs', __('Excluded file logs', 'xerotact-shield-x-security'), $this->lineListValue($excludedFileLogs), __('Selected log files are kept and will not be deleted by retention or maximum-file rotation. New daily logs continue to rotate normally.', 'xerotact-shield-x-security'), $fileLogSuggestions, __('Search log files to exclude', 'xerotact-shield-x-security'), false, 0); ?>
                </div>

                <?php
                $rotatedFileCount = (int) ($lastResult['deleted_files'] ?? 0) + (int) ($lastResult['trimmed_files'] ?? 0);
                $clearedByteCount = (int) ($lastResult['deleted_bytes'] ?? 0) + (int) ($lastResult['trimmed_bytes'] ?? 0);
                ?>
                <dl class="shieldx-detail-list shieldx-detail-list--compact shieldx-log-rotation-summary">
                    <?php $this->renderDetail(__('Primary log directory', 'xerotact-shield-x-security'), (string) ($rotation['directory'] ?? '')); ?>
                    <?php $this->renderDetail(__('Discovery', 'xerotact-shield-x-security'), (string) ($rotation['file_discovery'] ?? __('SHIELD-X and WordPress runtime logs.', 'xerotact-shield-x-security'))); ?>
                    <?php $this->renderDetail(__('Stored logs', 'xerotact-shield-x-security'), (string) ($files['count'] ?? 0) . ' / ' . $this->formatBytes((int) ($files['total_bytes'] ?? 0))); ?>
                    <?php /* translators: %d is the number of selected items. */ ?>
                    <?php $this->renderDetail(__('Excluded logs', 'xerotact-shield-x-security'), sprintf(__('%d selected', 'xerotact-shield-x-security'), (int) ($files['excluded_count'] ?? count($excludedFileLogs)))); ?>
                    <?php /* translators: %d is the number of log files currently marked for rotation. */ ?>
                    <?php $this->renderDetail(__('Marked for rotation', 'xerotact-shield-x-security'), sprintf(__('%d files', 'xerotact-shield-x-security'), max(0, (int) ($files['count'] ?? 0) - (int) ($files['excluded_count'] ?? count($excludedFileLogs))))); ?>
                    <?php $this->renderDetail(__('Last log scan', 'xerotact-shield-x-security'), !empty($fileScan['scanned_at']) ? $this->formatTime((string) $fileScan['scanned_at']) : __('Never', 'xerotact-shield-x-security')); ?>
                    <?php $this->renderDetail(__('Last run', 'xerotact-shield-x-security'), !empty($lastResult['ran_at']) ? $this->formatTime((string) $lastResult['ran_at']) : __('Never', 'xerotact-shield-x-security')); ?>
                    <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                    <?php $this->renderDetail(__('Last result', 'xerotact-shield-x-security'), !empty($lastResult['status']) ? $this->formatLabel((string) $lastResult['status']) . ' / ' . sprintf(__('%1$d files rotated, %2$s cleared, %3$d DB rows removed', 'xerotact-shield-x-security'), $rotatedFileCount, $this->formatBytes($clearedByteCount), (int) ($lastResult['deleted_rows'] ?? 0)) : __('No rotation recorded', 'xerotact-shield-x-security')); ?>
                </dl>

                <?php if ($logRows) : ?>
                    <div class="shieldx-compact-list shieldx-compact-list--logs" data-shieldx-log-files data-visible-step="<?php echo esc_attr((string) $logRowVisibleLimit); ?>">
                        <?php foreach ($logRows as $logIndex => $file) : ?>
                            <?php if (!is_array($file)) { continue; } ?>
                            <?php
                            $logName = (string) ($file['name'] ?? '');
                            $previewUrl = $logName !== ''
                                ? add_query_arg(
                                    [
                                        'page' => 'shield-x-log-rotation',
                                        'shieldx_log_preview' => $logName,
                                    ],
                                    admin_url('admin.php')
                                ) . '#shieldx-log-preview'
                                : '';
                            ?>
                            <div data-shieldx-log-file<?php if ($logIndex >= $logRowVisibleLimit) : ?> hidden<?php endif; ?>>
                                <span class="shieldx-log-name">
                                    <code><?php echo esc_html($logName); ?></code>
                                    <?php if ($previewUrl !== '') : ?>
                                        <?php /* translators: %s is the log file name. */ ?>
                                        <a class="shieldx-icon-link shieldx-log-preview-link" href="<?php echo esc_url($previewUrl); ?>" aria-label="<?php echo esc_attr(sprintf(__('Preview %s', 'xerotact-shield-x-security'), $logName)); ?>" title="<?php echo esc_attr__('Preview log', 'xerotact-shield-x-security'); ?>">
                                            <?php echo wp_kses(Iconoir::svg('eye', 'shieldx-icon'), $this->allowedIconSvg()); ?>
                                        </a>
                                    <?php endif; ?>
                                </span>
                                <span><?php echo esc_html($this->formatBytes((int) ($file['size'] ?? 0))); ?></span>
                                <?php if (!empty($file['excluded'])) : ?>
                                    <span class="shieldx-pill shieldx-pill--info"><?php echo esc_html__('Excluded', 'xerotact-shield-x-security'); ?></span>
                                <?php else : ?>
                                    <span class="shieldx-pill shieldx-pill--good"><?php echo esc_html__('Marked', 'xerotact-shield-x-security'); ?></span>
                                <?php endif; ?>
                                <span><?php echo esc_html((string) ($file['modified_at'] ?? '')); ?></span>
                            </div>
                        <?php endforeach; ?>
                        <?php if ($logRowTotal > $logRowVisibleLimit) : ?>
                            <div class="shieldx-log-files-actions">
                                <button type="button" class="button button-secondary button-small shieldx-log-files-more" data-shieldx-log-files-more data-label="<?php echo esc_attr__('Load more', 'xerotact-shield-x-security'); ?>" data-collapse-label="<?php echo esc_attr__('Show less', 'xerotact-shield-x-security'); ?>">
                                    <?php echo esc_html__('Load more', 'xerotact-shield-x-security'); ?>
                                </button>
                            </div>
                        <?php endif; ?>
                    </div>
                <?php endif; ?>

                <?php if ($logPreviewKey !== '') : ?>
                    <section id="shieldx-log-preview" class="shieldx-file-review shieldx-log-preview">
                        <div class="shieldx-file-review-head">
                            <div>
                                <h3><?php echo esc_html__('Log preview', 'xerotact-shield-x-security'); ?></h3>
                                <p><?php echo esc_html__('Read-only preview of a discovered log rotation file.', 'xerotact-shield-x-security'); ?></p>
                            </div>
                            <a class="button" href="<?php echo esc_url(add_query_arg(['page' => 'shield-x-log-rotation'], admin_url('admin.php'))); ?>"><?php echo esc_html__('Close preview', 'xerotact-shield-x-security'); ?></a>
                        </div>
                        <?php if (empty($logPreview['ok'])) : ?>
                            <p class="shieldx-alert shieldx-alert--warning"><?php echo esc_html((string) ($logPreview['error'] ?? __('The selected log could not be previewed.', 'xerotact-shield-x-security'))); ?></p>
                        <?php else : ?>
                            <div class="shieldx-file-review-meta">
                                <span><strong><?php echo esc_html__('Log file', 'xerotact-shield-x-security'); ?></strong> <code><?php echo esc_html((string) ($logPreview['name'] ?? '')); ?></code></span>
                                <span><strong><?php echo esc_html__('Size', 'xerotact-shield-x-security'); ?></strong> <?php echo esc_html($this->formatBytes((int) ($logPreview['size'] ?? 0))); ?></span>
                                <span><strong><?php echo esc_html__('Modified', 'xerotact-shield-x-security'); ?></strong> <?php echo esc_html((string) ($logPreview['modified_at'] ?? '')); ?></span>
                                <span><strong><?php echo esc_html__('Preview', 'xerotact-shield-x-security'); ?></strong> <?php echo esc_html((string) ($logPreview['summary'] ?? '')); ?></span>
                            </div>
                            <pre><?php echo esc_html((string) ($logPreview['content'] ?? '')); ?></pre>
                        <?php endif; ?>
                    </section>
                <?php endif; ?>

                <div class="shieldx-form-actions shieldx-form-actions--split">
                    <div class="shieldx-form-actions__left">
                        <button type="submit" form="shieldx-log-scan-form" class="button button-primary" data-shieldx-scan-run-button data-running-label="<?php echo esc_attr__('Scanning...', 'xerotact-shield-x-security'); ?>"><?php echo esc_html__('Scan log files', 'xerotact-shield-x-security'); ?></button>
                        <span class="shieldx-muted"><?php echo esc_html($logScanSummary); ?></span>
                    </div>
                    <div class="shieldx-form-actions__right">
                        <button type="submit" class="button"><?php echo esc_html__('Save log rotation', 'xerotact-shield-x-security'); ?></button>
                        <button type="submit" name="rotate_now" value="1" class="button button-primary"><?php echo esc_html__('Rotate now', 'xerotact-shield-x-security'); ?></button>
                    </div>
                </div>
            </form>
        </section>
        <?php
    }

    /**
     * @param array<string,mixed> $rotation
     */
    private function renderTempCleanupPanel(array $rotation): void
    {
        $tempCleanup = isset($rotation['temp_cleanup']) && is_array($rotation['temp_cleanup']) ? $rotation['temp_cleanup'] : [];
        $lastResult = isset($tempCleanup['last_result']) && is_array($tempCleanup['last_result']) ? $tempCleanup['last_result'] : [];
        $deleted = isset($lastResult['deleted']) && is_array($lastResult['deleted']) ? $lastResult['deleted'] : [];
        $deletedFiles = (int) ($lastResult['deleted_files'] ?? 0);
        $deletedDirectories = (int) ($lastResult['deleted_directories'] ?? 0);
        $deletedBytes = (int) ($lastResult['deleted_bytes'] ?? 0);
        $failed = (int) ($lastResult['failed_files'] ?? 0) + (int) ($lastResult['failed_directories'] ?? 0);
        $preview = LogRotator::fromWordPress()->previewTemporaryFiles();
        $previewTargets = array_values(array_filter(array_map(static function ($target): string {
            return is_scalar($target) ? trim((string) $target) : '';
        }, (array) ($preview['targets'] ?? []))));
        $previewFiles = (int) ($preview['candidate_files'] ?? 0);
        $previewDirectories = (int) ($preview['candidate_directories'] ?? 0);
        $previewBytes = (int) ($preview['candidate_bytes'] ?? 0);
        ?>
        <section class="shieldx-panel shieldx-panel--full">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Temporary Files Cleanup', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html(!empty($lastResult['ran_at']) ? $this->formatLabel((string) ($lastResult['status'] ?? 'completed')) : __('Ready', 'xerotact-shield-x-security')); ?></span>
            </div>
            <p class="shieldx-muted"><?php echo esc_html__('Removes stale whole-site temporary artifacts while skipping backups, quarantine, restore points, vendor folders, and SHIELD-X private storage.', 'xerotact-shield-x-security'); ?></p>

            <dl class="shieldx-detail-list shieldx-detail-list--compact shieldx-log-rotation-summary">
                <?php $this->renderDetail(__('WordPress root', 'xerotact-shield-x-security'), (string) ($tempCleanup['root'] ?? '')); ?>
                <?php $this->renderDetail(__('Cleanup policy', 'xerotact-shield-x-security'), (string) ($tempCleanup['policy'] ?? __('Stale temporary artifacts older than one hour.', 'xerotact-shield-x-security'))); ?>
                <?php $this->renderDetail(__('Last temp cleanup', 'xerotact-shield-x-security'), !empty($lastResult['ran_at']) ? $this->formatTime((string) $lastResult['ran_at']) : __('Never', 'xerotact-shield-x-security')); ?>
                <?php /* translators: 1: file count, 2: byte size, 3: empty directory count. */ ?>
                <?php $this->renderDetail(__('Last cleanup result', 'xerotact-shield-x-security'), !empty($lastResult['ran_at']) ? sprintf(__('%1$d files, %2$s, %3$d empty directories', 'xerotact-shield-x-security'), $deletedFiles, $this->formatBytes($deletedBytes), $deletedDirectories) : __('No cleanup recorded', 'xerotact-shield-x-security')); ?>
                <?php $this->renderDetail(__('Scanned entries', 'xerotact-shield-x-security'), !empty($lastResult['ran_at']) ? (string) ((int) ($lastResult['scanned_entries'] ?? 0)) : __('Not scanned', 'xerotact-shield-x-security')); ?>
                <?php /* translators: %d: number of failed temporary file or directory delete attempts. */ ?>
                <?php $this->renderDetail(__('Cleanup warnings', 'xerotact-shield-x-security'), $failed > 0 ? sprintf(__('%d failed delete attempts', 'xerotact-shield-x-security'), $failed) : __('None', 'xerotact-shield-x-security')); ?>
                <?php if (!empty($lastResult['truncated'])) : ?>
                    <?php $this->renderDetail(__('Cleanup limit', 'xerotact-shield-x-security'), __('Stopped after the safety scan limit. Run again to continue.', 'xerotact-shield-x-security')); ?>
                <?php endif; ?>
                <?php /* translators: 1: file count, 2: byte size, 3: empty directory count. */ ?>
                <?php $this->renderDetail(__('Will clean now', 'xerotact-shield-x-security'), sprintf(__('%1$d files, %2$s, %3$d empty directories', 'xerotact-shield-x-security'), $previewFiles, $this->formatBytes($previewBytes), $previewDirectories)); ?>
                <?php $this->renderDetail(__('Preview scanned', 'xerotact-shield-x-security'), (string) ((int) ($preview['scanned_entries'] ?? 0))); ?>
                <?php if (!empty($preview['truncated'])) : ?>
                    <?php $this->renderDetail(__('Preview limit', 'xerotact-shield-x-security'), __('Stopped after the safety scan limit. Clean and run again to continue.', 'xerotact-shield-x-security')); ?>
                <?php endif; ?>
            </dl>

            <div class="shieldx-cleanup-preview">
                <div class="shieldx-section-title shieldx-section-title--compact">
                    <div>
                        <span class="shieldx-kicker"><?php echo esc_html__('Cleanup preview', 'xerotact-shield-x-security'); ?></span>
                        <h3><?php echo esc_html__('Will clean now', 'xerotact-shield-x-security'); ?></h3>
                    </div>
                </div>
                <?php if ($previewTargets) : ?>
                    <div class="shieldx-compact-list shieldx-compact-list--logs">
                        <?php foreach ($previewTargets as $target) : ?>
                            <div>
                                <span class="shieldx-log-name"><code><?php echo esc_html($target); ?></code></span>
                            </div>
                        <?php endforeach; ?>
                    </div>
                <?php else : ?>
                    <p class="shieldx-empty"><?php echo esc_html__('No eligible stale temporary files or empty temporary folders were found right now.', 'xerotact-shield-x-security'); ?></p>
                <?php endif; ?>
            </div>

            <?php if ($deleted) : ?>
                <div class="shieldx-section-title shieldx-section-title--compact shieldx-cleanup-result-title">
                    <div>
                        <span class="shieldx-kicker"><?php echo esc_html__('Last cleanup result', 'xerotact-shield-x-security'); ?></span>
                        <h3><?php echo esc_html__('Removed previously', 'xerotact-shield-x-security'); ?></h3>
                    </div>
                </div>
                <div class="shieldx-compact-list shieldx-compact-list--logs">
                    <?php foreach ($deleted as $item) : ?>
                        <?php if (!is_array($item)) { continue; } ?>
                        <div>
                            <span class="shieldx-log-name"><code><?php echo esc_html((string) ($item['name'] ?? '')); ?></code></span>
                            <span><?php echo esc_html($this->formatLabel((string) ($item['type'] ?? 'file'))); ?></span>
                            <span><?php echo esc_html($this->formatBytes((int) ($item['size'] ?? 0))); ?></span>
                        </div>
                    <?php endforeach; ?>
                </div>
            <?php endif; ?>

            <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Clean stale temporary files from the WordPress directory now?', 'xerotact-shield-x-security')); ?>');">
                <input type="hidden" name="action" value="shieldx_cleanup_temp_files">
                <?php wp_nonce_field('shieldx_cleanup_temp_files'); ?>
                <div class="shieldx-form-actions">
                    <button type="submit" class="button button-primary"><?php echo esc_html__('Clean temp files', 'xerotact-shield-x-security'); ?></button>
                </div>
            </form>
        </section>
        <?php
    }

    private function renderUninstallDataPanel(): void
    {
        ?>
        <section class="shieldx-panel shieldx-panel--full">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Plugin Deletion', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html(Uninstaller::purgeDataOnUninstall() ? __('Purge enabled', 'xerotact-shield-x-security') : __('Data retained', 'xerotact-shield-x-security')); ?></span>
            </div>
            <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                <input type="hidden" name="action" value="shieldx_save_uninstall_settings">
                <?php wp_nonce_field('shieldx_save_uninstall_settings'); ?>

                <label class="shieldx-hardening-option">
                    <input type="checkbox" name="purge_data_on_uninstall" value="1" <?php checked(Uninstaller::purgeDataOnUninstall()); ?>>
                    <span>
                        <strong><?php echo esc_html__('Delete all SHIELD-X data when WordPress deletes the plugin', 'xerotact-shield-x-security'); ?></strong>
                        <small><?php echo esc_html__('Deactivation never deletes data. When enabled, WordPress plugin deletion removes SHIELD-X settings, database tables, logs, backups, restore points, and private storage.', 'xerotact-shield-x-security'); ?></small>
                    </span>
                </label>

                <div class="shieldx-form-actions">
                    <button type="submit" class="button button-primary"><?php echo esc_html__('Save deletion policy', 'xerotact-shield-x-security'); ?></button>
                </div>
            </form>
        </section>
        <?php
    }

    /**
     * @param array<string,mixed> $files
     * @return array<string,mixed>
     */
    private function buildLogPreview(array $files, string $requestedLog): array
    {
        $items = isset($files['items']) && is_array($files['items']) ? $files['items'] : [];
        $requestedLog = $this->normalizeReviewPath($requestedLog);
        if ($requestedLog === '') {
            return [
                'ok' => false,
                'error' => __('No log file was selected.', 'xerotact-shield-x-security'),
            ];
        }

        foreach ($items as $item) {
            if (!is_array($item)) {
                continue;
            }

            $name = $this->normalizeReviewPath((string) ($item['name'] ?? ''));
            if ($name === '' || !hash_equals($name, $requestedLog)) {
                continue;
            }

            $path = (string) ($item['path'] ?? '');
            $realPath = $this->allowedLogPreviewPath($path);
            if ($realPath === '') {
                return [
                    'ok' => false,
                    'error' => __('The selected log path is no longer readable or is outside the WordPress installation.', 'xerotact-shield-x-security'),
                ];
            }

            $size = filesize($realPath);
            $modified = filemtime($realPath);
            $read = $this->readLogPreviewTail($realPath, self::LOG_PREVIEW_BYTES);
            $content = (string) ($read['content'] ?? '');
            $redacted = false;
            $content = $this->redactReviewContent($content, $redacted);
            $truncated = !empty($read['truncated']);
            $summary = $truncated
                /* translators: %s is a human-readable byte count. */
                ? sprintf(__('Last %s shown', 'xerotact-shield-x-security'), $this->formatBytes((int) ($read['read_bytes'] ?? 0)))
                : __('Full log shown', 'xerotact-shield-x-security');
            if ($redacted) {
                $summary .= ' · ' . __('sensitive values redacted', 'xerotact-shield-x-security');
            }

            return [
                'ok' => true,
                'name' => $name,
                'path' => $realPath,
                'size' => is_int($size) ? $size : 0,
                'modified_at' => is_int($modified) ? gmdate('Y-m-d H:i:s', $modified) : '',
                'content' => $content,
                'summary' => $summary,
            ];
        }

        return [
            'ok' => false,
            'error' => __('The selected log was not found in the current log rotation inventory. Run Scan log files, then try again.', 'xerotact-shield-x-security'),
        ];
    }

    private function allowedLogPreviewPath(string $path): string
    {
        $real = realpath($path);
        if (!is_string($real) || $real === '' || !is_file($real) || !is_readable($real)) {
            return '';
        }

        $realPath = $this->normalizeFilesystemPath($real);
        $roots = [];
        $wordpressBase = WordPressPaths::root();
        if ($wordpressBase !== '') {
            $wordpressRoot = realpath($wordpressBase);
            if (is_string($wordpressRoot) && $wordpressRoot !== '') {
                $roots[] = rtrim($this->normalizeFilesystemPath($wordpressRoot), '/') . '/';
            }
        }

        if (defined('SHIELDX_STORAGE_DIR')) {
            $storageRoot = realpath((string) constant('SHIELDX_STORAGE_DIR'));
            if (is_string($storageRoot) && $storageRoot !== '') {
                $roots[] = rtrim($this->normalizeFilesystemPath($storageRoot), '/') . '/';
            }
        }

        foreach ($roots as $root) {
            if (strpos($realPath . '/', $root) === 0) {
                return $realPath;
            }
        }

        return '';
    }

    /**
     * @return array{content:string,truncated:bool,read_bytes:int}
     */
    private function readLogPreviewTail(string $path, int $maxBytes): array
    {
        $size = filesize($path);
        $size = is_int($size) ? max(0, $size) : 0;
        $readBytes = min($maxBytes, $size);
        $offset = max(0, $size - $readBytes);

        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- Reads a single admin-selected discovered log file for read-only preview.
        $handle = fopen($path, 'rb');
        if (!is_resource($handle)) {
            return [
                'content' => '',
                'truncated' => false,
                'read_bytes' => 0,
            ];
        }

        try {
            if ($offset > 0) {
                fseek($handle, $offset);
            }

            $content = $readBytes > 0 ? stream_get_contents($handle, $readBytes) : '';
        } finally {
            // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Closes the read-only preview handle opened above for an admin-selected log file.
            fclose($handle);
        }

        return [
            'content' => is_string($content) ? $content : '',
            'truncated' => $offset > 0,
            'read_bytes' => $readBytes,
        ];
    }

    /**
     * @param array<string,mixed> $status
     */
    private function renderEnvironmentPanel(array $status): void
    {
        ?>
        <section class="shieldx-panel shieldx-system-side-panel">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Environment', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html__('Shared-hosting baseline', 'xerotact-shield-x-security'); ?></span>
            </div>
            <dl class="shieldx-detail-list">
                <?php $this->renderDetail(__('Memory', 'xerotact-shield-x-security'), (string) $status['environment']['memory_limit']); ?>
                <?php $this->renderDetail(__('Execution time', 'xerotact-shield-x-security'), (string) $status['environment']['max_execution_time'] . 's'); ?>
                <?php $this->renderDetail(__('Storage path', 'xerotact-shield-x-security'), (string) $status['storage']['base_dir']); ?>
                <?php $this->renderDetail(__('Schema version', 'xerotact-shield-x-security'), (string) $status['schema']['version'] . ' / ' . (string) $status['schema']['expected_version']); ?>
            </dl>
            <?php if (!empty($status['storage']['warning'])) : ?>
                <p class="shieldx-alert shieldx-alert--warning"><?php echo esc_html((string) $status['storage']['warning']); ?></p>
            <?php endif; ?>
        </section>
        <?php
    }

    /**
     * @param array<string,mixed> $status
     */
    private function renderEmergencyRecoveryPanel(array $status): void
    {
        $loginSecurity = isset($status['login_security']) && is_array($status['login_security']) ? $status['login_security'] : [];
        $recovery = $this->revealedEmergencyRecoveryAccess();
        $url = (string) ($recovery['url'] ?? '');
        $token = (string) ($recovery['token'] ?? '');
        $createdAt = (string) ($loginSecurity['emergency_recovery_token_created_at'] ?? '');
        $usedAt = (string) ($loginSecurity['emergency_recovery_used_at'] ?? '');
        $adminsEnrolled = (int) ($loginSecurity['admins_enrolled'] ?? 0);
        ?>
        <section class="shieldx-panel shieldx-panel--full">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Emergency Recovery URL', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html($adminsEnrolled > 0 ? __('2FA protected', 'xerotact-shield-x-security') : __('2FA setup needed', 'xerotact-shield-x-security')); ?></span>
            </div>
            <div class="shieldx-recovery-url-grid">
                <div class="shieldx-field shieldx-field--textarea">
                    <span><?php echo esc_html__('Safe-mode endpoint', 'xerotact-shield-x-security'); ?></span>
                    <code class="shieldx-copy-url"><?php echo esc_html($url !== '' ? $url : (function_exists('home_url') ? home_url('/index.php?shieldx_recovery=1') : '/index.php?shieldx_recovery=1')); ?></code>
                    <span><?php echo esc_html__('Recovery token', 'xerotact-shield-x-security'); ?></span>
                    <code class="shieldx-copy-url"><?php echo esc_html($token !== '' ? $token : __('Hidden until revealed', 'xerotact-shield-x-security')); ?></code>
                    <div class="shieldx-button-row">
                        <?php if ($url !== '') : ?>
                            <button type="button" class="button" data-shieldx-copy="<?php echo esc_attr($url); ?>"><?php echo esc_html__('Copy endpoint', 'xerotact-shield-x-security'); ?></button>
                        <?php endif; ?>
                        <?php if ($token !== '') : ?>
                            <button type="button" class="button" data-shieldx-copy="<?php echo esc_attr($token); ?>"><?php echo esc_html__('Copy token', 'xerotact-shield-x-security'); ?></button>
                        <?php endif; ?>
                        <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                            <input type="hidden" name="action" value="shieldx_reveal_emergency_recovery_url">
                            <?php wp_nonce_field('shieldx_reveal_emergency_recovery_url'); ?>
                            <button type="submit" class="button"><?php echo esc_html__('Reveal URL', 'xerotact-shield-x-security'); ?></button>
                        </form>
                        <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                            <input type="hidden" name="action" value="shieldx_regenerate_emergency_recovery_url">
                            <?php wp_nonce_field('shieldx_regenerate_emergency_recovery_url'); ?>
                            <button type="submit" class="button"><?php echo esc_html__('Regenerate URL', 'xerotact-shield-x-security'); ?></button>
                        </form>
                    </div>
                    <small><?php echo esc_html__('Open the endpoint, then submit the token in the form flow or send it as X-ShieldX-Recovery-Token. The token is not placed in the URL.', 'xerotact-shield-x-security'); ?></small>
                </div>
                <dl class="shieldx-detail-list shieldx-detail-list--boxed">
                    <?php $this->renderDetail(__('Recovery action', 'xerotact-shield-x-security'), __('After 2FA, choose which protections to revert or disable.', 'xerotact-shield-x-security')); ?>
                    <?php $this->renderDetail(__('Enrolled admins', 'xerotact-shield-x-security'), (string) $adminsEnrolled); ?>
                    <?php $this->renderDetail(__('Created', 'xerotact-shield-x-security'), $createdAt !== '' ? $this->formatTime($createdAt) : __('Just generated', 'xerotact-shield-x-security')); ?>
                    <?php $this->renderDetail(__('Last used', 'xerotact-shield-x-security'), $usedAt !== '' ? $this->formatTime($usedAt) : __('Never', 'xerotact-shield-x-security')); ?>
                </dl>
            </div>
        </section>
        <?php
    }

    /**
     * @return array{url:string,token:string}
     */
    private function revealedEmergencyRecoveryAccess(): array
    {
        if (!function_exists('get_transient')) {
            return ['url' => '', 'token' => ''];
        }

        $token = get_transient($this->emergencyRecoveryRevealTransientKey());
        $token = is_string($token) ? trim($token) : '';

        return [
            'url' => function_exists('home_url') ? home_url('/index.php?shieldx_recovery=1') : '/index.php?shieldx_recovery=1',
            'token' => preg_match('/^[A-Za-z0-9._~+-]{24,256}$/', $token) === 1 ? $token : '',
        ];
    }

    private function storeRevealedEmergencyRecoveryToken(string $token): void
    {
        $token = trim($token);
        if ($token === '' || !function_exists('set_transient')) {
            return;
        }

        $ttl = defined('MINUTE_IN_SECONDS') ? 5 * MINUTE_IN_SECONDS : 300;
        set_transient($this->emergencyRecoveryRevealTransientKey(), $token, $ttl);
    }

    private function emergencyRecoveryRevealTransientKey(): string
    {
        $userId = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;

        $sessionToken = function_exists('wp_get_session_token') ? (string) wp_get_session_token() : '';
        $sessionSuffix = $sessionToken !== '' ? '_' . hash('sha256', $sessionToken) : '';

        return 'shieldx_recovery_reveal_' . (string) max(0, $userId) . $sessionSuffix;
    }

    private function renderDetail(string $label, string $value): void
    {
        ?>
        <dt><?php echo esc_html($label); ?></dt>
        <dd><?php echo esc_html($value); ?></dd>
        <?php
    }

    private function renderDetailLink(string $label, string $url): void
    {
        ?>
        <dt><?php echo esc_html($label); ?></dt>
        <dd><a href="<?php echo esc_url($url); ?>" target="_blank" rel="noopener noreferrer"><?php echo esc_html($url); ?></a></dd>
        <?php
    }

    /**
     * @return array<string,array<string,bool>>
     */
    private function allowedQrSvg(): array
    {
        return [
            'svg' => [
                'aria-label' => true,
                'class' => true,
                'height' => true,
                'role' => true,
                'viewBox' => true,
                'viewbox' => true,
                'width' => true,
                'xmlns' => true,
            ],
            'rect' => [
                'fill' => true,
                'height' => true,
                'width' => true,
                'x' => true,
                'y' => true,
            ],
            'path' => [
                'd' => true,
                'fill' => true,
            ],
        ];
    }

    /**
     * @return array<string,array<string,bool>>
     */
    private function allowedIconSvg(): array
    {
        return [
            'svg' => [
                'aria-hidden' => true,
                'class' => true,
                'fill' => true,
                'focusable' => true,
                'height' => true,
                'stroke-width' => true,
                'viewBox' => true,
                'viewbox' => true,
                'width' => true,
                'xmlns' => true,
            ],
            'path' => [
                'd' => true,
                'clip-rule' => true,
                'fill' => true,
                'fill-rule' => true,
                'stroke' => true,
                'stroke-dasharray' => true,
                'stroke-linecap' => true,
                'stroke-linejoin' => true,
                'stroke-width' => true,
            ],
        ];
    }

    /**
     * @param array<string,mixed>      $scanner
     * @param array<string,mixed>|null $lastScan
     * @param array<string,mixed>      $vulnerability
     */
    private function renderScanTypeSelector(array $scanner, ?array $lastScan, array $vulnerability): void
    {
        $active = 'file';
        $requestedScopes = $this->queryText('shieldx_scopes');
        if ($requestedScopes === 'database') {
            $active = 'database';
        }
        if ($this->requestedDatabaseReviewThreatId() !== '') {
            $active = 'database';
        }
        if (
            $this->queryFlag('shieldx_vuln_scanned')
            || $this->requestedVirtualReviewKey() !== ''
            || $this->isVirtualScannerPath($this->requestedReviewFilePath())
        ) {
            $active = 'vulnerability';
        }

        $preferMarkup = $this->requestedDatabaseReviewThreatId() !== ''
            || $this->requestedVirtualReviewKey() !== ''
            || $this->requestedReviewFilePath() !== ''
            || $this->requestedCoreComparePath() !== ''
            || $this->requestedPluginComparePath() !== ''
            || $this->requestedProtectedFilePath() !== ''
            || $this->queryFlag('shieldx_vuln_scanned');
        $preferredHash = '';
        if ($this->requestedDatabaseReviewThreatId() !== '') {
            $preferredHash = '#shieldx-db-review';
        } elseif ($this->requestedVirtualReviewKey() !== '' || $this->isVirtualScannerPath($this->requestedReviewFilePath())) {
            $preferredHash = '#shieldx-vulnerability-review';
        } elseif ($this->requestedProtectedFilePath() !== '') {
            $preferredHash = '#shieldx-protected-file';
        } elseif ($this->requestedReviewFilePath() !== '') {
            $preferredHash = '#shieldx-file-review';
        } elseif ($this->requestedCoreComparePath() !== '') {
            $preferredHash = '#shieldx-core-compare';
        } elseif ($this->requestedPluginComparePath() !== '') {
            $preferredHash = '#shieldx-plugin-compare-result';
        } elseif ($this->queryFlag('shieldx_vuln_scanned')) {
            $preferredHash = '#shieldx-vulnerability-scanner';
        }

        $items = [
            [
                'id' => 'file',
                'href' => '#shieldx-file-scanner',
                'icon' => 'page-search',
                'label' => __('File Scanner', 'xerotact-shield-x-security'),
                /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                'value' => sprintf(__('%d tracked files', 'xerotact-shield-x-security'), (int) ($scanner['tracked_files'] ?? 0)),
                /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                'note' => $lastScan ? sprintf(__('%d skipped last run', 'xerotact-shield-x-security'), (int) ($lastScan['files_skipped'] ?? 0)) : __('Smart file baseline', 'xerotact-shield-x-security'),
            ],
            [
                'id' => 'database',
                'href' => '#shieldx-database-scanner',
                'icon' => 'database-stats',
                'label' => __('Database Scanner', 'xerotact-shield-x-security'),
                'value' => ($lastScan && !empty($lastScan['database_selected']))
                    /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                    ? sprintf(__('%d DB checks', 'xerotact-shield-x-security'), (int) ($lastScan['database_items_scanned'] ?? 0))
                    : __('Ready', 'xerotact-shield-x-security'),
                'note' => ($lastScan && !empty($lastScan['database_selected']))
                    /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                    ? sprintf(__('%d DB issues last run', 'xerotact-shield-x-security'), (int) ($lastScan['database_issues_found'] ?? 0))
                    : __('Options, users, posts, cron', 'xerotact-shield-x-security'),
            ],
            [
                'id' => 'vulnerability',
                'href' => '#shieldx-vulnerability-scanner',
                'icon' => 'message-alert',
                'label' => __('Vulnerability Scanner', 'xerotact-shield-x-security'),
                /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                'value' => sprintf(__('%d findings', 'xerotact-shield-x-security'), (int) ($vulnerability['issues_found'] ?? 0)),
                'note' => !empty($vulnerability['last_scan_at'])
                    /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                    ? sprintf(__('Last checked %s', 'xerotact-shield-x-security'), $this->formatTime((string) $vulnerability['last_scan_at']))
                    : __('Core, plugins, themes', 'xerotact-shield-x-security'),
            ],
        ];
        ?>
        <nav class="shieldx-scan-selector" data-shieldx-scan-selector data-shieldx-prefer-markup="<?php echo esc_attr($preferMarkup ? '1' : '0'); ?>" data-shieldx-preferred-hash="<?php echo esc_attr($preferredHash); ?>" role="tablist" aria-label="<?php echo esc_attr__('Scan sections', 'xerotact-shield-x-security'); ?>">
            <?php foreach ($items as $index => $item) : ?>
                <a
                    id="<?php echo esc_attr('shieldx-scan-tab-' . (string) $item['id']); ?>"
                    class="<?php echo esc_attr('shieldx-scan-selector-card' . ($active === $item['id'] ? ' is-active' : '')); ?>"
                    href="<?php echo esc_url((string) $item['href']); ?>"
                    role="tab"
                    data-shieldx-scan-tab="<?php echo esc_attr((string) $item['id']); ?>"
                    aria-controls="<?php echo esc_attr(ltrim((string) $item['href'], '#')); ?>"
                    aria-selected="<?php echo esc_attr($active === $item['id'] ? 'true' : 'false'); ?>"
                    tabindex="<?php echo esc_attr($active === $item['id'] ? '0' : '-1'); ?>"
                >
                    <span class="shieldx-scan-selector-icon"><?php echo wp_kses(Iconoir::svg((string) $item['icon']), $this->allowedIconSvg()); ?></span>
                    <span class="shieldx-scan-selector-copy">
                        <strong><?php echo esc_html((string) $item['label']); ?></strong>
                        <em><?php echo esc_html((string) $item['value']); ?></em>
                        <small><?php echo esc_html((string) $item['note']); ?></small>
                    </span>
                    <span class="shieldx-scan-selector-step" aria-hidden="true"><?php echo esc_html((string) ($index + 1)); ?></span>
                </a>
            <?php endforeach; ?>
        </nav>
        <?php
    }

    /**
     * @param array<string,mixed> $preview
     */
    private function renderLockdownPreview(array $preview): void
    {
        $actions = isset($preview['actions']) && is_array($preview['actions']) ? $preview['actions'] : [];
        $coverage = isset($preview['coverage']) && is_array($preview['coverage']) ? $preview['coverage'] : [];
        ?>
        <dl class="shieldx-detail-list">
            <?php $this->renderDetail(__('Exec block', 'xerotact-shield-x-security'), $this->formatLabel((string) ($coverage['exec_block'] ?? 'not_applied'))); ?>
            <?php $this->renderDetail(__('Read-only', 'xerotact-shield-x-security'), $this->formatLabel((string) ($coverage['readonly'] ?? 'not_applied'))); ?>
            <?php $this->renderDetail(__('Update/install lock', 'xerotact-shield-x-security'), $this->formatLabel((string) ($coverage['editor_lock'] ?? 'not_applied'))); ?>
            <?php $this->renderDetail(__('Coverage note', 'xerotact-shield-x-security'), (string) ($coverage['message'] ?? '')); ?>
        </dl>
        <?php if (empty($actions)) : ?>
            <p class="shieldx-empty"><?php echo esc_html__('No lockdown actions are currently planned.', 'xerotact-shield-x-security'); ?></p>
            <?php return; ?>
        <?php endif; ?>

        <div class="shieldx-compat-table-wrap">
            <table class="widefat striped shieldx-compat-table">
                <thead>
                    <tr>
                        <th><?php echo esc_html__('Action', 'xerotact-shield-x-security'); ?></th>
                        <th><?php echo esc_html__('Path', 'xerotact-shield-x-security'); ?></th>
                        <th><?php echo esc_html__('Target', 'xerotact-shield-x-security'); ?></th>
                        <th><?php echo esc_html__('Status', 'xerotact-shield-x-security'); ?></th>
                        <th><?php echo esc_html__('Detail', 'xerotact-shield-x-security'); ?></th>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach ($actions as $action) : ?>
                        <?php $action = is_array($action) ? $action : []; ?>
                        <tr>
                            <td><?php echo esc_html($this->formatLabel((string) ($action['type'] ?? ''))); ?></td>
                            <td><code><?php echo esc_html((string) ($action['path'] ?? '')); ?></code></td>
                            <td><code><?php echo esc_html((string) ($action['target'] ?? '')); ?></code></td>
                            <td><?php echo esc_html($this->formatLabel((string) ($action['status'] ?? ''))); ?></td>
                            <td><?php echo esc_html((string) ($action['detail'] ?? '')); ?></td>
                        </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $status
     */
    private function renderCompatibilityChecks(array $status): void
    {
        $compatibility = $this->compatibility($status);
        $summary = isset($compatibility['summary']) && is_array($compatibility['summary']) ? $compatibility['summary'] : [];
        $checks = isset($compatibility['checks']) && is_array($compatibility['checks']) ? $compatibility['checks'] : [];
        ?>
        <section class="shieldx-panel shieldx-panel--full">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Compatibility Checks', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html($this->formatCompatibilitySummary($summary)); ?></span>
            </div>
            <div class="shieldx-compat-table-wrap">
                <table class="widefat striped shieldx-compat-table">
                    <thead>
                        <tr>
                            <th><?php echo esc_html__('Check', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Status', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Level', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Detected', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Required', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Used by', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Impact', 'xerotact-shield-x-security'); ?></th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ($checks as $check) : ?>
                            <?php $this->renderCompatibilityCheckRow(is_array($check) ? $check : []); ?>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </section>
        <?php
    }

    /**
     * @param array<string,mixed> $status
     */
    private function renderModuleCompatibility(array $status): void
    {
        $compatibility = $this->compatibility($status);
        $modules = isset($compatibility['modules']) && is_array($compatibility['modules']) ? $compatibility['modules'] : [];
        ?>
        <section class="shieldx-panel shieldx-panel--full">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('SHIELD-X Module Readiness', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html__('Runtime dependencies by module', 'xerotact-shield-x-security'); ?></span>
            </div>
            <div class="shieldx-compat-table-wrap">
                <table class="widefat striped shieldx-compat-table">
                    <thead>
                        <tr>
                            <th><?php echo esc_html__('Module', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Status', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Mode', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Dependency', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Missing', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Notes', 'xerotact-shield-x-security'); ?></th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ($modules as $module) : ?>
                            <?php $this->renderModuleCompatibilityRow(is_array($module) ? $module : []); ?>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </section>
        <?php
    }

    /**
     * @param array<string,mixed> $status
     */
    private function renderPhpExtensionInventory(array $status): void
    {
        $compatibility = $this->compatibility($status);
        $extensions = isset($compatibility['php_extensions']) && is_array($compatibility['php_extensions']) ? $compatibility['php_extensions'] : [];
        ?>
        <section class="shieldx-panel shieldx-panel--full">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('PHP Extensions', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html__('Required, recommended, and optional accelerators', 'xerotact-shield-x-security'); ?></span>
            </div>
            <div class="shieldx-compat-table-wrap">
                <table class="widefat striped shieldx-compat-table">
                    <thead>
                        <tr>
                            <th><?php echo esc_html__('Extension', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Status', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Level', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Used by', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Impact', 'xerotact-shield-x-security'); ?></th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ($extensions as $extension) : ?>
                            <?php $this->renderRuntimeInventoryRow(is_array($extension) ? $extension : [], 'loaded'); ?>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </section>
        <?php
    }

    /**
     * @param array<string,mixed> $status
     */
    private function renderPhpFunctionInventory(array $status): void
    {
        $compatibility = $this->compatibility($status);
        $functions = isset($compatibility['php_functions']) && is_array($compatibility['php_functions']) ? $compatibility['php_functions'] : [];
        ?>
        <section class="shieldx-panel shieldx-panel--full">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('PHP Functions', 'xerotact-shield-x-security'); ?></h2>
                <span><?php echo esc_html__('Low-level runtime helpers SHIELD-X uses', 'xerotact-shield-x-security'); ?></span>
            </div>
            <div class="shieldx-compat-table-wrap">
                <table class="widefat striped shieldx-compat-table">
                    <thead>
                        <tr>
                            <th><?php echo esc_html__('Function', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Status', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Level', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Used by', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Impact', 'xerotact-shield-x-security'); ?></th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ($functions as $function) : ?>
                            <?php $this->renderRuntimeInventoryRow(is_array($function) ? $function : [], 'available'); ?>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </section>
        <?php
    }

    /**
     * @param array<string,mixed> $check
     */
    private function renderCompatibilityCheckRow(array $check): void
    {
        $usedBy = isset($check['used_by']) && is_array($check['used_by'])
            ? implode(', ', array_map('strval', $check['used_by']))
            : (string) ($check['used_by'] ?? '');
        ?>
        <tr>
            <td><strong><?php echo esc_html((string) ($check['label'] ?? '')); ?></strong></td>
            <td><?php $this->renderStatusBadge((string) ($check['status'] ?? 'warning')); ?></td>
            <td><?php echo esc_html($this->formatLabel((string) ($check['level'] ?? ''))); ?></td>
            <td><code><?php echo esc_html((string) ($check['detected'] ?? '')); ?></code></td>
            <td><?php echo esc_html((string) ($check['required'] ?? '')); ?></td>
            <td><?php echo esc_html($usedBy); ?></td>
            <td><?php echo esc_html((string) ($check['impact'] ?? '')); ?></td>
        </tr>
        <?php
    }

    /**
     * @param array<string,mixed> $module
     */
    private function renderModuleCompatibilityRow(array $module): void
    {
        $missing = isset($module['missing']) && is_array($module['missing'])
            ? implode(', ', array_map('strval', $module['missing']))
            : (string) ($module['missing'] ?? '');
        ?>
        <tr>
            <td><strong><?php echo esc_html((string) ($module['module'] ?? '')); ?></strong></td>
            <td><?php $this->renderStatusBadge((string) ($module['status'] ?? 'warning')); ?></td>
            <td><code><?php echo esc_html((string) ($module['mode'] ?? '')); ?></code></td>
            <td><?php echo esc_html((string) ($module['dependency'] ?? '')); ?></td>
            <td><?php echo esc_html($missing !== '' ? $missing : __('None', 'xerotact-shield-x-security')); ?></td>
            <td><?php echo esc_html((string) ($module['notes'] ?? '')); ?></td>
        </tr>
        <?php
    }

    /**
     * @param array<string,mixed> $item
     */
    private function renderRuntimeInventoryRow(array $item, string $availabilityKey): void
    {
        $name = (string) ($item['name'] ?? '');
        $available = !empty($item[$availabilityKey]);
        ?>
        <tr>
            <td><code><?php echo esc_html($name); ?></code></td>
            <td><?php $this->renderStatusBadge((string) ($item['status'] ?? ($available ? 'ready' : 'warning'))); ?></td>
            <td><?php echo esc_html($this->formatLabel((string) ($item['level'] ?? ''))); ?></td>
            <td><?php echo esc_html((string) ($item['used_by'] ?? '')); ?></td>
            <td><?php echo esc_html((string) ($item['impact'] ?? '')); ?></td>
        </tr>
        <?php
    }

    /**
     * @param array<string,mixed> $vulnerability
     * @param array<int,array<string,mixed>> $issues
     */
    private function renderVulnerabilityScannerPanel(array $vulnerability, array $schedule, array $issues = []): void
    {
        $advisoryCache = isset($vulnerability['advisory_cache']) && is_array($vulnerability['advisory_cache'])
            ? $vulnerability['advisory_cache']
            : [];
        $feedSettings = isset($vulnerability['feed_settings']) && is_array($vulnerability['feed_settings'])
            ? $vulnerability['feed_settings']
            : VulnerabilityScanner::settings();
        $feedSource = (string) ($feedSettings['feed_source'] ?? 'wpvulnerability');
        $customFeedUrl = (string) ($feedSettings['custom_feed_url'] ?? '');
        $lastScan = (string) ($vulnerability['last_scan_at'] ?? '');
        $software = isset($vulnerability['software']) && is_array($vulnerability['software'])
            ? array_values(array_filter($vulnerability['software'], 'is_array'))
            : [];
        ?>
        <div class="shieldx-scan-actions">
            <dl class="shieldx-detail-list">
                <?php $this->renderDetail(__('Last checked', 'xerotact-shield-x-security'), $lastScan !== '' ? $this->formatTime($lastScan) : __('Never', 'xerotact-shield-x-security')); ?>
                <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                <?php $this->renderDetail(__('Inventory', 'xerotact-shield-x-security'), sprintf(__('%1$d plugins, %2$d themes, core %3$s', 'xerotact-shield-x-security'), (int) ($vulnerability['plugins_total'] ?? 0), (int) ($vulnerability['themes_total'] ?? 0), (string) ($vulnerability['core_version'] ?? __('Unknown', 'xerotact-shield-x-security')))); ?>
                <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                <?php $this->renderDetail(__('Software inventory', 'xerotact-shield-x-security'), sprintf(__('%1$d detected / %2$d checked', 'xerotact-shield-x-security'), (int) ($vulnerability['software_detected'] ?? 0), (int) ($vulnerability['software_total'] ?? 0))); ?>
                <?php $this->renderDetail(__('Updates found', 'xerotact-shield-x-security'), (string) ($vulnerability['updates_found'] ?? 0)); ?>
                <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                <?php $this->renderDetail(__('Stale / abandoned', 'xerotact-shield-x-security'), sprintf(__('%1$d stale / %2$d abandoned', 'xerotact-shield-x-security'), (int) ($vulnerability['stale_plugins'] ?? 0), (int) ($vulnerability['abandoned_plugins'] ?? 0))); ?>
                <?php $this->renderDetail(__('Risky ranges', 'xerotact-shield-x-security'), (string) ($vulnerability['risky_version_ranges'] ?? 0)); ?>
                <?php $this->renderDetail(__('Advisory hits', 'xerotact-shield-x-security'), (string) ($vulnerability['advisory_hits'] ?? 0)); ?>
                <?php $this->renderDetail(__('Software advisories', 'xerotact-shield-x-security'), (string) ($vulnerability['software_advisory_hits'] ?? 0)); ?>
                <?php $this->renderDetail(__('Virtual patches', 'xerotact-shield-x-security'), (string) ($vulnerability['virtual_patch_recommendations'] ?? 0)); ?>
                <?php $this->renderDetail(__('Feed source', 'xerotact-shield-x-security'), $this->formatLabel((string) ($advisoryCache['source'] ?? $feedSource))); ?>
                <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                <?php $this->renderDetail(__('Feed records', 'xerotact-shield-x-security'), sprintf(__('%1$d records / %2$s', 'xerotact-shield-x-security'), (int) ($advisoryCache['records_total'] ?? 0), $this->formatLabel((string) ($advisoryCache['last_refresh_status'] ?? 'cached')))); ?>
            </dl>
            <form class="shieldx-scan-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" data-shieldx-scan-run-form>
                <input type="hidden" name="action" value="shieldx_run_vulnerability_scan">
                <?php wp_nonce_field('shieldx_run_vulnerability_scan'); ?>
                <p class="shieldx-muted">
                    <?php echo esc_html__('Checks installed core, plugins, themes, PHP, web server, database, and common runtime libraries for updates, abandoned components, risky version gaps, known vulnerability advisories, and virtual-patch recommendations.', 'xerotact-shield-x-security'); ?>
                </p>
                <div class="shieldx-settings-grid shieldx-settings-grid--vulnerability">
                    <label class="shieldx-field">
                        <span><?php echo esc_html__('Vulnerability feed', 'xerotact-shield-x-security'); ?></span>
                        <select name="vulnerability_feed_source">
                            <option value="wpvulnerability" <?php selected($feedSource, 'wpvulnerability'); ?>><?php echo esc_html__('WPVulnerability API', 'xerotact-shield-x-security'); ?></option>
                            <option value="local_cache" <?php selected($feedSource, 'local_cache'); ?>><?php echo esc_html__('Local cache only', 'xerotact-shield-x-security'); ?></option>
                            <option value="custom_json" <?php selected($feedSource, 'custom_json'); ?>><?php echo esc_html__('Custom SHIELD-X JSON', 'xerotact-shield-x-security'); ?></option>
                        </select>
                        <small><?php echo esc_html__('WPVulnerability is open and keyless. Custom feeds must use SHIELD-X normalized JSON.', 'xerotact-shield-x-security'); ?></small>
                    </label>
                    <label class="shieldx-field">
                        <span><?php echo esc_html__('Custom JSON feed URL', 'xerotact-shield-x-security'); ?></span>
                        <input type="url" name="vulnerability_custom_feed_url" value="<?php echo esc_attr($customFeedUrl); ?>" placeholder="https://example.com/shieldx-advisories.json">
                        <small><?php echo esc_html__('Use SHIELD-X normalized JSON with core, plugins, and themes buckets.', 'xerotact-shield-x-security'); ?></small>
                    </label>
                    <label class="shieldx-hardening-option shieldx-hardening-option--compact">
                        <input type="checkbox" name="refresh_vulnerability_feed" value="1">
                        <span>
                            <strong><?php echo esc_html__('Refresh feed now', 'xerotact-shield-x-security'); ?></strong>
                            <small><?php echo esc_html__('For live feeds, scans also auto-refresh stale cache older than 12 hours.', 'xerotact-shield-x-security'); ?></small>
                        </span>
                    </label>
                </div>
                <?php if (!empty($advisoryCache['last_error'])) : ?>
                    <p class="shieldx-muted shieldx-muted--warning"><?php echo esc_html((string) $advisoryCache['last_error']); ?></p>
                <?php elseif (!empty($advisoryCache['last_warning'])) : ?>
                    <p class="shieldx-muted shieldx-muted--warning"><?php echo esc_html((string) $advisoryCache['last_warning']); ?></p>
                <?php endif; ?>
                <div class="shieldx-form-actions">
                    <button type="submit" class="button button-primary" data-shieldx-scan-run-button data-running-label="<?php echo esc_attr__('Scanning...', 'xerotact-shield-x-security'); ?>"><?php echo esc_html__('Run vulnerability scan', 'xerotact-shield-x-security'); ?></button>
                    <?php
                    $priorityActions = function_exists('apply_filters')
                        ? apply_filters('shieldx_vulnerability_priority_actions', [], $issues, 'vulnerability_scanner')
                        : [];
                    foreach (is_array($priorityActions) ? $priorityActions : [] as $priorityAction) {
                        if (!is_array($priorityAction)) {
                            continue;
                        }
                        $label = isset($priorityAction['label']) && is_scalar($priorityAction['label']) ? (string) $priorityAction['label'] : '';
                        $url = isset($priorityAction['url']) && is_scalar($priorityAction['url']) ? (string) $priorityAction['url'] : '';
                        $class = isset($priorityAction['class']) && is_scalar($priorityAction['class']) ? (string) $priorityAction['class'] : 'button button-secondary';
                        if ($label === '' || $url === '') {
                            continue;
                        }
                        ?>
                        <a class="<?php echo esc_attr($class); ?>" href="<?php echo esc_url($url); ?>"><?php echo esc_html($label); ?></a>
                        <?php
                    }
                    ?>
                </div>
                <div class="shieldx-scan-progress" data-shieldx-scan-progress hidden aria-live="polite">
                    <span class="shieldx-scan-progress-track" role="progressbar" aria-label="<?php echo esc_attr__('Vulnerability scan progress', 'xerotact-shield-x-security'); ?>">
                        <span></span>
                    </span>
                    <small><?php echo esc_html__('Checking installed versions and vulnerability feeds. Keep this tab open until results load.', 'xerotact-shield-x-security'); ?></small>
                </div>
            </form>
            <?php if (!empty($software)) : ?>
                <section class="shieldx-subpanel shieldx-vulnerability-software">
                    <div class="shieldx-section-head shieldx-section-head--compact">
                        <h3><?php echo esc_html__('Detected Software', 'xerotact-shield-x-security'); ?></h3>
                        <span><?php echo esc_html__('WPVulnerability-style runtime coverage', 'xerotact-shield-x-security'); ?></span>
                    </div>
                    <table class="widefat striped shieldx-compat-table">
                        <thead>
                            <tr>
                                <th><?php echo esc_html__('Component', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Version', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Detection', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Detection confidence', 'xerotact-shield-x-security'); ?></th>
                                <th><?php echo esc_html__('Status', 'xerotact-shield-x-security'); ?></th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php foreach ($software as $component) : ?>
                                <?php
                                $detected = !empty($component['detected']) && (string) ($component['version'] ?? '') !== '';
                                $confidence = max(0, min(100, (int) ($component['reliability'] ?? 0)));
                                ?>
                                <tr>
                                    <td><strong><?php echo esc_html((string) ($component['name'] ?? $component['slug'] ?? '')); ?></strong><br><code><?php echo esc_html((string) ($component['slug'] ?? '')); ?></code></td>
                                    <td><?php echo esc_html($detected ? (string) ($component['version'] ?? '') : __('Not detected', 'xerotact-shield-x-security')); ?></td>
                                    <td><?php echo esc_html((string) ($component['method'] ?? '')); ?></td>
                                    <td>
                                        <?php
                                        echo esc_html($detected
                                            ? sprintf(
                                                /* translators: %d: software version detection confidence percentage. */
                                                __('%d%%', 'xerotact-shield-x-security'),
                                                $confidence
                                            )
                                            : __('N/A', 'xerotact-shield-x-security'));
                                        ?>
                                    </td>
                                    <td><?php $this->renderStatusBadge($detected ? 'ready' : 'warning'); ?></td>
                                </tr>
                            <?php endforeach; ?>
                        </tbody>
                    </table>
                </section>
            <?php endif; ?>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $schedule
     */
    /**
     * @param array<string,mixed> $summary
     */
    private function renderBackupStoragePanel(array $summary): void
    {
        $mode = (string) ($summary['mode'] ?? BackupStorageSettings::MODE_LOCAL);
        $google = isset($summary['google']) && is_array($summary['google']) ? $summary['google'] : [];
        $pending = isset($google['pending']) && is_array($google['pending']) ? $google['pending'] : [];
        $connected = !empty($google['connected']);
        $credentialsAvailable = !empty($google['credentials_available']);
        $localOAuthConfigured = !empty($google['local_oauth_configured']);
        $secretStorageReady = !empty($google['secret_storage_ready']);
        $googleSelectable = $secretStorageReady && $connected;
        $status = (string) ($google['status'] ?? GoogleDriveSettings::STATUS_DISCONNECTED);
        $lastError = (string) ($google['last_error'] ?? '');
        $accountEmail = (string) ($google['account_email'] ?? '');
        $pendingUserCode = (string) ($pending['user_code'] ?? '');
        $pendingRestartNeeded = $status === GoogleDriveSettings::STATUS_PENDING
            && $lastError !== ''
            && stripos($lastError, 'already exchanged') !== false;
        $pendingCanConfirm = $status === GoogleDriveSettings::STATUS_PENDING
            && $pendingUserCode !== ''
            && !$pendingRestartNeeded;
        $oauthMasks = $localOAuthConfigured && $secretStorageReady ? $this->googleOAuthCredentialMasks() : [
            'client_id' => '',
            'client_secret' => '',
        ];
        $statusLabel = __('Local storage', 'xerotact-shield-x-security');
        if ($mode !== BackupStorageSettings::MODE_LOCAL) {
            if (!$secretStorageReady) {
                $statusLabel = __('Token encryption unavailable', 'xerotact-shield-x-security');
            } elseif ($connected) {
                $statusLabel = __('Google Drive connected', 'xerotact-shield-x-security');
            } elseif ($status === GoogleDriveSettings::STATUS_PENDING) {
                $statusLabel = __('Google authorization pending', 'xerotact-shield-x-security');
            } else {
                $statusLabel = __('Google Drive disconnected', 'xerotact-shield-x-security');
            }
        }
        $googleCardExtra = '';
        if ($connected) {
            ob_start();
            ?>
            <div class="shieldx-storage-card-extra">
                <p class="shieldx-muted">
                    <?php
                    echo esc_html($accountEmail !== ''
                        /* translators: %s: connected Google account email. */
                        ? sprintf(__('Connected as %s.', 'xerotact-shield-x-security'), $accountEmail)
                        : __('Google Drive is connected.', 'xerotact-shield-x-security'));
                    ?>
                </p>
                <div class="shieldx-storage-card-actions">
                    <button type="submit" form="shieldx-google-drive-test-form" class="button button-small"><?php echo esc_html__('Test connection', 'xerotact-shield-x-security'); ?></button>
                    <button type="submit" form="shieldx-google-drive-disconnect-form" class="button button-small shieldx-button-warning" data-shieldx-confirm="<?php echo esc_attr__('Disconnect Google Drive from SHIELD-X backup storage?', 'xerotact-shield-x-security'); ?>"><?php echo esc_html__('Disconnect', 'xerotact-shield-x-security'); ?></button>
                </div>
            </div>
            <?php
            $googleCardExtra = (string) ob_get_clean();
        } elseif ($pendingCanConfirm) {
            ob_start();
            ?>
            <div class="shieldx-storage-card-extra">
                <p class="shieldx-muted"><?php echo esc_html__('Google authorization is waiting for approval.', 'xerotact-shield-x-security'); ?></p>
                <div class="shieldx-storage-card-actions">
                    <button type="button" class="button button-primary button-small" data-shieldx-form-modal-open aria-controls="shieldx-google-drive-auth-modal"><?php echo esc_html__('Open verification', 'xerotact-shield-x-security'); ?></button>
                </div>
            </div>
            <?php
            $googleCardExtra = (string) ob_get_clean();
        }
        ?>
        <section class="shieldx-panel shieldx-panel--full shieldx-backup-storage" id="shieldx-backup-storage">
            <div class="shieldx-section-head">
                <div>
                    <div class="shieldx-storage-section-title">
                        <h2><?php echo esc_html__('Backup Storage', 'xerotact-shield-x-security'); ?></h2>
                        <span class="shieldx-storage-status"><?php echo esc_html($statusLabel); ?></span>
                    </div>
                    <span><?php echo esc_html__('Choose where database backups and restore points are kept', 'xerotact-shield-x-security'); ?></span>
                </div>
            </div>

            <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="shieldx-storage-settings-form">
                <input type="hidden" name="action" value="shieldx_save_backup_storage">
                <?php wp_nonce_field('shieldx_save_backup_storage'); ?>
                <div class="shieldx-storage-mode-grid">
                    <?php
                    $googleHowToButton = '<button type="button" class="button button-small shieldx-storage-title-action" data-shieldx-form-modal-open aria-controls="shieldx-google-drive-howto-modal">' . esc_html__('How to', 'xerotact-shield-x-security') . '</button>';
                    $googleEditButton = $localOAuthConfigured && $secretStorageReady
                        ? '<button type="button" class="button button-small shieldx-storage-title-action" data-shieldx-form-modal-open aria-controls="shieldx-google-drive-oauth-modal">' . esc_html__('Edit OAuth app', 'xerotact-shield-x-security') . '</button>'
                        : '';
                    $googleTitleActions = $googleHowToButton . $googleEditButton;
                    $this->renderStorageModeCard(BackupStorageSettings::MODE_LOCAL, __('Local', 'xerotact-shield-x-security'), __('Keep backups only in SHIELD-X private local storage.', 'xerotact-shield-x-security'), $mode);
                    $this->renderStorageModeCard(BackupStorageSettings::MODE_GOOGLE, __('Google Drive', 'xerotact-shield-x-security'), __('Upload a Google copy and remove the local artifact only after upload is verified.', 'xerotact-shield-x-security'), $mode, !$googleSelectable, $googleCardExtra, $googleTitleActions);
                    $this->renderStorageModeCard(BackupStorageSettings::MODE_BOTH, __('Both', 'xerotact-shield-x-security'), __('Keep local backups and upload a second copy to Google Drive.', 'xerotact-shield-x-security'), $mode, !$googleSelectable);
                    ?>
                </div>
                <div class="shieldx-form-actions shieldx-storage-actions">
                    <button type="submit" class="button button-primary"><?php echo esc_html__('Save storage mode', 'xerotact-shield-x-security'); ?></button>
                </div>
            </form>

            <?php if ($connected) : ?>
                <form id="shieldx-google-drive-test-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                    <input type="hidden" name="action" value="shieldx_google_drive_test">
                    <?php wp_nonce_field('shieldx_google_drive_test'); ?>
                </form>
                <form id="shieldx-google-drive-disconnect-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                    <input type="hidden" name="action" value="shieldx_google_drive_disconnect">
                    <?php wp_nonce_field('shieldx_google_drive_disconnect'); ?>
                </form>
            <?php endif; ?>

            <?php $this->renderGoogleOAuthGuideModal(); ?>
            <?php if ($pendingCanConfirm) : ?>
                <?php $this->renderGoogleDriveAuthorizationModal($pending, $pendingUserCode); ?>
            <?php endif; ?>
            <?php if (!$connected && $localOAuthConfigured && $secretStorageReady) : ?>
                <?php $this->renderGoogleOAuthEditModal($oauthMasks['client_id'], $oauthMasks['client_secret']); ?>
            <?php endif; ?>

            <?php if (!$connected && !$pendingCanConfirm) : ?>
            <div class="shieldx-storage-google-card">
                <div>
                    <div class="shieldx-google-drive-heading">
                        <h3><?php echo esc_html__('Google Drive', 'xerotact-shield-x-security'); ?></h3>
                    </div>
                    <?php if (!$secretStorageReady) : ?>
                        <p class="shieldx-muted"><?php echo esc_html__('OpenSSL and WordPress salts are required before SHIELD-X can store Google tokens locally.', 'xerotact-shield-x-security'); ?></p>
                    <?php elseif ($pendingRestartNeeded) : ?>
                        <p class="shieldx-storage-error"><?php echo esc_html__('The current Google authorization code cannot be used anymore. Start a new Google authorization to get a fresh code.', 'xerotact-shield-x-security'); ?></p>
                    <?php else : ?>
                        <p class="shieldx-muted"><?php echo esc_html($credentialsAvailable ? __('Authenticate with Google to let this WordPress site upload backup files directly to your Drive.', 'xerotact-shield-x-security') : __('Enter the Google OAuth app credentials for this site, then authenticate with Google.', 'xerotact-shield-x-security')); ?></p>
                    <?php endif; ?>
                    <?php if ($lastError !== '' && $credentialsAvailable && !$pendingRestartNeeded && !$connected) : ?>
                        <p class="shieldx-storage-error"><?php echo esc_html($lastError); ?></p>
                    <?php endif; ?>
                    <?php if ($secretStorageReady && !$credentialsAvailable) : ?>
                        <?php $this->renderGoogleOAuthForm(false); ?>
                    <?php endif; ?>
                </div>
                <div class="shieldx-storage-google-actions">
                    <?php if ($secretStorageReady && $credentialsAvailable && !$connected && !$pendingCanConfirm) : ?>
                        <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                            <input type="hidden" name="action" value="shieldx_google_drive_auth_start">
                            <?php wp_nonce_field('shieldx_google_drive_auth_start'); ?>
                            <button type="submit" class="button button-primary"><?php echo esc_html($pendingRestartNeeded ? __('Start new Google authorization', 'xerotact-shield-x-security') : __('Authenticate with Google', 'xerotact-shield-x-security')); ?></button>
                        </form>
                    <?php endif; ?>
                </div>
            </div>
            <?php endif; ?>
        </section>
        <?php
    }

    /**
     * @return array{client_id:string,client_secret:string}
     */
    private function googleOAuthCredentialMasks(): array
    {
        $settings = GoogleDriveSettings::fromWordPress();

        return [
            'client_id' => str_repeat('*', strlen($settings->localClientId())),
            'client_secret' => str_repeat('*', strlen($settings->localClientSecret())),
        ];
    }

    private function renderGoogleOAuthEditModal(string $clientIdMask, string $clientSecretMask): void
    {
        $modalId = 'shieldx-google-drive-oauth-modal';
        $titleId = $modalId . '-title';
        $descId = $modalId . '-desc';
        ?>
        <div id="<?php echo esc_attr($modalId); ?>" class="shieldx-form-modal shieldx-google-oauth-modal" data-shieldx-form-modal hidden>
            <div class="shieldx-form-modal-backdrop" data-shieldx-form-modal-close></div>
            <section class="shieldx-form-modal-card" role="dialog" aria-modal="true" aria-labelledby="<?php echo esc_attr($titleId); ?>" aria-describedby="<?php echo esc_attr($descId); ?>">
                <button type="button" class="shieldx-modal-close" data-shieldx-form-modal-close aria-label="<?php echo esc_attr__('Close dialog', 'xerotact-shield-x-security'); ?>">
                    <span aria-hidden="true">&times;</span>
                </button>
                <p class="shieldx-modal-kicker"><?php echo esc_html__('Google Drive', 'xerotact-shield-x-security'); ?></p>
                <h2 id="<?php echo esc_attr($titleId); ?>"><?php echo esc_html__('Edit Google OAuth App', 'xerotact-shield-x-security'); ?></h2>
                <p id="<?php echo esc_attr($descId); ?>" class="shieldx-muted"><?php echo esc_html__('Enter a new client ID and client secret to replace the locally saved Google OAuth app. The existing secret is never displayed.', 'xerotact-shield-x-security'); ?></p>
                <?php $this->renderGoogleOAuthForm(true, $clientIdMask, $clientSecretMask); ?>
            </section>
        </div>
        <?php
    }

    private function renderGoogleOAuthGuideModal(): void
    {
        $modalId = 'shieldx-google-drive-howto-modal';
        $titleId = $modalId . '-title';
        $descId = $modalId . '-desc';
        ?>
        <div id="<?php echo esc_attr($modalId); ?>" class="shieldx-form-modal shieldx-google-howto-modal" data-shieldx-form-modal hidden>
            <div class="shieldx-form-modal-backdrop" data-shieldx-form-modal-close></div>
            <section class="shieldx-form-modal-card" role="dialog" aria-modal="true" aria-labelledby="<?php echo esc_attr($titleId); ?>" aria-describedby="<?php echo esc_attr($descId); ?>">
                <button type="button" class="shieldx-modal-close" data-shieldx-form-modal-close aria-label="<?php echo esc_attr__('Close dialog', 'xerotact-shield-x-security'); ?>">
                    <span aria-hidden="true">&times;</span>
                </button>
                <p class="shieldx-modal-kicker"><?php echo esc_html__('Google Drive', 'xerotact-shield-x-security'); ?></p>
                <h2 id="<?php echo esc_attr($titleId); ?>"><?php echo esc_html__('Google OAuth Setup', 'xerotact-shield-x-security'); ?></h2>
                <p id="<?php echo esc_attr($descId); ?>" class="shieldx-muted"><?php echo esc_html__('Create the correct Google OAuth client, then paste its client ID and secret into SHIELD-X.', 'xerotact-shield-x-security'); ?></p>
                <?php $this->renderGoogleOAuthGuide(); ?>
                <p class="shieldx-muted"><?php echo esc_html__('SHIELD-X stores these values encrypted locally and does not export or log them.', 'xerotact-shield-x-security'); ?></p>
            </section>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $pending
     */
    private function renderGoogleDriveAuthorizationModal(array $pending, string $pendingUserCode): void
    {
        $modalId = 'shieldx-google-drive-auth-modal';
        $titleId = $modalId . '-title';
        $descId = $modalId . '-desc';
        $verificationUrl = (string) ($pending['verification_url'] ?? '');
        ?>
        <div id="<?php echo esc_attr($modalId); ?>" class="shieldx-form-modal shieldx-google-auth-modal" data-shieldx-form-modal hidden>
            <div class="shieldx-form-modal-backdrop" data-shieldx-form-modal-close></div>
            <section class="shieldx-form-modal-card" role="dialog" aria-modal="true" aria-labelledby="<?php echo esc_attr($titleId); ?>" aria-describedby="<?php echo esc_attr($descId); ?>">
                <button type="button" class="shieldx-modal-close" data-shieldx-form-modal-close aria-label="<?php echo esc_attr__('Close dialog', 'xerotact-shield-x-security'); ?>">
                    <span aria-hidden="true">&times;</span>
                </button>
                <p class="shieldx-modal-kicker"><?php echo esc_html__('Google Drive', 'xerotact-shield-x-security'); ?></p>
                <h2 id="<?php echo esc_attr($titleId); ?>"><?php echo esc_html__('Finish Google Verification', 'xerotact-shield-x-security'); ?></h2>
                <p id="<?php echo esc_attr($descId); ?>" class="shieldx-muted"><?php echo esc_html__('Open Google, enter this code, approve access, then return here and confirm.', 'xerotact-shield-x-security'); ?></p>
                <div class="shieldx-google-device-code shieldx-google-device-code--modal">
                    <?php if ($verificationUrl !== '') : ?>
                        <a class="button button-primary" href="<?php echo esc_url($verificationUrl); ?>" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('Open Google verification', 'xerotact-shield-x-security'); ?></a>
                    <?php endif; ?>
                    <div class="shieldx-google-device-code-value">
                        <span><?php echo esc_html__('Verification code', 'xerotact-shield-x-security'); ?></span>
                        <code><?php echo esc_html($pendingUserCode); ?></code>
                    </div>
                    <button type="button" class="button" data-shieldx-copy="<?php echo esc_attr($pendingUserCode); ?>"><?php echo esc_html__('Copy code', 'xerotact-shield-x-security'); ?></button>
                </div>
                <form class="shieldx-modal-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                    <input type="hidden" name="action" value="shieldx_google_drive_auth_complete">
                    <?php wp_nonce_field('shieldx_google_drive_auth_complete'); ?>
                    <div class="shieldx-modal-actions">
                        <button type="button" class="button" data-shieldx-form-modal-close><?php echo esc_html__('Cancel', 'xerotact-shield-x-security'); ?></button>
                        <button type="submit" class="button button-primary"><?php echo esc_html__('I approved access', 'xerotact-shield-x-security'); ?></button>
                    </div>
                </form>
            </section>
        </div>
        <?php
    }

    private function renderGoogleOAuthForm(bool $replace, string $clientIdMask = '', string $clientSecretMask = ''): void
    {
        $clientIdPlaceholder = $replace ? $clientIdMask : '';
        $clientSecretPlaceholder = $replace ? $clientSecretMask : '';
        ?>
        <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="shieldx-google-oauth-form" autocomplete="off">
            <input type="hidden" name="action" value="shieldx_google_drive_oauth_save">
            <?php wp_nonce_field('shieldx_google_drive_oauth_save'); ?>
            <label class="shieldx-field">
                <span><?php echo esc_html__('Google OAuth client ID', 'xerotact-shield-x-security'); ?></span>
                <input type="text" name="google_drive_client_id" value="" placeholder="<?php echo esc_attr($clientIdPlaceholder); ?>" autocomplete="off" spellcheck="false">
            </label>
            <label class="shieldx-field">
                <span><?php echo esc_html__('Google OAuth client secret', 'xerotact-shield-x-security'); ?></span>
                <input type="password" name="google_drive_client_secret" value="" placeholder="<?php echo esc_attr($clientSecretPlaceholder); ?>" autocomplete="new-password" spellcheck="false">
            </label>
            <p class="shieldx-muted"><?php echo esc_html__('SHIELD-X stores these values encrypted locally and does not export or log them.', 'xerotact-shield-x-security'); ?></p>
            <button type="submit" class="button button-primary"><?php echo esc_html($replace ? __('Replace Google OAuth app', 'xerotact-shield-x-security') : __('Save Google OAuth app', 'xerotact-shield-x-security')); ?></button>
        </form>
        <?php
    }

    private function renderGoogleOAuthGuide(): void
    {
        $allowedLinks = [
            'a' => [
                'href' => [],
                'target' => [],
                'rel' => [],
            ],
        ];
        $googleLink = static function (string $url, string $label): string {
            return '<a href="' . esc_url($url) . '" target="_blank" rel="noopener noreferrer">' . esc_html($label) . '</a>';
        };
        $projectLink = $googleLink('https://console.cloud.google.com/projectcreate', __('Create a Google Cloud project', 'xerotact-shield-x-security'));
        $driveApiLink = $googleLink('https://console.cloud.google.com/apis/library/drive.googleapis.com', __('Google Drive API', 'xerotact-shield-x-security'));
        $credentialsLink = $googleLink('https://console.cloud.google.com/apis/credentials', __('Credentials page', 'xerotact-shield-x-security'));
        $createCredentialsLink = $googleLink('https://console.cloud.google.com/apis/credentials', __('Create Credentials', 'xerotact-shield-x-security'));
        $oauthClientLink = $googleLink('https://console.cloud.google.com/apis/credentials/oauthclient', __('OAuth client ID', 'xerotact-shield-x-security'));
        $limitedInputLink = $googleLink('https://developers.google.com/identity/protocols/oauth2/limited-input-device', __('TVs and Limited Input devices', 'xerotact-shield-x-security'));
        ?>
        <div class="shieldx-google-oauth-guide">
            <strong><?php echo esc_html__('How to create the Google OAuth client', 'xerotact-shield-x-security'); ?></strong>
            <ol>
                <li>
                    <?php
                    echo wp_kses(sprintf(
                        /* translators: %s: Link to Google Cloud project creation. */
                        __('%s or select an existing Google Cloud project.', 'xerotact-shield-x-security'),
                        $projectLink
                    ), $allowedLinks);
                    ?>
                </li>
                <li>
                    <?php
                    echo wp_kses(sprintf(
                        /* translators: %s: Link to Google Drive API enablement page. */
                        __('Enable the %s for that project.', 'xerotact-shield-x-security'),
                        $driveApiLink
                    ), $allowedLinks);
                    ?>
                </li>
                <li>
                    <?php
                    echo wp_kses(sprintf(
                        /* translators: %s: Link to Google Cloud Console Credentials page. */
                        __('Open the Google Cloud Console %s.', 'xerotact-shield-x-security'),
                        $credentialsLink
                    ), $allowedLinks);
                    ?>
                </li>
                <li>
                    <?php
                    echo wp_kses(sprintf(
                        /* translators: 1: Link to Google Cloud credentials page, 2: Link to OAuth client page. */
                        __('Click %1$s, then choose %2$s.', 'xerotact-shield-x-security'),
                        $createCredentialsLink,
                        $oauthClientLink
                    ), $allowedLinks);
                    ?>
                </li>
                <li>
                    <?php
                    echo wp_kses(sprintf(
                        /* translators: %s: Link to Google OAuth limited-input device documentation. */
                        __('Under Application type, choose %s.', 'xerotact-shield-x-security'),
                        $limitedInputLink
                    ), $allowedLinks);
                    ?>
                </li>
                <li><?php echo esc_html__('Name the OAuth 2.0 client and click Create.', 'xerotact-shield-x-security'); ?></li>
                <li><?php echo esc_html__('Copy the generated Client ID and Client Secret.', 'xerotact-shield-x-security'); ?></li>
                <li><?php echo esc_html__('Paste those credentials into the SHIELD-X OAuth app fields.', 'xerotact-shield-x-security'); ?></li>
            </ol>
        </div>
        <?php
    }

    private function renderStorageModeCard(string $value, string $label, string $description, string $selected, bool $disabled = false, string $extraHtml = '', string $titleActionHtml = ''): void
    {
        $id = 'shieldx-storage-mode-' . $value;
        ?>
        <div class="shieldx-storage-mode-card<?php echo esc_attr($disabled ? ' is-disabled' : ''); ?>">
            <input id="<?php echo esc_attr($id); ?>" type="radio" name="backup_storage_mode" value="<?php echo esc_attr($value); ?>" <?php checked($selected, $value); ?> <?php disabled($disabled); ?>>
            <div class="shieldx-storage-mode-card-body">
                <div class="shieldx-storage-mode-card-title">
                    <label for="<?php echo esc_attr($id); ?>"><strong><?php echo esc_html($label); ?></strong></label>
                    <?php echo wp_kses($titleActionHtml, $this->allowedStorageCardMarkup()); ?>
                </div>
                <label class="shieldx-storage-mode-card-desc" for="<?php echo esc_attr($id); ?>"><small><?php echo esc_html($description); ?></small></label>
            </div>
            <?php echo wp_kses($extraHtml, $this->allowedStorageCardMarkup()); ?>
        </div>
        <?php
    }

    /**
     * @return array<string,array<string,bool>>
     */
    private function allowedStorageCardMarkup(): array
    {
        return [
            'div' => [
                'class' => true,
            ],
            'p' => [
                'class' => true,
            ],
            'button' => [
                'aria-controls' => true,
                'class' => true,
                'data-shieldx-confirm' => true,
                'data-shieldx-form-modal-open' => true,
                'form' => true,
                'type' => true,
            ],
        ];
    }

    private function renderStorageBadges(array $item): void
    {
        $provider = (string) ($item['storage_provider'] ?? 'local');
        $remoteStatus = (string) ($item['remote_status'] ?? '');
        $localExists = array_key_exists('local_exists', $item) ? !empty($item['local_exists']) : true;
        $badges = [];

        if ($provider === 'both') {
            $badges[] = __('Local', 'xerotact-shield-x-security');
            $badges[] = __('Google Drive', 'xerotact-shield-x-security');
        } elseif ($provider === 'google') {
            $badges[] = __('Google Drive', 'xerotact-shield-x-security');
        } else {
            $badges[] = __('Local', 'xerotact-shield-x-security');
        }

        if ($remoteStatus === 'failed') {
            $badges[] = __('Google upload failed', 'xerotact-shield-x-security');
        } elseif ($provider === 'google' && !$localExists) {
            $badges[] = __('downloads before restore', 'xerotact-shield-x-security');
        }

        if (empty($badges)) {
            return;
        }
        ?>
        <span class="shieldx-storage-badges">
            <?php foreach ($badges as $badge) : ?>
                <span class="shieldx-storage-badge"><?php echo esc_html($badge); ?></span>
            <?php endforeach; ?>
        </span>
        <?php
    }

    /**
     * @param array<string,mixed>      $state
     * @param array<string,mixed>|null $compare
     */
    private function renderCoreVerifierPanel(array $state, ?array $compare, bool $showHelpText = true): void
    {
        $prepared = !empty($state['prepared']);
        $version = (string) ($state['version'] ?? '');
        $archiveUrl = (string) ($state['archive_url'] ?? '');
        $hashCount = (int) ($state['hash_count'] ?? 0);
        ?>
        <div class="shieldx-core-tools">
            <dl class="shieldx-detail-list">
                <?php $this->renderDetail(__('Installed version', 'xerotact-shield-x-security'), $version !== '' ? $version : __('Unknown', 'xerotact-shield-x-security')); ?>
                <?php $this->renderDetail(__('Clean package', 'xerotact-shield-x-security'), $prepared ? __('Ready', 'xerotact-shield-x-security') : __('Not downloaded yet', 'xerotact-shield-x-security')); ?>
                <?php
                /* translators: %d: number of official WordPress core file hashes. */
                $this->renderDetail(__('Official hashes', 'xerotact-shield-x-security'), $hashCount > 0 ? sprintf(_n('%d file hash', '%d file hashes', $hashCount, 'xerotact-shield-x-security'), $hashCount) : __('Not built yet', 'xerotact-shield-x-security'));
                ?>
                <?php $this->renderDetail(__('Release source', 'xerotact-shield-x-security'), $archiveUrl); ?>
            </dl>
            <form
                method="post"
                action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
                data-shieldx-core-progress-form
                data-shieldx-progress-poll-delay="250"
                data-shieldx-progress-refresh-selector="#shieldx-core-compare"
                data-shieldx-progress-refresh-url="<?php echo esc_url(admin_url('admin.php?page=shield-x-scan')); ?>"
                data-shieldx-progress-running-title="<?php echo esc_attr__('Official core download in progress', 'xerotact-shield-x-security'); ?>"
                data-shieldx-progress-running-intro="<?php echo esc_attr__('SHIELD-X is downloading, verifying, extracting, and hashing the official WordPress core package in safe stages.', 'xerotact-shield-x-security'); ?>"
                data-shieldx-progress-complete-title="<?php echo esc_attr__('Official core ready', 'xerotact-shield-x-security'); ?>"
                data-shieldx-progress-complete-intro="<?php echo esc_attr__('Official WordPress core hashes are ready. Core files that match the clean package are trusted by hash.', 'xerotact-shield-x-security'); ?>"
            >
                <input type="hidden" name="action" value="shieldx_prepare_core_release">
                <?php wp_nonce_field('shieldx_prepare_core_release'); ?>
                <button type="submit" class="button button-secondary" data-shieldx-core-progress-button data-running-label="<?php echo esc_attr__('Downloading...', 'xerotact-shield-x-security'); ?>">
                    <?php echo esc_html($prepared ? __('Refresh official core', 'xerotact-shield-x-security') : __('Download official core', 'xerotact-shield-x-security')); ?>
                </button>
            </form>
        </div>
        <?php if ($compare !== null) : ?>
            <?php $this->renderCoreCompare($compare); ?>
        <?php elseif ($showHelpText) : ?>
            <p class="shieldx-empty"><?php echo esc_html__('Use Compare core beside a scanner issue to check a local core file against the matching official WordPress release.', 'xerotact-shield-x-security'); ?></p>
        <?php endif; ?>
        <?php
    }

    /**
     * @param array<string,mixed> $state
     */
    private function renderPluginVerifierPanel(array $state): void
    {
        $installed = (int) ($state['installed_count'] ?? 0);
        $prepared = (int) ($state['prepared_count'] ?? 0);
        $hashCount = (int) ($state['hash_count'] ?? 0);
        $lastResult = isset($state['last_result']) && is_array($state['last_result']) ? $state['last_result'] : [];
        $lastErrors = isset($lastResult['errors']) && is_array($lastResult['errors']) ? count($lastResult['errors']) : 0;
        $lastFailureDetails = $this->pluginPackageFailureDetailsForState($state);
        $lastPreparedAt = (string) ($lastResult['prepared_at'] ?? '');
        /* translators: %d: number of installed WordPress plugins eligible for official package comparison. */
        $installedLabel = sprintf(_n('%d plugin', '%d plugins', $installed, 'xerotact-shield-x-security'), $installed);
        /* translators: 1: current count, 2: total count. */
        $readyLabel = sprintf(__('%1$d / %2$d', 'xerotact-shield-x-security'), $prepared, $installed);
        /* translators: %d: number of official WordPress.org plugin file hashes. */
        $hashLabel = $hashCount > 0 ? sprintf(_n('%d hash', '%d hashes', $hashCount, 'xerotact-shield-x-security'), $hashCount) : __('Not built', 'xerotact-shield-x-security');
        if ($lastErrors > 0) {
            /* translators: %d: number of WordPress.org plugin package refresh errors. */
            $refreshLabel = sprintf(_n('%d failed', '%d failed', $lastErrors, 'xerotact-shield-x-security'), $lastErrors);
        } else {
            $refreshLabel = $lastPreparedAt !== '' ? $this->formatTime($lastPreparedAt) : __('Not run', 'xerotact-shield-x-security');
        }
        ?>
        <div class="shieldx-core-tools shieldx-core-tools--plugin">
            <dl class="shieldx-detail-list shieldx-plugin-compare-stats">
                <?php $this->renderDetail(__('Installed', 'xerotact-shield-x-security'), $installedLabel); ?>
                <?php $this->renderDetail(__('Ready', 'xerotact-shield-x-security'), $readyLabel); ?>
                <?php $this->renderDetail(__('Hashes', 'xerotact-shield-x-security'), $hashLabel); ?>
                <?php $this->renderDetail($lastErrors > 0 ? __('Errors', 'xerotact-shield-x-security') : __('Refresh', 'xerotact-shield-x-security'), $refreshLabel); ?>
            </dl>
            <form
                method="post"
                action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
                data-shieldx-plugin-packages-progress-form
                data-shieldx-progress-poll-delay="250"
                data-shieldx-progress-refresh-selector="#shieldx-plugin-compare"
                data-shieldx-progress-refresh-url="<?php echo esc_url(admin_url('admin.php?page=shield-x-scan')); ?>"
                data-shieldx-progress-running-title="<?php echo esc_attr__('Official plugin download in progress', 'xerotact-shield-x-security'); ?>"
                data-shieldx-progress-running-intro="<?php echo esc_attr__('SHIELD-X is downloading and hashing WordPress.org plugin packages one plugin at a time to stay below server timeout limits.', 'xerotact-shield-x-security'); ?>"
                data-shieldx-progress-complete-title="<?php echo esc_attr__('Official plugins ready', 'xerotact-shield-x-security'); ?>"
                data-shieldx-progress-complete-intro="<?php echo esc_attr__('Official plugin package hashes are refreshed. Plugin files that match WordPress.org packages are trusted by hash.', 'xerotact-shield-x-security'); ?>"
            >
                <input type="hidden" name="action" value="shieldx_prepare_plugin_packages">
                <?php wp_nonce_field('shieldx_prepare_plugin_packages'); ?>
                <button type="submit" class="button button-secondary" data-shieldx-plugin-packages-progress-button data-running-label="<?php echo esc_attr__('Downloading...', 'xerotact-shield-x-security'); ?>">
                    <?php echo esc_html($prepared > 0 ? __('Refresh official plugins', 'xerotact-shield-x-security') : __('Download official plugins', 'xerotact-shield-x-security')); ?>
                </button>
            </form>
            <?php if (!empty($lastFailureDetails)) : ?>
                <div class="shieldx-package-failures">
                    <strong><?php echo esc_html__('Failed downloads', 'xerotact-shield-x-security'); ?></strong>
                    <ul>
                        <?php foreach ($lastFailureDetails as $failure) : ?>
                            <?php
                            $failedSlug = sanitize_key((string) ($failure['slug'] ?? ''));
                            $failedPlugin = (string) ($failure['plugin'] ?? __('Unknown plugin', 'xerotact-shield-x-security'));
                            ?>
                            <li>
                                <div class="shieldx-package-failure-head">
                                    <span><?php echo esc_html($failedPlugin); ?></span>
                                    <?php if ($failedSlug !== '') : ?>
                                        <form class="shieldx-package-upload-form" method="post" enctype="multipart/form-data" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                                            <input type="hidden" name="action" value="shieldx_upload_plugin_package">
                                            <input type="hidden" name="plugin_slug" value="<?php echo esc_attr($failedSlug); ?>">
                                            <?php wp_nonce_field('shieldx_upload_plugin_package'); ?>
                                            <label class="button button-small shieldx-package-upload-button">
                                                <?php echo esc_html__('Upload ZIP', 'xerotact-shield-x-security'); ?>
                                                <input class="shieldx-screen-reader-text" type="file" name="plugin_package" accept=".zip,application/zip,application/x-zip-compressed" data-shieldx-plugin-package-upload>
                                            </label>
                                        </form>
                                        <form class="shieldx-package-trust-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                                            <input type="hidden" name="action" value="shieldx_trust_installed_plugin">
                                            <input type="hidden" name="plugin_slug" value="<?php echo esc_attr($failedSlug); ?>">
                                            <?php wp_nonce_field('shieldx_trust_installed_plugin'); ?>
                                            <button
                                                type="submit"
                                                class="button button-small shieldx-package-trust-button"
                                                data-shieldx-confirm="<?php echo esc_attr__('Trust the currently installed plugin files as the clean baseline? Only do this after you have verified this installed plugin is clean.', 'xerotact-shield-x-security'); ?>"
                                            >
                                                <?php echo esc_html__('Trust installed version', 'xerotact-shield-x-security'); ?>
                                            </button>
                                        </form>
                                    <?php endif; ?>
                                </div>
                                <small><?php echo esc_html((string) ($failure['reason'] ?? __('No failure reason was returned.', 'xerotact-shield-x-security'))); ?></small>
                            </li>
                        <?php endforeach; ?>
                    </ul>
                </div>
            <?php endif; ?>
        </div>
        <p class="shieldx-empty shieldx-plugin-compare-note"><?php echo esc_html__('Unmodified WordPress.org plugin files are trusted by hash.', 'xerotact-shield-x-security'); ?></p>
        <?php
    }

    /**
     * @param array<string,mixed> $state
     * @return array<int,array{slug:string,plugin:string,version:string,reason:string}>
     */
    private function pluginPackageFailureDetailsForState(array $state): array
    {
        $lastResult = isset($state['last_result']) && is_array($state['last_result']) ? $state['last_result'] : [];
        $errors = isset($lastResult['errors']) && is_array($lastResult['errors']) ? $lastResult['errors'] : [];
        if (empty($errors)) {
            return [];
        }

        $installedBySlug = [];
        foreach ((array) ($state['installed'] ?? []) as $plugin) {
            if (!is_array($plugin)) {
                continue;
            }

            $slug = sanitize_key((string) ($plugin['slug'] ?? ''));
            if ($slug === '') {
                continue;
            }

            $name = (string) ($plugin['name'] ?? $slug);
            $version = (string) ($plugin['version'] ?? '');
            $installedBySlug[$slug] = [
                'label' => $version !== '' ? $name . ' ' . $version : $name,
                'version' => $version,
            ];
        }

        $details = [];
        foreach ($errors as $slug => $reason) {
            $slug = sanitize_key((string) $slug);
            $plugin = $slug !== '' && isset($installedBySlug[$slug]) && is_array($installedBySlug[$slug])
                ? $installedBySlug[$slug]
                : [];
            $label = !empty($plugin) ? (string) ($plugin['label'] ?? '') : ($slug !== '' ? $slug : __('Unknown plugin', 'xerotact-shield-x-security'));
            $details[] = [
                'slug' => $slug,
                'plugin' => $this->shortDisplayText((string) $label, 120),
                'version' => (string) ($plugin['version'] ?? ''),
                'reason' => $this->shortDisplayText((string) $reason, 260),
            ];
        }

        return $details;
    }

    private function shortDisplayText(string $value, int $limit): string
    {
        $value = trim(preg_replace('/\s+/', ' ', $value) ?? $value);
        if ($value === '' || strlen($value) <= $limit) {
            return $value;
        }

        return rtrim(substr($value, 0, max(0, $limit - 3))) . '...';
    }

    /**
     * @param array<string,mixed> $compare
     */
    private function renderCoreCompare(array $compare): void
    {
        $error = (string) ($compare['error'] ?? '');
        $path = (string) ($compare['path'] ?? '');

        if ($error !== '') {
            ?>
            <div class="shieldx-core-compare shieldx-core-compare--error">
                <h3><?php echo esc_html($path); ?></h3>
                <p><?php echo esc_html($error); ?></p>
            </div>
            <?php
            return;
        }

        $eligible = !empty($compare['eligible']);
        $localExists = !empty($compare['local_exists']);
        $cleanExists = !empty($compare['clean_exists']);
        $matches = !empty($compare['matches']);
        $canRestore = $eligible && $cleanExists;
        ?>
        <div class="shieldx-core-compare">
            <div class="shieldx-core-compare-head">
                <div>
                    <h3><?php echo esc_html($path); ?></h3>
                    <p>
                        <?php
                        if (!$eligible) {
                            echo esc_html__('This file is not eligible for official core replacement.', 'xerotact-shield-x-security');
                        } elseif (!$cleanExists) {
                            echo esc_html__('This file was not found in the official WordPress release package.', 'xerotact-shield-x-security');
                        } elseif ($matches) {
                            echo esc_html__('Installed file matches the official WordPress copy.', 'xerotact-shield-x-security');
                        } else {
                            echo esc_html__('Installed file differs from the official WordPress copy.', 'xerotact-shield-x-security');
                        }
                        ?>
                    </p>
                </div>
                <div class="shieldx-core-compare-actions">
                    <a class="button button-secondary" href="<?php echo esc_url($this->closeCoreCompareUrl()); ?>">
                        <?php echo esc_html__('Close compare', 'xerotact-shield-x-security'); ?>
                    </a>
                    <?php if ($canRestore) : ?>
                        <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                            <input type="hidden" name="action" value="shieldx_restore_core_file">
                            <input type="hidden" name="shieldx_file_route_token" value="<?php echo esc_attr($this->fileRouteToken($path)); ?>">
                            <?php wp_nonce_field('shieldx_restore_core_file'); ?>
                            <button type="submit" class="button button-primary"><?php echo esc_html__('Replace with official copy', 'xerotact-shield-x-security'); ?></button>
                        </form>
                    <?php endif; ?>
                </div>
            </div>
            <div class="shieldx-core-hashes">
                <span><strong><?php echo esc_html__('Installed', 'xerotact-shield-x-security'); ?></strong> <code><?php echo esc_html(substr((string) ($compare['local_hash'] ?? ''), 0, 16)); ?></code> <?php echo esc_html((string) ($compare['local_size'] ?? 0) . ' bytes'); ?></span>
                <span><strong><?php echo esc_html__('Official', 'xerotact-shield-x-security'); ?></strong> <code><?php echo esc_html(substr((string) ($compare['clean_hash'] ?? ''), 0, 16)); ?></code> <?php echo esc_html((string) ($compare['clean_size'] ?? 0) . ' bytes'); ?></span>
                <?php if (!empty($compare['first_changed_line'])) : ?>
                    <?php
                    $firstChangedLineLabel = sprintf(
                        /* translators: %d: line number. */
                        __('Line %d', 'xerotact-shield-x-security'),
                        (int) $compare['first_changed_line']
                    );
                    ?>
                    <span><strong><?php echo esc_html__('First change', 'xerotact-shield-x-security'); ?></strong> <?php echo esc_html($firstChangedLineLabel); ?></span>
                <?php endif; ?>
            </div>
            <?php if (!empty($compare['resolved_scanner_issues'])) : ?>
                <p class="shieldx-muted">
                    <?php
                    $resolvedScannerCount = (int) $compare['resolved_scanner_issues'];
                    $resolvedScannerMessage = $resolvedScannerCount === 1
                        ? __('Official core match verified. One active scanner finding was resolved for this file.', 'xerotact-shield-x-security')
                        : __('Official core match verified. Active scanner findings resolved for this file:', 'xerotact-shield-x-security') . ' ' . number_format_i18n($resolvedScannerCount) . '.';
                    echo esc_html($resolvedScannerMessage);
                    ?>
                </p>
            <?php endif; ?>
            <?php if (!empty($compare['text_comparable'])) : ?>
                <div class="shieldx-diff-grid">
                    <div>
                        <h4><?php echo esc_html__('Installed file', 'xerotact-shield-x-security'); ?></h4>
                        <pre><?php echo esc_html($this->formatExcerpt((array) ($compare['local_excerpt'] ?? []))); ?></pre>
                    </div>
                    <div>
                        <h4><?php echo esc_html__('Official WordPress file', 'xerotact-shield-x-security'); ?></h4>
                        <pre><?php echo esc_html($this->formatExcerpt((array) ($compare['clean_excerpt'] ?? []))); ?></pre>
                    </div>
                </div>
                <?php if (!empty($compare['excerpt_windowed'])) : ?>
                    <p class="shieldx-muted"><?php echo esc_html__('Preview is windowed for browser performance: first lines plus context around the first changed line are shown.', 'xerotact-shield-x-security'); ?></p>
                <?php endif; ?>
            <?php else : ?>
                <p class="shieldx-empty"><?php echo esc_html__('Binary file preview is skipped; hashes and sizes are shown for comparison.', 'xerotact-shield-x-security'); ?></p>
            <?php endif; ?>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $compare
     */
    private function renderPluginCompare(array $compare): void
    {
        $error = (string) ($compare['error'] ?? '');
        $path = (string) ($compare['path'] ?? '');

        if ($error !== '') {
            ?>
            <div class="shieldx-core-compare shieldx-core-compare--error">
                <h3><?php echo esc_html($path); ?></h3>
                <p><?php echo esc_html($error); ?></p>
            </div>
            <?php
            return;
        }

        $eligible = !empty($compare['eligible']);
        $cleanExists = !empty($compare['clean_exists']);
        $matches = !empty($compare['matches']);
        $pluginLabel = trim((string) ($compare['plugin_name'] ?? ''));
        $pluginVersion = trim((string) ($compare['plugin_version'] ?? ''));
        $expectedHash = strtolower((string) ($compare['local_hash'] ?? ''));
        $canDecide = $eligible && !empty($compare['local_exists']) && preg_match('/^[a-f0-9]{64}$/', $expectedHash) === 1;
        $editableContent = !$matches && !empty($compare['text_comparable'])
            ? $this->editablePluginCompareContents($compare)
            : null;
        $closeActionLabel = $canDecide
            ? __('Keep untrusted', 'xerotact-shield-x-security')
            : __('Close compare', 'xerotact-shield-x-security');
        ?>
        <div class="shieldx-core-compare">
            <div class="shieldx-core-compare-head">
                <div>
                    <h3><?php echo esc_html($path); ?></h3>
                    <p>
                        <?php
                        if (!$eligible) {
                            echo esc_html__('This file is not eligible for official WordPress.org plugin comparison.', 'xerotact-shield-x-security');
                        } elseif (!$cleanExists) {
                            echo esc_html__('This file was not found in the official WordPress.org plugin package.', 'xerotact-shield-x-security');
                        } elseif ($matches) {
                            echo esc_html__('Installed file matches the official WordPress.org plugin package.', 'xerotact-shield-x-security');
                        } else {
                            echo esc_html__('Installed file differs from the official WordPress.org plugin package.', 'xerotact-shield-x-security');
                        }
                        ?>
                    </p>
                    <?php if ($pluginLabel !== '') : ?>
                        <p class="shieldx-muted">
                            <?php echo esc_html($pluginVersion !== '' ? $pluginLabel . ' ' . $pluginVersion : $pluginLabel); ?>
                        </p>
                    <?php endif; ?>
                </div>
                <div class="shieldx-core-compare-actions">
                    <?php if ($canDecide) : ?>
                        <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Trust this exact current file hash and resolve scanner findings for this path?', 'xerotact-shield-x-security')); ?>');">
                            <input type="hidden" name="action" value="shieldx_trust_scanner_file">
                            <input type="hidden" name="shieldx_file_route_token" value="<?php echo esc_attr($this->fileRouteToken($path)); ?>">
                            <input type="hidden" name="expected_file_hash" value="<?php echo esc_attr($expectedHash); ?>">
                            <input type="hidden" name="compare_context" value="plugin_compare">
                            <?php wp_nonce_field('shieldx_trust_scanner_file'); ?>
                            <button type="submit" class="button button-primary"><?php echo esc_html__('Trust current file', 'xerotact-shield-x-security'); ?></button>
                        </form>
                    <?php endif; ?>
                    <a class="button button-secondary" href="<?php echo esc_url($this->closePluginCompareUrl()); ?>">
                        <?php echo esc_html($closeActionLabel); ?>
                    </a>
                </div>
            </div>
            <div class="shieldx-core-hashes">
                <span><strong><?php echo esc_html__('Installed', 'xerotact-shield-x-security'); ?></strong> <code><?php echo esc_html(substr((string) ($compare['local_hash'] ?? ''), 0, 16)); ?></code> <?php echo esc_html((string) ($compare['local_size'] ?? 0) . ' bytes'); ?></span>
                <span><strong><?php echo esc_html__('Official plugin', 'xerotact-shield-x-security'); ?></strong> <code><?php echo esc_html(substr((string) ($compare['clean_hash'] ?? ''), 0, 16)); ?></code> <?php echo esc_html((string) ($compare['clean_size'] ?? 0) . ' bytes'); ?></span>
                <?php if (!empty($compare['first_changed_line'])) : ?>
                    <?php
                    $firstChangedLineLabel = sprintf(
                        /* translators: %d: line number. */
                        __('Line %d', 'xerotact-shield-x-security'),
                        (int) $compare['first_changed_line']
                    );
                    ?>
                    <span><strong><?php echo esc_html__('First change', 'xerotact-shield-x-security'); ?></strong> <?php echo esc_html($firstChangedLineLabel); ?></span>
                <?php endif; ?>
            </div>
            <?php if ($canDecide) : ?>
                <p class="shieldx-muted">
                    <?php echo esc_html__('This comparison is advisory. Trusting the current file is a separate administrator action; keeping it untrusted leaves the scanner state unchanged.', 'xerotact-shield-x-security'); ?>
                </p>
            <?php endif; ?>
            <?php if (!empty($compare['text_comparable'])) : ?>
                <div class="shieldx-diff-grid" data-shieldx-linked-scroll>
                    <div>
                        <h4><?php echo esc_html__('Installed plugin file', 'xerotact-shield-x-security'); ?></h4>
                        <pre><?php echo esc_html($this->formatExcerpt((array) ($compare['local_excerpt'] ?? []))); ?></pre>
                    </div>
                    <div>
                        <h4><?php echo esc_html__('Official plugin file', 'xerotact-shield-x-security'); ?></h4>
                        <pre><?php echo esc_html($this->formatExcerpt((array) ($compare['clean_excerpt'] ?? []))); ?></pre>
                    </div>
                </div>
                <?php if (!empty($compare['excerpt_windowed'])) : ?>
                    <p class="shieldx-muted"><?php echo esc_html__('Preview is windowed for browser performance: first lines plus context around the first changed line are shown.', 'xerotact-shield-x-security'); ?></p>
                <?php endif; ?>
                <?php if ($editableContent !== null) : ?>
                    <section class="shieldx-compare-editor">
                        <h4><?php echo esc_html__('Edit installed file', 'xerotact-shield-x-security'); ?></h4>
                        <p><?php echo esc_html__('Edit the complete installed text below. SHIELD-X creates a restore point and a local backup before saving, then rechecks the file.', 'xerotact-shield-x-security'); ?></p>
                        <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Save this complete file content? SHIELD-X will create a restore point, back up the current file, then recheck the saved file.', 'xerotact-shield-x-security')); ?>');">
                            <input type="hidden" name="action" value="shieldx_save_compared_plugin_file">
                            <input type="hidden" name="shieldx_file_route_token" value="<?php echo esc_attr($this->fileRouteToken($path)); ?>">
                            <input type="hidden" name="expected_file_hash" value="<?php echo esc_attr($expectedHash); ?>">
                            <?php wp_nonce_field('shieldx_save_compared_plugin_file'); ?>
                            <label for="shieldx-plugin-compare-editor"><span class="screen-reader-text"><?php echo esc_html__('Complete installed file content', 'xerotact-shield-x-security'); ?></span>
                                <textarea id="shieldx-plugin-compare-editor" name="replacement_content" class="large-text code shieldx-compare-editor-input" rows="18" spellcheck="false"><?php echo esc_textarea($editableContent); ?></textarea>
                            </label>
                            <div class="shieldx-button-row shieldx-compare-editor-actions">
                                <button type="submit" class="button button-primary"><?php echo esc_html__('Save changes', 'xerotact-shield-x-security'); ?></button>
                                <a class="button button-secondary" href="<?php echo esc_url($this->closePluginCompareUrl()); ?>"><?php echo esc_html__('Keep untrusted', 'xerotact-shield-x-security'); ?></a>
                            </div>
                        </form>
                    </section>
                <?php elseif (!$matches) : ?>
                    <p class="shieldx-muted"><?php echo esc_html__('Direct editing is available only for complete valid UTF-8 text files up to 256 KB.', 'xerotact-shield-x-security'); ?></p>
                <?php endif; ?>
            <?php else : ?>
                <p class="shieldx-empty"><?php echo esc_html__('Binary file preview is skipped; hashes and sizes are shown for comparison.', 'xerotact-shield-x-security'); ?></p>
            <?php endif; ?>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $compare
     */
    private function editablePluginCompareContents(array $compare): ?string
    {
        $path = self::sanitizeRelativeFilePath((string) ($compare['path'] ?? ''));
        $expectedHash = strtolower((string) ($compare['local_hash'] ?? ''));
        if ($path === '' || preg_match('/^[a-f0-9]{64}$/', $expectedHash) !== 1 || !PluginFileVerifier::fromWordPress()->isPluginPath($path)) {
            return null;
        }

        $root = realpath(WordPressPaths::root());
        $file = is_string($root) && $root !== '' ? realpath(rtrim($root, '/\\') . '/' . $path) : false;
        $normalizedRoot = is_string($root) ? rtrim(str_replace('\\', '/', $root), '/') . '/' : '';
        $normalizedFile = is_string($file) ? str_replace('\\', '/', $file) : '';
        if (!is_string($file) || $normalizedRoot === '' || strpos($normalizedFile, $normalizedRoot) !== 0 || !is_file($file) || !is_readable($file)) {
            return null;
        }

        $size = filesize($file);
        if (!is_int($size) || $size > self::EDITABLE_PLUGIN_COMPARE_MAX_BYTES) {
            return null;
        }

        $contents = file_get_contents($file);
        if (!is_string($contents) || strpos($contents, "\0") !== false || preg_match('//u', $contents) !== 1) {
            return null;
        }

        $currentHash = hash_file('sha256', $file);

        return is_string($currentHash) && hash_equals($expectedHash, $currentHash) ? $contents : null;
    }

    /**
     * @param array<int,mixed> $lines
     */
    private function formatExcerpt(array $lines): string
    {
        $output = [];

        foreach ($lines as $line) {
            if (!is_array($line)) {
                continue;
            }

            $output[] = str_pad((string) ((int) ($line['line'] ?? 0)), 4, ' ', STR_PAD_LEFT) . ' | ' . (string) ($line['text'] ?? '');
        }

        return implode("\n", $output);
    }

    private function requestedCoreComparePath(): string
    {
        return $this->requestedFileRoutePath('shieldx_core_compare_token', 'shieldx_core_compare');
    }

    private function requestedPluginComparePath(): string
    {
        return $this->queryText('shieldx_plugin_compare');
    }

    private function requestedShadowComparePath(): string
    {
        return $this->queryText('shieldx_shadow_compare');
    }

    private function requestedReviewFilePath(): string
    {
        return $this->requestedFileRoutePath('shieldx_review_file_token', 'shieldx_review_file');
    }

    private function requestedVirtualReviewKey(): string
    {
        return $this->queryText('shieldx_vuln_review');
    }

    private function requestedProtectedFilePath(): string
    {
        return $this->requestedFileRoutePath('shieldx_protected_file_token', 'shieldx_protected_file');
    }

    private function fileReviewUrl(string $path): string
    {
        return add_query_arg(
            [
                'page' => 'shield-x-scan',
                'shieldx_review_file_token' => $this->fileRouteToken($path),
            ],
            admin_url('admin.php')
        ) . '#shieldx-file-review';
    }

    private function closeFileReviewUrl(): string
    {
        return $this->adminPageUrlWithout(
            ['shieldx_review_file', 'shieldx_review_file_token'],
            'shield-x-scan'
        ) . '#shieldx-file-review';
    }

    private function coreCompareUrl(string $path): string
    {
        $args = [
            'page' => 'shield-x-scan',
            'shieldx_core_compare_token' => $this->fileRouteToken($path),
        ];
        $reviewPath = $this->requestedReviewFilePath();
        $protectedPath = $this->requestedProtectedFilePath();

        if ($reviewPath !== '') {
            $args['shieldx_review_file_token'] = $this->fileRouteToken($reviewPath);
        }

        if ($protectedPath !== '') {
            $args['shieldx_protected_file_token'] = $this->fileRouteToken($protectedPath);
        }

        return add_query_arg($args, admin_url('admin.php')) . '#shieldx-core-compare';
    }

    private function pluginCompareUrl(string $path): string
    {
        $args = [
            'page' => 'shield-x-scan',
            'shieldx_plugin_compare' => $path,
        ];
        $reviewPath = $this->requestedReviewFilePath();

        if ($reviewPath !== '') {
            $args['shieldx_review_file'] = $reviewPath;
        }

        return add_query_arg($args, admin_url('admin.php')) . '#shieldx-plugin-compare-result';
    }

    private function shadowCompareUrl(string $path, string $scanId = ''): string
    {
        $args = [
            'page' => 'shield-x-backup',
            'shieldx_shadow_compare' => $path,
        ];
        $scanId = $this->sanitizeShadowScanId($scanId);

        if ($scanId !== '') {
            $args['shieldx_shadow_scanned'] = '1';
            $args['shieldx_shadow_scan'] = $scanId;
        }

        return add_query_arg($args, admin_url('admin.php')) . '#shieldx-shadow-compare';
    }

    private function closeCoreCompareUrl(): string
    {
        $url = $this->adminPageUrlWithout(['shieldx_core_compare', 'shieldx_core_compare_token', 'shieldx_core_restored', 'shieldx_core_error', 'shieldx_core_ready'], 'shield-x-scan');
        if ($this->requestedReviewFilePath() !== '') {
            return $url . '#shieldx-file-review';
        }

        if ($this->requestedProtectedFilePath() !== '') {
            return $url . '#shieldx-protected-file';
        }

        return $url . '#shieldx-core-compare';
    }

    private function closePluginCompareUrl(): string
    {
        $url = $this->adminPageUrlWithout(['shieldx_plugin_compare'], 'shield-x-scan');
        if ($this->requestedReviewFilePath() !== '') {
            return $url . '#shieldx-file-review';
        }

        return $url . '#shieldx-plugin-compare';
    }

    private function closeShadowCompareUrl(): string
    {
        return $this->adminPageUrlWithout(['shieldx_shadow_compare'], 'shield-x-backup') . '#shieldx-file-shadow-copies';
    }

    private function protectedFileRepairUrl(string $path): string
    {
        return add_query_arg(
            [
                'page' => 'shield-x-scan',
                'shieldx_protected_file_token' => $this->fileRouteToken($path),
            ],
            admin_url('admin.php')
        ) . '#shieldx-protected-file';
    }

    private function requestedDatabaseReviewThreatId(): string
    {
        return $this->queryText('shieldx_db_review');
    }

    private function databaseReviewUrl(string $threatId): string
    {
        return add_query_arg(
            [
                'page' => 'shield-x-scan',
                'shieldx_db_review' => $threatId,
            ],
            admin_url('admin.php')
        ) . '#shieldx-db-review';
    }

    private function virtualIssueReviewUrl(string $threatId, string $path): string
    {
        $subject = $threatId !== '' ? $threatId : $path;

        return add_query_arg(
            [
                'page' => 'shield-x-scan',
                'shieldx_vuln_review' => $subject,
            ],
            admin_url('admin.php')
        ) . '#shieldx-vulnerability-review';
    }

    private function closeVirtualIssueReviewUrl(): string
    {
        return $this->adminPageUrlWithout(['shieldx_vuln_review', 'shieldx_review_file'], 'shield-x-scan') . '#shieldx-vulnerability-scanner';
    }

    /**
     * @return array{ok:bool,subject:string,path:string,label:string,specific_issue:bool,issues:array<int,array<string,mixed>>}
     */
    private function buildVirtualScannerReview(string $subject): array
    {
        $repository = ScanRepository::fromWordPress();
        $subject = trim($subject);
        $path = '';
        $issues = [];
        $specificIssue = false;

        if ($subject !== '') {
            $issue = $repository->issueByThreatId($subject);
            if (is_array($issue) && $this->isVirtualScannerPath((string) ($issue['affected_path'] ?? ''))) {
                $path = (string) ($issue['affected_path'] ?? '');
                $issues[] = $issue;
                $specificIssue = true;
            } elseif ($this->isVirtualScannerPath($subject)) {
                $path = $subject;
            }
        }

        if ($path !== '' && !$specificIssue) {
            foreach ($repository->issuesForPath($path, 20) as $issue) {
                if (!is_array($issue)) {
                    continue;
                }

                $threatId = (string) ($issue['threat_id'] ?? '');
                $alreadyAdded = false;
                foreach ($issues as $existing) {
                    if ((string) ($existing['threat_id'] ?? '') === $threatId && $threatId !== '') {
                        $alreadyAdded = true;
                        break;
                    }
                }

                if (!$alreadyAdded) {
                    $issues[] = $issue;
                }
            }
        }

        return [
            'ok' => $path !== '' && !empty($issues),
            'subject' => $subject,
            'path' => $path,
            'label' => $path !== '' ? $this->virtualScannerPathLabel($path) : __('Vulnerability advisory', 'xerotact-shield-x-security'),
            'specific_issue' => $specificIssue,
            'issues' => $issues,
        ];
    }

    /**
     * @param array{ok:bool,subject:string,path:string,label:string,specific_issue?:bool,issues:array<int,array<string,mixed>>} $review
     */
    private function renderVirtualScannerReview(array $review): void
    {
        ?>
        <div class="shieldx-file-review">
            <div class="shieldx-review-title">
                <div>
                    <h3><?php echo esc_html((string) ($review['label'] ?? __('Vulnerability advisory', 'xerotact-shield-x-security'))); ?></h3>
                    <p><?php echo esc_html__('This is a virtual scanner finding for installed software or runtime inventory, not a filesystem path.', 'xerotact-shield-x-security'); ?></p>
                </div>
                <a class="button button-secondary" href="<?php echo esc_url($this->closeVirtualIssueReviewUrl()); ?>">
                    <?php echo esc_html__('Close review', 'xerotact-shield-x-security'); ?>
                </a>
            </div>

            <?php if (empty($review['ok'])) : ?>
                <p class="shieldx-empty"><?php echo esc_html__('Vulnerability item was not found or is no longer active.', 'xerotact-shield-x-security'); ?></p>
            <?php else : ?>
                <dl class="shieldx-detail-list shieldx-detail-list--compact">
                    <?php $this->renderDetail(__('Subject', 'xerotact-shield-x-security'), (string) ($review['path'] ?? '')); ?>
                    <?php $this->renderDetail(!empty($review['specific_issue']) ? __('Selected finding', 'xerotact-shield-x-security') : __('Active findings', 'xerotact-shield-x-security'), (string) count((array) ($review['issues'] ?? []))); ?>
                    <?php $this->renderDetail(__('Recommended action', 'xerotact-shield-x-security'), $this->virtualScannerActionLabel((string) ($review['path'] ?? ''))); ?>
                </dl>

                <div class="shieldx-review-findings">
                    <?php foreach ((array) ($review['issues'] ?? []) as $issue) : ?>
                        <?php if (!is_array($issue)) { continue; } ?>
                        <?php $description = (string) ($issue['description'] ?? ''); ?>
                        <?php $parts = explode("\n\nEvidence: ", $description, 2); ?>
                        <article class="shieldx-review-finding">
                            <div class="shieldx-review-finding-head">
                                <div>
                                    <strong><?php echo esc_html($this->formatLabel((string) ($issue['type'] ?? 'vulnerability'))); ?></strong>
                                    <span><?php echo esc_html((string) ($issue['rule_id'] ?? '')); ?></span>
                                </div>
                                <span class="<?php echo esc_attr('shieldx-severity shieldx-severity--' . strtolower((string) ($issue['severity'] ?? 'medium'))); ?>"><?php echo esc_html($this->formatLabel((string) ($issue['severity'] ?? 'medium'))); ?></span>
                            </div>
                            <p><?php echo esc_html($parts[0]); ?></p>
                            <?php if (!empty($parts[1])) : ?>
                                <code><?php echo esc_html((string) $parts[1]); ?></code>
                            <?php endif; ?>
                            <dl class="shieldx-detail-list shieldx-detail-list--compact">
                                <?php $this->renderDetail(__('First seen', 'xerotact-shield-x-security'), $this->formatTime((string) ($issue['first_detected'] ?? ''))); ?>
                                <?php $this->renderDetail(__('Last seen', 'xerotact-shield-x-security'), $this->formatTime((string) ($issue['last_detected'] ?? ''))); ?>
                            </dl>
                            <div class="shieldx-review-actions">
                                <?php if ($this->virtualIssueHasUpdatesPage((string) ($review['path'] ?? ''))) : ?>
                                    <a class="button button-small" href="<?php echo esc_url(admin_url('update-core.php')); ?>">
                                        <?php echo esc_html__('Open updates', 'xerotact-shield-x-security'); ?>
                                    </a>
                                <?php endif; ?>
                                <?php $this->renderIssueIgnoreActions((string) ($issue['threat_id'] ?? ''), (string) ($issue['affected_path'] ?? ''), (string) ($issue['rule_id'] ?? '')); ?>
                            </div>
                        </article>
                    <?php endforeach; ?>
                </div>
            <?php endif; ?>
        </div>
        <?php
    }

    private function virtualScannerPathLabel(string $path): string
    {
        $parts = explode(':', $path, 2);
        $type = $this->formatLabel((string) ($parts[0] ?? 'software'));
        $name = (string) ($parts[1] ?? '');

        return trim($type . ($name !== '' ? ': ' . $name : ''));
    }

    private function virtualScannerActionLabel(string $path): string
    {
        if ($this->virtualIssueHasUpdatesPage($path)) {
            return __('Update or disable the affected WordPress component.', 'xerotact-shield-x-security');
        }

        return __('Follow the advisory recommendation, keep WAF protection enabled, or disable the affected runtime component at the host level.', 'xerotact-shield-x-security');
    }

    private function virtualIssueHasUpdatesPage(string $path): bool
    {
        return strpos($path, 'plugin:') === 0 || strpos($path, 'theme:') === 0 || strpos($path, 'core:') === 0;
    }

    /**
     * @return array<string,mixed>
     */
    private function buildFileReview(string $path): array
    {
        $path = $this->normalizeReviewPath($path);
        $result = [
            'path' => $path,
            'error' => '',
            'absolute_path' => '',
            'size' => 0,
            'modified' => 0,
            'hash' => '',
            'previewable' => false,
            'truncated' => false,
            'redacted' => false,
            'excerpt' => [],
            'baseline' => [],
            'issues' => [],
            'quarantine_history' => [],
            'protected' => false,
            'core_eligible' => false,
            'plugin_eligible' => false,
            'official_plugin_verified' => false,
        ];

        if ($path === '') {
            $result['error'] = __('No file path was provided.', 'xerotact-shield-x-security');
            return $result;
        }

        if (function_exists('validate_file') && validate_file($path) !== 0) {
            $result['error'] = __('The requested file path is not allowed.', 'xerotact-shield-x-security');
            return $result;
        }

        $wordpressRoot = WordPressPaths::root();
        if ($wordpressRoot === '') {
            $result['error'] = __('WordPress root path is unavailable.', 'xerotact-shield-x-security');
            return $result;
        }

        $root = rtrim($this->normalizeFilesystemPath($wordpressRoot), '/') . '/';
        $candidate = $root . ltrim($path, '/');
        $real = realpath($candidate);

        if (!is_string($real) || $real === '') {
            $result['error'] = __('The file could not be found on disk.', 'xerotact-shield-x-security');
            return $result;
        }

        $realPath = $this->normalizeFilesystemPath($real);
        if (strpos($realPath, $root) !== 0) {
            $result['error'] = __('The requested file is outside the WordPress root.', 'xerotact-shield-x-security');
            return $result;
        }

        if (!is_file($realPath) || !is_readable($realPath)) {
            $result['error'] = __('The file is not readable by WordPress.', 'xerotact-shield-x-security');
            return $result;
        }

        $size = filesize($realPath);
        $modified = filemtime($realPath);
        $hash = hash_file('sha256', $realPath);
        $read = file_get_contents($realPath, false, null, 0, self::FILE_REVIEW_PREVIEW_BYTES + 1);
        $contents = is_string($read) ? $read : '';
        $binary = strpos($contents, "\0") !== false;
        $truncated = is_int($size) && $size > self::FILE_REVIEW_PREVIEW_BYTES;
        if (strlen($contents) > self::FILE_REVIEW_PREVIEW_BYTES) {
            $contents = substr($contents, 0, self::FILE_REVIEW_PREVIEW_BYTES);
            $truncated = true;
        }

        $result['absolute_path'] = $realPath;
        $result['size'] = is_int($size) ? $size : 0;
        $result['modified'] = is_int($modified) ? $modified : 0;
        $result['hash'] = is_string($hash) ? $hash : '';
        $result['truncated'] = $truncated;

        if ($binary) {
            return $result;
        }

        $redacted = $this->redactReviewContent($contents, $result['redacted']);
        $result['previewable'] = true;
        $result['excerpt'] = $this->excerptLinesFromText($redacted);
        $result['protected'] = ScannerRemediation::isProtectedQuarantinePath($path);
        $result['core_eligible'] = CoreFileVerifier::fromWordPress()->isCorePath($path);
        $repository = ScanRepository::fromWordPress();
        $result['baseline'] = $repository->fileRecord($path) ?: [];
        $result['issues'] = $repository->issuesForPath($path, 12);
        $result['quarantine_history'] = ScannerRemediation::fromWordPress()->quarantineHistoryForPath($path, 8);
        $pluginVerifier = PluginFileVerifier::fromWordPress();
        $result['plugin_eligible'] = $pluginVerifier->isPluginPath($path);
        $result['official_plugin_verified'] = $result['hash'] !== '' && $pluginVerifier->isOfficialPluginMatch($path, (string) $result['hash']);

        return $result;
    }

    /**
     * @return array<int,array{line:int,text:string}>
     */
    private function excerptLinesFromText(string $contents): array
    {
        $lines = preg_split('/\r\n|\n|\r/', $contents);
        $lines = is_array($lines) ? array_slice($lines, 0, self::FILE_REVIEW_PREVIEW_LINES) : [];
        $excerpt = [];
        foreach ($lines as $index => $line) {
            $text = (string) $line;
            if (strlen($text) > self::FILE_REVIEW_LINE_BYTES) {
                $text = substr($text, 0, self::FILE_REVIEW_LINE_BYTES) . ' ... [line truncated]';
            }
            $excerpt[] = [
                'line' => $index + 1,
                'text' => $text,
            ];
        }

        return $excerpt;
    }

    /**
     * @param array<string,mixed> $preview
     * @return array<string,mixed>
     */
    private function buildRestorePointFilePreview(array $preview): array
    {
        $result = [
            'ok' => !empty($preview['ok']),
            'error' => (string) ($preview['error'] ?? ''),
            'restore_id' => (string) ($preview['restore_id'] ?? ''),
            'relative_path' => (string) ($preview['relative_path'] ?? ''),
            'size' => (int) ($preview['size'] ?? 0),
            'modified' => (int) ($preview['modified'] ?? 0),
            'hash' => (string) ($preview['hash'] ?? ''),
            'previewable' => false,
            'redacted' => false,
            'excerpt' => [],
        ];

        if (empty($preview['ok'])) {
            if ($result['error'] === '') {
                $result['error'] = __('The restore point snapshot could not be previewed.', 'xerotact-shield-x-security');
            }

            return $result;
        }

        $contents = (string) ($preview['contents'] ?? '');
        if (strpos($contents, "\0") !== false) {
            return $result;
        }

        $redacted = $this->redactReviewContent($contents, $result['redacted']);
        $lines = preg_split('/\r\n|\n|\r/', $redacted);
        $lines = is_array($lines) ? $lines : [];
        foreach ($lines as $index => $line) {
            $result['excerpt'][] = [
                'line' => $index + 1,
                'text' => (string) $line,
            ];
        }

        $result['previewable'] = true;

        return $result;
    }

    /**
     * @return array<string,mixed>
     */
    private function buildProtectedFileRepair(string $path, CoreFileVerifier $coreVerifier): array
    {
        $path = $this->normalizeReviewPath($path);
        $protected = ScannerRemediation::isProtectedQuarantinePath($path);
        $fileReview = $path !== '' ? $this->buildFileReview($path) : [];

        return [
            'path' => $path,
            'protected' => $protected,
            'core_eligible' => $path !== '' && $coreVerifier->isCorePath($path),
            'htaccess' => $path === '.htaccess',
            'config' => in_array($path, ['wp-config.php', 'wp-config-docker.php'], true),
            'config_cleanup' => $path === 'wp-config.php' ? ScannerRemediation::fromWordPress()->previewProtectedConfigCleanup($path) : null,
            'drop_in' => strpos($path, 'wp-content/') === 0,
            'file_review' => $fileReview,
            'error' => $protected ? '' : __('This file is not in the protected repair list.', 'xerotact-shield-x-security'),
        ];
    }

    private function normalizeReviewPath(string $path): string
    {
        $path = str_replace('\\', '/', trim($path));
        $path = preg_replace('#/+#', '/', $path);
        $path = is_string($path) ? ltrim($path, '/') : '';

        return str_replace("\0", '', $path);
    }

    private function normalizeFilesystemPath(string $path): string
    {
        if (function_exists('wp_normalize_path')) {
            return wp_normalize_path($path);
        }

        return str_replace('\\', '/', $path);
    }

    private function redactReviewContent(string $contents, bool &$redacted): string
    {
        $redacted = false;
        $keys = 'DB_NAME|DB_USER|DB_PASSWORD|DB_HOST|AUTH_KEY|SECURE_AUTH_KEY|LOGGED_IN_KEY|NONCE_KEY|AUTH_SALT|SECURE_AUTH_SALT|LOGGED_IN_SALT|NONCE_SALT';
        $contents = (string) preg_replace_callback(
            '/(define\s*\(\s*([\'"])(' . $keys . ')\2\s*,\s*)([\'"])(.*?)(\4)(\s*\)\s*;?)/i',
            static function (array $matches) use (&$redacted): string {
                $redacted = true;
                return $matches[1] . $matches[4] . '[redacted]' . $matches[6] . $matches[7];
            },
            $contents
        );

        $contents = (string) preg_replace_callback(
            '/((?:password|passwd|secret|token|api[_-]?key)\s*=\s*)([^\s&]+)/i',
            static function (array $matches) use (&$redacted): string {
                $redacted = true;
                return $matches[1] . '[redacted]';
            },
            $contents
        );

        return $contents;
    }

    /**
     * @param array<string,mixed> $repair
     */
    private function renderProtectedFileRepair(array $repair): void
    {
        $path = (string) ($repair['path'] ?? '');
        $routeToken = $this->fileRouteToken($path);
        $error = (string) ($repair['error'] ?? '');
        $fileReview = isset($repair['file_review']) && is_array($repair['file_review']) ? $repair['file_review'] : [];
        $kind = $this->protectedFileKind($repair);
        ?>
        <div class="<?php echo esc_attr($error !== '' ? 'shieldx-protected-repair shieldx-protected-repair--error' : 'shieldx-protected-repair'); ?>">
            <div class="shieldx-file-review-head">
                <div>
                    <h3><?php echo esc_html($path !== '' ? $path : __('Protected file', 'xerotact-shield-x-security')); ?></h3>
                    <p><?php echo esc_html($error !== '' ? $error : __('This file is protected from quarantine because removing it can break WordPress bootstrap, admin access, routing, or recovery.', 'xerotact-shield-x-security')); ?></p>
                </div>
                <a class="button button-secondary" href="<?php echo esc_url($this->adminPageUrlWithout(['shieldx_protected_file', 'shieldx_protected_file_token', 'shieldx_htaccess_repaired', 'shieldx_file_trusted', 'shieldx_file_rechecked', 'shieldx_recheck_status', 'shieldx_recheck_count', 'shieldx_scan_action_error'], 'shield-x-scan')); ?>">
                    <?php echo esc_html__('Close repair', 'xerotact-shield-x-security'); ?>
                </a>
            </div>

            <?php if ($error === '') : ?>
                <dl class="shieldx-detail-list shieldx-protected-summary">
                    <?php $this->renderDetail(__('Protection type', 'xerotact-shield-x-security'), $kind); ?>
                    <?php $this->renderDetail(__('Recommended repair', 'xerotact-shield-x-security'), $this->protectedFileRecommendation($repair)); ?>
                    <?php $this->renderDetail(__('Quarantine', 'xerotact-shield-x-security'), __('Blocked for site safety', 'xerotact-shield-x-security')); ?>
                    <?php $this->renderDetail(__('Current hash', 'xerotact-shield-x-security'), substr((string) ($fileReview['hash'] ?? ''), 0, 24)); ?>
                </dl>

                <div class="shieldx-protected-actions">
                    <a class="button button-secondary" href="<?php echo esc_url($this->fileReviewUrl($path)); ?>">
                        <?php echo esc_html(!empty($repair['config']) ? __('Review redacted file', 'xerotact-shield-x-security') : __('Review file', 'xerotact-shield-x-security')); ?>
                    </a>
                    <?php if (!empty($repair['core_eligible'])) : ?>
                        <a class="button button-secondary" href="<?php echo esc_url($this->coreCompareUrl($path)); ?>">
                            <?php echo esc_html__('Compare official', 'xerotact-shield-x-security'); ?>
                        </a>
                        <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Replace this file with the official WordPress copy after creating a backup?', 'xerotact-shield-x-security')); ?>');">
                            <input type="hidden" name="action" value="shieldx_restore_core_file">
                            <input type="hidden" name="shieldx_file_route_token" value="<?php echo esc_attr($routeToken); ?>">
                            <?php wp_nonce_field('shieldx_restore_core_file'); ?>
                            <button type="submit" class="button button-primary"><?php echo esc_html__('Replace with official copy', 'xerotact-shield-x-security'); ?></button>
                        </form>
                    <?php endif; ?>
                    <?php if (!empty($repair['htaccess'])) : ?>
                        <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Back up .htaccess and repair only the WordPress rewrite block?', 'xerotact-shield-x-security')); ?>');">
                            <input type="hidden" name="action" value="shieldx_repair_htaccess">
                            <?php wp_nonce_field('shieldx_repair_htaccess'); ?>
                            <button type="submit" class="button button-primary"><?php echo esc_html__('Repair WordPress rewrite block', 'xerotact-shield-x-security'); ?></button>
                        </form>
                    <?php endif; ?>
                    <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Mark this file validated & trusted and resolve scanner findings for this path?', 'xerotact-shield-x-security')); ?>');">
                        <input type="hidden" name="action" value="shieldx_trust_scanner_file">
                        <input type="hidden" name="shieldx_file_route_token" value="<?php echo esc_attr($routeToken); ?>">
                        <?php wp_nonce_field('shieldx_trust_scanner_file'); ?>
                        <button type="submit" class="button button-secondary"><?php echo esc_html__('Validated & trusted', 'xerotact-shield-x-security'); ?></button>
                    </form>
                    <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                        <input type="hidden" name="action" value="shieldx_recheck_scanner_file">
                        <input type="hidden" name="shieldx_file_route_token" value="<?php echo esc_attr($routeToken); ?>">
                        <?php wp_nonce_field('shieldx_recheck_scanner_file'); ?>
                        <button type="submit" class="button button-secondary"><?php echo esc_html__('Re-scan this file', 'xerotact-shield-x-security'); ?></button>
                    </form>
                </div>

                <?php if (!empty($repair['config'])) : ?>
                    <p class="shieldx-protected-note"><?php echo esc_html__('wp-config.php is never auto-replaced. Review the redacted preview, remove suspicious injected lines manually, then trust the baseline only after confirming the change is legitimate.', 'xerotact-shield-x-security'); ?></p>
                    <?php if (isset($repair['config_cleanup']) && is_array($repair['config_cleanup'])) : ?>
                        <?php $this->renderConfigCleanupPanel($path, $repair['config_cleanup']); ?>
                    <?php endif; ?>
                <?php elseif (!empty($repair['drop_in'])) : ?>
                    <p class="shieldx-protected-note"><?php echo esc_html__('WordPress drop-ins are loaded directly by core. Review ownership and source plugin before trusting or replacing them manually.', 'xerotact-shield-x-security'); ?></p>
                <?php endif; ?>

                <?php if (empty($fileReview['error']) && !empty($fileReview['previewable'])) : ?>
                    <div class="shieldx-protected-preview">
                        <h4><?php echo esc_html__('Read-only preview', 'xerotact-shield-x-security'); ?></h4>
                        <pre><?php echo esc_html($this->formatExcerpt((array) ($fileReview['excerpt'] ?? []))); ?></pre>
                        <?php if (!empty($fileReview['redacted'])) : ?>
                            <p class="shieldx-muted">
                                <?php echo esc_html__('sensitive values redacted', 'xerotact-shield-x-security'); ?>
                            </p>
                        <?php endif; ?>
                        <?php if (!empty($fileReview['truncated'])) : ?>
                            <p class="shieldx-muted">
                                <?php echo esc_html__('Preview is truncated for browser performance; use server tools for full large-file review.', 'xerotact-shield-x-security'); ?>
                            </p>
                        <?php endif; ?>
                    </div>
                <?php elseif (!empty($fileReview['error'])) : ?>
                    <p class="shieldx-empty"><?php echo esc_html((string) $fileReview['error']); ?></p>
                <?php endif; ?>
            <?php endif; ?>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $repair
     */
    private function protectedFileKind(array $repair): string
    {
        if (!empty($repair['core_eligible'])) {
            return __('WordPress core runtime', 'xerotact-shield-x-security');
        }

        if (!empty($repair['htaccess'])) {
            return __('Rewrite / access control', 'xerotact-shield-x-security');
        }

        if (!empty($repair['config'])) {
            return __('Configuration secrets', 'xerotact-shield-x-security');
        }

        if (!empty($repair['drop_in'])) {
            return __('WordPress drop-in', 'xerotact-shield-x-security');
        }

        return __('Protected runtime file', 'xerotact-shield-x-security');
    }

    /**
     * @param array<string,mixed> $repair
     */
    private function protectedFileRecommendation(array $repair): string
    {
        if (!empty($repair['core_eligible'])) {
            return __('Compare with official WordPress, then replace from clean package if changed unexpectedly.', 'xerotact-shield-x-security');
        }

        if (!empty($repair['htaccess'])) {
            return __('Repair only the WordPress rewrite block while preserving custom and SHIELD-X sections.', 'xerotact-shield-x-security');
        }

        if (!empty($repair['config'])) {
            return __('Review redacted content and manually remove suspicious injected lines.', 'xerotact-shield-x-security');
        }

        return __('Review source and mark this file validated & trusted only when the change is legitimate.', 'xerotact-shield-x-security');
    }

    /**
     * @param array<string,mixed> $review
     */
    private function renderFileReview(array $review): void
    {
        $error = (string) ($review['error'] ?? '');
        $path = (string) ($review['path'] ?? '');
        $issues = isset($review['issues']) && is_array($review['issues']) ? array_values(array_filter($review['issues'], 'is_array')) : [];
        $baseline = isset($review['baseline']) && is_array($review['baseline']) ? $review['baseline'] : [];
        $history = isset($review['quarantine_history']) && is_array($review['quarantine_history']) ? array_values(array_filter($review['quarantine_history'], 'is_array')) : [];
        $firstThreatId = '';
        foreach ($issues as $issue) {
            if ((string) ($issue['status'] ?? '') === 'active' && (string) ($issue['threat_id'] ?? '') !== '') {
                $firstThreatId = (string) $issue['threat_id'];
                break;
            }
        }
        if ($firstThreatId === '' && !empty($issues[0]['threat_id'])) {
            $firstThreatId = (string) $issues[0]['threat_id'];
        }
        $isProtected = !empty($review['protected']);
        $canCompare = !empty($review['core_eligible']);
        $canPluginCompare = !$canCompare && $path !== '' && PluginFileVerifier::fromWordPress()->isPluginPath($path);
        $officialPluginVerified = !empty($review['official_plugin_verified']);
        ?>
        <div class="<?php echo esc_attr($error !== '' ? 'shieldx-file-review shieldx-file-review--error' : 'shieldx-file-review'); ?>">
            <div class="shieldx-file-review-head">
                <div>
                    <h3><?php echo esc_html($path !== '' ? $path : __('File review', 'xerotact-shield-x-security')); ?></h3>
                    <p>
                        <?php
                        echo esc_html($error !== ''
                            ? $error
                            : __('Preview is read-only for safe admin review.', 'xerotact-shield-x-security'));
                        ?>
                    </p>
                </div>
                <a class="button button-secondary" href="<?php echo esc_url($this->closeFileReviewUrl()); ?>">
                    <?php echo esc_html__('Close review', 'xerotact-shield-x-security'); ?>
                </a>
            </div>

            <?php if ($error === '') : ?>
                <div class="shieldx-file-review-meta">
                    <span><strong><?php echo esc_html__('Size', 'xerotact-shield-x-security'); ?></strong> <?php echo esc_html($this->formatBytes((int) ($review['size'] ?? 0))); ?></span>
                    <span><strong><?php echo esc_html__('Modified', 'xerotact-shield-x-security'); ?></strong> <?php echo esc_html($this->formatTimestamp((int) ($review['modified'] ?? 0))); ?></span>
                    <span><strong><?php echo esc_html__('SHA-256', 'xerotact-shield-x-security'); ?></strong> <code><?php echo esc_html(substr((string) ($review['hash'] ?? ''), 0, 24)); ?></code></span>
                    <?php if ($officialPluginVerified) : ?>
                        <span><strong><?php echo esc_html__('Official plugin', 'xerotact-shield-x-security'); ?></strong> <?php echo esc_html__('Matched', 'xerotact-shield-x-security'); ?></span>
                    <?php endif; ?>
                    <span><strong><?php echo esc_html__('Disk path', 'xerotact-shield-x-security'); ?></strong> <code><?php echo esc_html((string) ($review['absolute_path'] ?? '')); ?></code></span>
                </div>

                <div class="shieldx-file-side-by-side">
                    <div class="shieldx-file-review-column">
                        <h4><?php echo esc_html__('Current file', 'xerotact-shield-x-security'); ?></h4>
                        <?php if (!empty($review['previewable'])) : ?>
                            <pre><?php echo esc_html($this->formatExcerpt((array) ($review['excerpt'] ?? []))); ?></pre>
                            <?php if (!empty($review['redacted'])) : ?>
                                <p class="shieldx-muted">
                                    <?php echo esc_html__('sensitive values redacted', 'xerotact-shield-x-security'); ?>
                                </p>
                            <?php endif; ?>
                            <?php if (!empty($review['truncated'])) : ?>
                                <p class="shieldx-muted">
                                    <?php echo esc_html__('Preview is truncated for browser performance; use server tools for full large-file review.', 'xerotact-shield-x-security'); ?>
                                </p>
                            <?php endif; ?>
                        <?php else : ?>
                            <p class="shieldx-empty"><?php echo esc_html__('Binary or unreadable text preview is skipped; metadata is shown above.', 'xerotact-shield-x-security'); ?></p>
                        <?php endif; ?>
                    </div>
                    <div class="shieldx-file-review-column shieldx-file-review-column--context">
                        <h4><?php echo esc_html__('Scanner context', 'xerotact-shield-x-security'); ?></h4>
                        <div class="shieldx-file-review-actions">
                            <?php if ($canCompare) : ?>
                                <a class="button button-small" href="<?php echo esc_url($this->coreCompareUrl($path)); ?>">
                                    <?php echo esc_html__('Compare core', 'xerotact-shield-x-security'); ?>
                                </a>
                            <?php endif; ?>
                            <?php if ($canPluginCompare) : ?>
                                <a class="button button-small" href="<?php echo esc_url($this->pluginCompareUrl($path)); ?>">
                                    <?php echo esc_html__('Compare plugin', 'xerotact-shield-x-security'); ?>
                                </a>
                            <?php endif; ?>
                            <?php if ($isProtected) : ?>
                                <a class="button button-small" href="<?php echo esc_url($this->protectedFileRepairUrl($path)); ?>">
                                    <?php echo esc_html__('Repair protected file', 'xerotact-shield-x-security'); ?>
                                </a>
                            <?php endif; ?>
                            <?php if ($firstThreatId !== '') : ?>
                                <?php $this->renderScanIssueExtensionActions($this->scanIssueByThreatId($issues, $firstThreatId), 'file_review'); ?>
                            <?php endif; ?>
                            <?php if ($path !== '') : ?>
                                <form class="shieldx-row-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                                    <input type="hidden" name="action" value="shieldx_recheck_scanner_file">
                                    <input type="hidden" name="file_path" value="<?php echo esc_attr($path); ?>">
                                    <?php wp_nonce_field('shieldx_recheck_scanner_file'); ?>
                                    <button type="submit" class="button button-small shieldx-row-action-button"><?php echo esc_html__('Recheck file', 'xerotact-shield-x-security'); ?></button>
                                </form>
                                <form class="shieldx-row-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Mark this file validated & trusted only after manual review?', 'xerotact-shield-x-security')); ?>');">
                                    <input type="hidden" name="action" value="shieldx_trust_scanner_file">
                                    <input type="hidden" name="file_path" value="<?php echo esc_attr($path); ?>">
                                    <?php wp_nonce_field('shieldx_trust_scanner_file'); ?>
                                    <button type="submit" class="button button-small shieldx-row-action-button"><?php echo esc_html__('Validated & trusted', 'xerotact-shield-x-security'); ?></button>
                                </form>
                            <?php endif; ?>
                            <?php if (!$isProtected && !$canCompare && $firstThreatId !== '') : ?>
                                <form class="shieldx-row-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Move this suspicious file into SHIELD-X quarantine?', 'xerotact-shield-x-security')); ?>');">
                                    <input type="hidden" name="action" value="shieldx_quarantine_scan_issue">
                                    <input type="hidden" name="threat_id" value="<?php echo esc_attr($firstThreatId); ?>">
                                    <?php wp_nonce_field('shieldx_quarantine_scan_issue'); ?>
                                    <button type="submit" class="button button-small shieldx-row-action-button shieldx-button-danger"><?php echo esc_html__('Quarantine file', 'xerotact-shield-x-security'); ?></button>
                                </form>
                            <?php endif; ?>
                        </div>

                        <div class="shieldx-file-context-block">
                            <h5><?php echo esc_html__('Active findings', 'xerotact-shield-x-security'); ?></h5>
                            <?php if (empty($issues)) : ?>
                                <p class="shieldx-empty"><?php echo esc_html__('No stored scanner findings currently point at this file.', 'xerotact-shield-x-security'); ?></p>
                            <?php else : ?>
                                <div class="shieldx-file-context-list">
                                    <?php foreach ($issues as $issue) : ?>
                                        <?php $this->renderFileReviewIssue($issue); ?>
                                    <?php endforeach; ?>
                                </div>
                            <?php endif; ?>
                        </div>

                        <div class="shieldx-file-context-block">
                            <h5><?php echo esc_html__('Baseline', 'xerotact-shield-x-security'); ?></h5>
                            <?php if (empty($baseline)) : ?>
                                <p class="shieldx-empty"><?php echo esc_html__('No previous file baseline was recorded for this path.', 'xerotact-shield-x-security'); ?></p>
                            <?php else : ?>
                                <dl class="shieldx-mini-detail-list">
                                    <?php $this->renderMiniDetail(__('Baseline hash', 'xerotact-shield-x-security'), substr((string) ($baseline['file_hash'] ?? ''), 0, 24)); ?>
                                    <?php $this->renderMiniDetail(__('Baseline size', 'xerotact-shield-x-security'), $this->formatBytes((int) ($baseline['file_size'] ?? 0))); ?>
                                    <?php $this->renderMiniDetail(__('Last modified', 'xerotact-shield-x-security'), (string) ($baseline['last_modified'] ?? '')); ?>
                                    <?php $this->renderMiniDetail(__('Scan time', 'xerotact-shield-x-security'), (string) ($baseline['scan_time'] ?? '')); ?>
                                </dl>
                            <?php endif; ?>
                        </div>

                        <div class="shieldx-file-context-block">
                            <h5><?php echo esc_html__('Quarantine history', 'xerotact-shield-x-security'); ?></h5>
                            <?php if (empty($history)) : ?>
                                <p class="shieldx-empty"><?php echo esc_html__('No quarantine or restore attempt is recorded for this file.', 'xerotact-shield-x-security'); ?></p>
                            <?php else : ?>
                                <div class="shieldx-file-context-list">
                                    <?php foreach ($history as $item) : ?>
                                        <?php $this->renderQuarantineHistoryItem($item, false); ?>
                                    <?php endforeach; ?>
                                </div>
                            <?php endif; ?>
                        </div>
                    </div>
                </div>

                <?php if ($path === 'wp-config.php') : ?>
                    <?php $this->renderConfigCleanupPanel($path, ScannerRemediation::fromWordPress()->previewProtectedConfigCleanup($path)); ?>
                <?php endif; ?>
            <?php endif; ?>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $issue
     */
    private function renderFileReviewIssue(array $issue): void
    {
        $description = (string) ($issue['description'] ?? '');
        $parts = explode("\n\nEvidence: ", $description, 2);
        ?>
        <div class="shieldx-file-context-item">
            <div>
                <strong><?php echo esc_html($this->formatLabel((string) ($issue['type'] ?? 'scanner'))); ?></strong>
                <span class="<?php echo esc_attr('shieldx-severity shieldx-severity--' . strtolower((string) ($issue['severity'] ?? 'medium'))); ?>"><?php echo esc_html($this->formatLabel((string) ($issue['severity'] ?? 'medium'))); ?></span>
            </div>
            <p><?php echo esc_html($parts[0]); ?></p>
            <?php if (!empty($parts[1])) : ?>
                <code><?php echo esc_html((string) $parts[1]); ?></code>
            <?php endif; ?>
            <small><?php echo esc_html($this->formatLabel((string) ($issue['status'] ?? 'active')) . ' / ' . (string) ($issue['rule_id'] ?? '')); ?></small>
        </div>
        <?php
    }

    private function renderMiniDetail(string $label, string $value): void
    {
        ?>
        <dt><?php echo esc_html($label); ?></dt>
        <dd><?php echo esc_html($value !== '' ? $value : __('Unknown', 'xerotact-shield-x-security')); ?></dd>
        <?php
    }

    /**
     * @param array<string,mixed> $cleanup
     */
    private function renderConfigCleanupPanel(string $path, array $cleanup): void
    {
        $candidates = isset($cleanup['candidates']) && is_array($cleanup['candidates']) ? $cleanup['candidates'] : [];
        $error = empty($cleanup['ok']) ? (string) ($cleanup['message'] ?? '') : '';
        ?>
        <div class="<?php echo esc_attr($error !== '' ? 'shieldx-config-cleanup shieldx-config-cleanup--error' : 'shieldx-config-cleanup'); ?>">
            <div class="shieldx-section-head shieldx-section-head--compact">
                <h4><?php echo esc_html__('Guided wp-config cleanup', 'xerotact-shield-x-security'); ?></h4>
                <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                <span><?php echo esc_html(sprintf(_n('%d candidate line', '%d candidate lines', count($candidates), 'xerotact-shield-x-security'), count($candidates))); ?></span>
            </div>

            <?php if ($error !== '') : ?>
                <p class="shieldx-empty"><?php echo esc_html($error); ?></p>
            <?php elseif (empty($candidates)) : ?>
                <p class="shieldx-empty"><?php echo esc_html__('No removable suspicious wp-config.php lines were detected in the current file.', 'xerotact-shield-x-security'); ?></p>
            <?php else : ?>
                <form class="shieldx-config-cleanup-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Back up wp-config.php and remove the selected suspicious lines?', 'xerotact-shield-x-security')); ?>');">
                    <input type="hidden" name="action" value="shieldx_cleanup_config_file">
                    <input type="hidden" name="shieldx_file_route_token" value="<?php echo esc_attr($this->fileRouteToken($path)); ?>">
                    <?php wp_nonce_field('shieldx_cleanup_config_file'); ?>

                    <div class="shieldx-config-cleanup-list">
                        <?php foreach ($candidates as $candidate) : ?>
                            <?php
                            $line = (int) ($candidate['line'] ?? 0);
                            if ($line < 1) {
                                continue;
                            }
                            ?>
                            <label class="shieldx-config-cleanup-item">
                                <input type="checkbox" name="cleanup_lines[]" value="<?php echo esc_attr((string) $line); ?>" checked>
                                <span>
                                    <?php /* translators: %d: line number. */ ?>
                                    <strong><?php echo esc_html(sprintf(__('Line %d', 'xerotact-shield-x-security'), $line)); ?></strong>
                                    <em><?php echo esc_html((string) ($candidate['reason'] ?? __('Suspicious config code', 'xerotact-shield-x-security'))); ?></em>
                                    <code><?php echo esc_html((string) ($candidate['snippet'] ?? '')); ?></code>
                                </span>
                            </label>
                        <?php endforeach; ?>
                    </div>

                    <div class="shieldx-protected-actions">
                        <button type="submit" class="button button-primary"><?php echo esc_html__('Back up and remove selected lines', 'xerotact-shield-x-security'); ?></button>
                    </div>
                </form>
            <?php endif; ?>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $review
     */
    private function renderDatabaseReview(array $review): void
    {
        $ok = !empty($review['ok']);
        $issue = isset($review['issue']) && is_array($review['issue']) ? $review['issue'] : [];
        $target = isset($review['target']) && is_array($review['target']) ? $review['target'] : [];
        $row = isset($review['row']) && is_array($review['row']) ? $review['row'] : [];
        $actions = isset($review['actions']) && is_array($review['actions']) ? $review['actions'] : [];
        $history = isset($review['history']) && is_array($review['history']) ? $review['history'] : [];
        $notice = (string) ($review['notice'] ?? '');
        $closeUrl = $this->adminPageUrlWithout(['shieldx_db_review', 'shieldx_db_remediated', 'shieldx_db_restored', 'shieldx_db_rechecked', 'shieldx_recheck_status', 'shieldx_recheck_count', 'shieldx_scan_action_error'], 'shield-x-scan');
        $rowJson = wp_json_encode($row, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        ?>
        <div class="<?php echo esc_attr($ok ? 'shieldx-db-review' : 'shieldx-db-review shieldx-db-review--error'); ?>">
            <div class="shieldx-file-review-head">
                <div>
                    <h3><?php echo esc_html($ok ? (string) ($issue['affected_path'] ?? __('Database finding', 'xerotact-shield-x-security')) : __('Database review unavailable', 'xerotact-shield-x-security')); ?></h3>
                    <p><?php echo esc_html($ok ? (string) ($issue['description'] ?? '') : (string) ($review['error'] ?? __('SHIELD-X could not load this database row.', 'xerotact-shield-x-security'))); ?></p>
                </div>
                <a class="button button-secondary" href="<?php echo esc_url($closeUrl); ?>"><?php echo esc_html__('Close review', 'xerotact-shield-x-security'); ?></a>
            </div>

            <?php if ($ok) : ?>
                <?php if ($notice !== '') : ?>
                    <p class="shieldx-alert shieldx-alert--info"><?php echo esc_html($notice); ?></p>
                <?php endif; ?>
                <dl class="shieldx-detail-list shieldx-db-review-details">
                    <?php $this->renderDetail(__('Target', 'xerotact-shield-x-security'), $this->formatLabel((string) ($target['kind'] ?? 'database')) . ' / ' . (string) ($target['id'] ?? '')); ?>
                    <?php $this->renderDetail(__('Rule', 'xerotact-shield-x-security'), (string) ($issue['rule_id'] ?? '')); ?>
                    <?php $this->renderDetail(__('Status', 'xerotact-shield-x-security'), $this->formatLabel((string) ($issue['status'] ?? 'active'))); ?>
                    <?php $this->renderDetail(__('Severity', 'xerotact-shield-x-security'), $this->formatLabel((string) ($issue['severity'] ?? 'medium'))); ?>
                </dl>

                <div class="shieldx-db-action-strip">
                    <?php $this->renderDatabaseRecheckButton((string) ($issue['threat_id'] ?? '')); ?>
                    <?php $this->renderScanIssueExtensionActions($issue, 'database_review'); ?>
                    <?php if (!empty($review['resolved'])) : ?>
                        <span class="shieldx-muted"><?php echo esc_html__('This database finding is already resolved. The row preview is shown for audit only.', 'xerotact-shield-x-security'); ?></span>
                    <?php elseif (empty($actions)) : ?>
                        <span class="shieldx-muted"><?php echo esc_html__('Review only. This database finding needs manual confirmation before SHIELD-X offers an automatic repair.', 'xerotact-shield-x-security'); ?></span>
                    <?php else : ?>
                        <?php foreach ($actions as $action) : ?>
                            <?php
                            if (!is_array($action)) {
                                continue;
                            }
                            $this->renderDatabaseActionButton(
                                (string) ($issue['threat_id'] ?? ''),
                                (string) ($action['id'] ?? ''),
                                (string) ($action['label'] ?? __('Repair', 'xerotact-shield-x-security')),
                                (string) ($action['tone'] ?? 'warning')
                            );
                            ?>
                        <?php endforeach; ?>
                    <?php endif; ?>
                </div>

                <pre class="shieldx-db-review-json"><?php echo esc_html(is_string($rowJson) ? $rowJson : '{}'); ?></pre>
                <p class="shieldx-muted"><?php echo esc_html__('Sensitive column names are redacted and long values are truncated in this preview.', 'xerotact-shield-x-security'); ?></p>
                <?php $this->renderDatabaseReviewHistory($history); ?>
            <?php endif; ?>
        </div>
        <?php
    }

    private function renderDatabaseRecheckButton(string $threatId): void
    {
        if ($threatId === '') {
            return;
        }
        ?>
        <form class="shieldx-row-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
            <input type="hidden" name="action" value="shieldx_recheck_db_issue">
            <input type="hidden" name="threat_id" value="<?php echo esc_attr($threatId); ?>">
            <?php wp_nonce_field('shieldx_recheck_db_issue'); ?>
            <button type="submit" class="button button-small shieldx-row-action-button">
                <?php echo esc_html__('Re-scan this item', 'xerotact-shield-x-security'); ?>
            </button>
        </form>
        <?php
    }

    /**
     * @param array<int,array<string,mixed>> $history
     */
    private function renderDatabaseReviewHistory(array $history): void
    {
        ?>
        <div class="shieldx-db-history">
            <h4><?php echo esc_html__('Remediation History', 'xerotact-shield-x-security'); ?></h4>
            <?php if (empty($history)) : ?>
                <p class="shieldx-empty"><?php echo esc_html__('No repair backups have been recorded for this item yet.', 'xerotact-shield-x-security'); ?></p>
            <?php else : ?>
                <div class="shieldx-db-history-list">
                    <?php foreach ($history as $backup) : ?>
                        <?php
                        if (!is_array($backup)) {
                            continue;
                        }
                        ?>
                        <div class="shieldx-db-history-item">
                            <strong><?php echo esc_html($this->formatLabel((string) ($backup['action'] ?? __('Database repair', 'xerotact-shield-x-security')))); ?></strong>
                            <span><?php echo esc_html($this->formatTime((string) ($backup['created_at'] ?? ''))); ?></span>
                            <code><?php echo esc_html((string) ($backup['backup_id'] ?? '')); ?></code>
                            <?php if (!empty($backup['restored_at'])) : ?>
                                <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                                <em><?php echo esc_html(sprintf(__('Restored %s', 'xerotact-shield-x-security'), $this->formatTime((string) $backup['restored_at']))); ?></em>
                            <?php endif; ?>
                        </div>
                    <?php endforeach; ?>
                </div>
            <?php endif; ?>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $issue
     * @return array<int,array{id:string,label:string,tone:string}>
     */
    private function databaseIssueActions(array $issue): array
    {
        $path = (string) ($issue['affected_path'] ?? '');
        if (strpos($path, 'db:') !== 0) {
            return [];
        }

        $parts = explode(':', $path, 3);
        if (count($parts) !== 3) {
            return [];
        }

        $kind = $parts[1];
        $id = $parts[2];
        $ruleId = (string) ($issue['rule_id'] ?? '');

        if ($kind === 'cron') {
            return [['id' => 'delete_cron_hook', 'label' => __('Delete cron hook', 'xerotact-shield-x-security'), 'tone' => 'danger']];
        }

        if ($kind === 'posts') {
            return [['id' => 'sanitize_post', 'label' => __('Clean content', 'xerotact-shield-x-security'), 'tone' => 'danger']];
        }

        if ($kind === 'users') {
            return [['id' => 'flag_admin', 'label' => __('Flag admin', 'xerotact-shield-x-security'), 'tone' => 'warning']];
        }

        if ($kind !== 'options') {
            return [];
        }

        if ($ruleId === 'SX-DB-OPTION-001') {
            return [['id' => 'disable_autoload', 'label' => __('Disable autoload', 'xerotact-shield-x-security'), 'tone' => 'warning']];
        }

        if ($id === 'cron') {
            return [['id' => 'clean_cron_hooks', 'label' => __('Clean cron hooks', 'xerotact-shield-x-security'), 'tone' => 'danger']];
        }

        if (in_array($id, ['home', 'siteurl'], true)) {
            return [];
        }

        return [
            ['id' => 'sanitize_option', 'label' => __('Clean option', 'xerotact-shield-x-security'), 'tone' => 'danger'],
            ['id' => 'disable_autoload', 'label' => __('Disable autoload', 'xerotact-shield-x-security'), 'tone' => 'warning'],
        ];
    }

    /**
     * @param array<string,mixed> $issue
     */
    private function renderDatabaseIssueActions(array $issue): void
    {
        $threatId = (string) ($issue['threat_id'] ?? '');
        foreach ($this->databaseIssueActions($issue) as $action) {
            $this->renderDatabaseActionButton($threatId, $action['id'], $action['label'], $action['tone']);
        }
    }

    private function renderDatabaseActionButton(string $threatId, string $action, string $label, string $tone = 'warning'): void
    {
        if ($threatId === '' || $action === '') {
            return;
        }

        $isDanger = $tone === 'danger';
        $confirm = $isDanger
            ? __('SHIELD-X will back up the current database row before applying this repair. Continue?', 'xerotact-shield-x-security')
            : __('SHIELD-X will back up the current database row before changing it. Continue?', 'xerotact-shield-x-security');
        ?>
        <form class="shieldx-row-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js($confirm); ?>');">
            <input type="hidden" name="action" value="shieldx_remediate_db_issue">
            <input type="hidden" name="threat_id" value="<?php echo esc_attr($threatId); ?>">
            <input type="hidden" name="db_action" value="<?php echo esc_attr($action); ?>">
            <?php wp_nonce_field('shieldx_remediate_db_issue'); ?>
            <button type="submit" class="<?php echo esc_attr($isDanger ? 'button button-small shieldx-row-action-button shieldx-button-danger' : 'button button-small shieldx-row-action-button shieldx-button-warning'); ?>">
                <?php echo esc_html($label); ?>
            </button>
        </form>
        <?php
    }

    /**
     * @param array<string,mixed> $issue
     */
    private function renderIssueRow(array $issue, CoreFileVerifier $coreVerifier, PluginFileVerifier $pluginVerifier, ScannerRemediation $scannerRemediation, string $bulkFormId = 'shieldx-bulk-scan-form-file', bool $showSelectColumn = true): void
    {
        $description = (string) ($issue['description'] ?? '');
        $parts = explode("\n\nEvidence: ", $description, 2);
        $path = (string) ($issue['affected_path'] ?? '');
        $threatId = (string) ($issue['threat_id'] ?? '');
        $ruleId = (string) ($issue['rule_id'] ?? '');
        $isDatabaseIssue = strpos($path, 'db:') === 0;
        $isVirtualIssue = $this->isVirtualScannerPath($path);
        $isLocalFileIssue = $path !== '' && !$isDatabaseIssue && !$isVirtualIssue;
        $canFileAction = $isLocalFileIssue && $scannerRemediation->hasExistingFile($path);
        $canCompare = $canFileAction && $coreVerifier->isCorePath($path);
        $canPluginCompare = !$canCompare && $canFileAction && $pluginVerifier->isPluginPath($path);
        $isQuarantineProtected = ScannerRemediation::isProtectedQuarantinePath($path);
        ?>
        <tr>
            <?php if ($showSelectColumn) : ?>
                <td class="shieldx-bulk-check-column">
                    <?php if ($isLocalFileIssue && $threatId !== '') : ?>
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <input type="checkbox" form="<?php echo esc_attr($bulkFormId); ?>" name="bulk_threat_ids[]" value="<?php echo esc_attr($threatId); ?>" data-shieldx-file-available="<?php echo $canFileAction ? '1' : '0'; ?>" aria-label="<?php echo esc_attr(sprintf(__('Select issue for %s', 'xerotact-shield-x-security'), $path)); ?>">
                    <?php endif; ?>
                </td>
            <?php endif; ?>
            <td><span class="<?php echo esc_attr('shieldx-severity shieldx-severity--' . strtolower((string) ($issue['severity'] ?? 'medium'))); ?>"><?php echo esc_html($this->formatLabel((string) ($issue['severity'] ?? 'medium'))); ?></span></td>
            <td>
                <strong><?php echo esc_html($this->formatLabel((string) ($issue['type'] ?? 'scanner'))); ?></strong>
                <span><?php echo esc_html($parts[0]); ?></span>
                <?php if (isset($parts[1]) && $parts[1] !== '') : ?>
                    <small><?php echo esc_html($parts[1]); ?></small>
                <?php endif; ?>
            </td>
            <td><code><?php echo esc_html($path); ?></code></td>
            <td><?php echo esc_html($this->formatTime((string) ($issue['first_detected'] ?? ''))); ?></td>
            <td><?php echo esc_html($this->formatTime((string) ($issue['last_detected'] ?? ''))); ?></td>
            <td>
                <?php if ($canFileAction) : ?>
                    <div class="shieldx-row-actions">
                        <a class="button button-small" href="<?php echo esc_url($this->fileReviewUrl($path)); ?>">
                            <?php echo esc_html__('Review file', 'xerotact-shield-x-security'); ?>
                        </a>
                        <?php $this->renderScanIssueExtensionActions($issue, 'scanner_row'); ?>
                        <?php if ($canCompare) : ?>
                            <a class="button button-small" href="<?php echo esc_url($this->coreCompareUrl($path)); ?>">
                                <?php echo esc_html__('Compare core', 'xerotact-shield-x-security'); ?>
                            </a>
                        <?php endif; ?>
                        <?php if ($canPluginCompare) : ?>
                            <a class="button button-small" href="<?php echo esc_url($this->pluginCompareUrl($path)); ?>">
                                <?php echo esc_html__('Compare plugin', 'xerotact-shield-x-security'); ?>
                            </a>
                        <?php endif; ?>
                        <?php if ($isQuarantineProtected) : ?>
                            <a class="button button-small" href="<?php echo esc_url($this->protectedFileRepairUrl($path)); ?>">
                                <?php echo esc_html__('Repair options', 'xerotact-shield-x-security'); ?>
                            </a>
                        <?php endif; ?>
                        <?php if (!$canCompare && !$isQuarantineProtected && $threatId !== '') : ?>
                            <form class="shieldx-row-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Move this file out of the site into SHIELD-X quarantine?', 'xerotact-shield-x-security')); ?>');">
                                <input type="hidden" name="action" value="shieldx_quarantine_scan_issue">
                                <input type="hidden" name="threat_id" value="<?php echo esc_attr($threatId); ?>">
                                <?php wp_nonce_field('shieldx_quarantine_scan_issue'); ?>
                                <button type="submit" class="button button-small shieldx-row-action-button shieldx-button-danger"><?php echo esc_html__('Quarantine', 'xerotact-shield-x-security'); ?></button>
                            </form>
                        <?php elseif ($isQuarantineProtected) : ?>
                            <span class="shieldx-muted" title="<?php echo esc_attr__('Critical WordPress runtime files must be repaired or replaced, not quarantined.', 'xerotact-shield-x-security'); ?>">
                                <?php echo esc_html__('Protected file', 'xerotact-shield-x-security'); ?>
                            </span>
                        <?php endif; ?>
                        <?php $this->renderIssueIgnoreActions($threatId, $path, $ruleId); ?>
                    </div>
                <?php elseif ($isLocalFileIssue) : ?>
                    <div class="shieldx-row-actions">
                        <span class="shieldx-muted"><?php echo esc_html__('File no longer exists', 'xerotact-shield-x-security'); ?></span>
                        <?php $this->renderIssueIgnoreActions($threatId, $path, $ruleId); ?>
                    </div>
                <?php elseif ($isDatabaseIssue) : ?>
                    <div class="shieldx-row-actions">
                        <a class="button button-small" href="<?php echo esc_url($this->databaseReviewUrl($threatId)); ?>">
                            <?php echo esc_html__('Review DB', 'xerotact-shield-x-security'); ?>
                        </a>
                        <?php $this->renderScanIssueExtensionActions($issue, 'scanner_row'); ?>
                        <?php $this->renderDatabaseIssueActions($issue); ?>
                        <?php $this->renderIssueIgnoreActions($threatId, $path, $ruleId); ?>
                    </div>
                <?php elseif ($isVirtualIssue) : ?>
                    <div class="shieldx-row-actions">
                        <a class="button button-small" href="<?php echo esc_url($this->virtualIssueReviewUrl($threatId, $path)); ?>">
                            <?php echo esc_html__('Review advisory', 'xerotact-shield-x-security'); ?>
                        </a>
                        <?php if ($this->virtualIssueHasUpdatesPage($path)) : ?>
                            <a class="button button-small" href="<?php echo esc_url(admin_url('update-core.php')); ?>">
                                <?php echo esc_html__('Open updates', 'xerotact-shield-x-security'); ?>
                            </a>
                        <?php endif; ?>
                        <?php $this->renderIssueIgnoreActions($threatId, $path, $ruleId); ?>
                    </div>
                <?php else : ?>
                    <div class="shieldx-row-actions">
                        <span class="shieldx-muted"><?php echo esc_html__('No file', 'xerotact-shield-x-security'); ?></span>
                        <?php $this->renderIssueIgnoreActions($threatId, $path, $ruleId); ?>
                    </div>
                <?php endif; ?>
            </td>
        </tr>
        <?php
    }

    private function isVirtualScannerPath(string $path): bool
    {
        return strpos($path, 'plugin:') === 0
            || strpos($path, 'theme:') === 0
            || strpos($path, 'core:') === 0
            || strpos($path, 'php:') === 0
            || strpos($path, 'software:') === 0;
    }

    /**
     * @param array<string,mixed> $issue
     */
    private function renderScanIssueExtensionActions(array $issue, string $context): void
    {
        if (empty($issue) || !function_exists('apply_filters')) {
            return;
        }
        $actions = apply_filters('shieldx_scan_issue_actions', [], $issue, $context);
        if (!is_array($actions)) {
            return;
        }
        foreach ($actions as $action) {
            if (!is_array($action)) {
                continue;
            }
            $label = isset($action['label']) && is_scalar($action['label']) ? trim((string) $action['label']) : '';
            $url = isset($action['url']) && is_scalar($action['url']) ? (string) $action['url'] : '';
            if ($label === '' || $url === '') {
                continue;
            }
            $class = isset($action['class']) && is_scalar($action['class']) ? (string) $action['class'] : 'button button-small';
            $class = preg_replace('/[^A-Za-z0-9_\-\s]/', '', $class);
            ?>
            <a class="<?php echo esc_attr($class !== '' ? $class : 'button button-small'); ?>" href="<?php echo esc_url($url); ?>">
                <?php echo esc_html($label); ?>
            </a>
            <?php
        }
    }

    /**
     * @param array<int,array<string,mixed>> $issues
     * @return array<string,mixed>
     */
    private function scanIssueByThreatId(array $issues, string $threatId): array
    {
        foreach ($issues as $issue) {
            if ((string) ($issue['threat_id'] ?? '') === $threatId) {
                return $issue;
            }
        }

        return [];
    }

    private function renderIssueIgnoreActions(string $threatId, string $path, string $ruleId): void
    {
        if ($threatId === '') {
            return;
        }
        ?>
        <form class="shieldx-row-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
            <input type="hidden" name="action" value="shieldx_ignore_scan_issue">
            <input type="hidden" name="threat_id" value="<?php echo esc_attr($threatId); ?>">
            <input type="hidden" name="ignore_scope" value="once">
            <?php wp_nonce_field('shieldx_ignore_scan_issue'); ?>
            <button type="submit" class="button button-small shieldx-row-action-button"><?php echo esc_html__('Ignore once', 'xerotact-shield-x-security'); ?></button>
        </form>
        <?php if ($path !== '' && strpos($path, 'db:') !== 0) : ?>
            <form class="shieldx-row-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                <input type="hidden" name="action" value="shieldx_ignore_scan_issue">
                <input type="hidden" name="threat_id" value="<?php echo esc_attr($threatId); ?>">
                <input type="hidden" name="ignore_scope" value="file">
                <?php wp_nonce_field('shieldx_ignore_scan_issue'); ?>
                <button type="submit" class="button button-small shieldx-row-action-button"><?php echo esc_html($this->isVirtualScannerPath($path) ? __('Ignore item', 'xerotact-shield-x-security') : __('Ignore file', 'xerotact-shield-x-security')); ?></button>
            </form>
        <?php endif; ?>
        <?php if ($ruleId !== '') : ?>
            <form class="shieldx-row-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                <input type="hidden" name="action" value="shieldx_ignore_scan_issue">
                <input type="hidden" name="threat_id" value="<?php echo esc_attr($threatId); ?>">
                <input type="hidden" name="ignore_scope" value="rule">
                <?php wp_nonce_field('shieldx_ignore_scan_issue'); ?>
                <button type="submit" class="button button-small shieldx-row-action-button"><?php echo esc_html__('Ignore rule', 'xerotact-shield-x-security'); ?></button>
            </form>
        <?php endif; ?>
        <?php
    }

    /**
     * @param array<int,array<string,mixed>> $quarantinedIssues
     * @param array<string,mixed> $ignored
     * @param array<int,array<string,mixed>> $quarantineHistory
     */
    private function renderScannerRemediationPanel(array $quarantinedIssues, array $ignored, array $quarantineHistory): void
    {
        $ignoredPaths = isset($ignored['paths']) && is_array($ignored['paths']) ? array_values(array_map('strval', $ignored['paths'])) : [];
        $ignoredRules = isset($ignored['rules']) && is_array($ignored['rules']) ? array_values(array_map('strval', $ignored['rules'])) : [];
        ?>
        <div class="shieldx-remediation-grid shieldx-remediation-grid--scanner">
            <div class="shieldx-remediation-box">
                <h3><?php echo esc_html__('Quarantined Files', 'xerotact-shield-x-security'); ?></h3>
                <?php if (empty($quarantinedIssues)) : ?>
                    <p class="shieldx-empty"><?php echo esc_html__('No files are currently in SHIELD-X quarantine.', 'xerotact-shield-x-security'); ?></p>
                <?php else : ?>
                    <div class="shieldx-remediation-list">
                        <?php foreach ($quarantinedIssues as $issue) : ?>
                            <?php $this->renderQuarantinedIssue((array) $issue); ?>
                        <?php endforeach; ?>
                    </div>
                <?php endif; ?>
            </div>
            <div class="shieldx-remediation-box">
                <h3><?php echo esc_html__('Ignored Scanner Items', 'xerotact-shield-x-security'); ?></h3>
                <?php if (empty($ignoredPaths) && empty($ignoredRules)) : ?>
                    <p class="shieldx-empty"><?php echo esc_html__('No persistent scanner ignores are configured.', 'xerotact-shield-x-security'); ?></p>
                <?php else : ?>
                    <div class="shieldx-ignore-cloud">
                        <?php foreach ($ignoredPaths as $path) : ?>
                            <?php $this->renderIgnoredScannerItem('path', $path); ?>
                        <?php endforeach; ?>
                        <?php foreach ($ignoredRules as $rule) : ?>
                            <?php $this->renderIgnoredScannerItem('rule', $rule); ?>
                        <?php endforeach; ?>
                    </div>
                <?php endif; ?>
            </div>
            <div class="shieldx-remediation-box">
                <h3><?php echo esc_html__('Quarantine & Restore History', 'xerotact-shield-x-security'); ?></h3>
                <?php if (empty($quarantineHistory)) : ?>
                    <p class="shieldx-empty"><?php echo esc_html__('No quarantine or restore attempts have been recorded yet.', 'xerotact-shield-x-security'); ?></p>
                <?php else : ?>
                    <div class="shieldx-remediation-list">
                        <?php foreach ($quarantineHistory as $item) : ?>
                            <?php $this->renderQuarantineHistoryItem(is_array($item) ? $item : []); ?>
                        <?php endforeach; ?>
                    </div>
                <?php endif; ?>
            </div>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $item
     */
    private function renderQuarantineHistoryItem(array $item, bool $showPath = true): void
    {
        $path = (string) ($item['original_path'] ?? '');
        $event = (string) ($item['last_event'] ?? ($item['status'] ?? 'quarantined'));
        $eventAt = (string) ($item['last_event_at'] ?? ($item['quarantined_at'] ?? ''));
        $message = (string) ($item['last_message'] ?? '');
        ?>
        <div class="shieldx-file-context-item shieldx-file-context-item--history">
            <div>
                <strong><?php echo esc_html($this->formatLabel($event)); ?></strong>
                <span class="<?php echo esc_attr('shieldx-severity shieldx-severity--' . strtolower((string) ($item['severity'] ?? 'medium'))); ?>"><?php echo esc_html($this->formatLabel((string) ($item['severity'] ?? 'medium'))); ?></span>
            </div>
            <?php if ($showPath && $path !== '') : ?>
                <code><?php echo esc_html($path); ?></code>
            <?php endif; ?>
            <small><?php echo esc_html($this->formatTime($eventAt)); ?></small>
            <?php if ($message !== '') : ?>
                <p><?php echo esc_html($message); ?></p>
            <?php endif; ?>
        </div>
        <?php
    }

    private function renderIgnoredScannerItem(string $type, string $value): void
    {
        $type = $type === 'rule' ? 'rule' : 'path';
        ?>
        <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="shieldx-ignore-pill">
            <input type="hidden" name="action" value="shieldx_remove_scan_ignore">
            <input type="hidden" name="ignore_type" value="<?php echo esc_attr($type); ?>">
            <input type="hidden" name="ignore_value" value="<?php echo esc_attr($value); ?>">
            <?php wp_nonce_field('shieldx_remove_scan_ignore'); ?>
            <code><?php echo esc_html($value); ?></code>
            <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
            <button type="submit" aria-label="<?php echo esc_attr(sprintf(__('Remove scanner ignore for %s', 'xerotact-shield-x-security'), $value)); ?>">
                <?php echo esc_html_x('x', 'remove ignored scanner item', 'xerotact-shield-x-security'); ?>
            </button>
        </form>
        <?php
    }

    /**
     * @param array<int,mixed> $items
     * @return array<int,array<string,mixed>>
     */
    private function filterShadowChangedItems(array $items): array
    {
        $changed = [];

        foreach ($items as $item) {
            if (!is_array($item)) {
                continue;
            }

            $path = (string) ($item['path'] ?? '');
            $trustedHash = (string) ($item['trusted_hash'] ?? $item['previous_hash'] ?? '');
            $currentHash = (string) ($item['current_hash'] ?? '');
            if ($path === '' || $trustedHash === '' || $currentHash === '' || $trustedHash === $currentHash) {
                continue;
            }

            $item['trusted_hash'] = $trustedHash;
            $item['current_hash'] = $currentHash;
            $item['status'] = (string) ($item['status'] ?? 'pending');
            $changed[] = $item;
        }

        return $changed;
    }

    /**
     * @param array<int,array<string,mixed>> $items
     * @param array<int,array<string,mixed>> $recentScans
     * @return array<int,array{scan_id:string,scan:array<string,mixed>,items:array<int,array<string,mixed>>,changed_count:int}>
     */
    private function shadowScanHistorySessions(array $items, string $excludeScanId, array $recentScans = []): array
    {
        $excludeScanId = $this->sanitizeShadowScanId($excludeScanId);
        $groups = [];

        foreach ($items as $item) {
            $scanId = $this->sanitizeShadowScanId((string) ($item['last_scan_id'] ?? ''));
            if ($scanId !== '' && $scanId === $excludeScanId) {
                continue;
            }

            $key = $scanId !== '' ? $scanId : 'pending';
            if (!isset($groups[$key])) {
                $groups[$key] = [
                    'scan_id' => $scanId,
                    'scan' => [],
                    'items' => [],
                    'changed_count' => 0,
                    'sort' => 0,
                    'scan_started_at' => '',
                ];
            }

            $groups[$key]['items'][] = $item;
            $groups[$key]['changed_count'] = (int) ($groups[$key]['changed_count'] ?? 0) + 1;
            $timestamp = strtotime((string) ($item['last_seen_at'] ?? $item['changed_at'] ?? '')) ?: 0;
            $groups[$key]['sort'] = max((int) $groups[$key]['sort'], $timestamp);
            if ((string) $groups[$key]['scan_started_at'] === '' && (string) ($item['last_scan_at'] ?? '') !== '') {
                $groups[$key]['scan_started_at'] = (string) $item['last_scan_at'];
            }
        }

        foreach ($recentScans as $scan) {
            if (!is_array($scan)) {
                continue;
            }

            $scanId = $this->sanitizeShadowScanId((string) ($scan['scan_id'] ?? ''));
            if ($scanId === '' || $scanId === $excludeScanId) {
                continue;
            }

            if (!isset($groups[$scanId])) {
                $groups[$scanId] = [
                    'scan_id' => $scanId,
                    'scan' => $scan,
                    'items' => [],
                    'changed_count' => 0,
                    'sort' => 0,
                    'scan_started_at' => (string) ($scan['started_at'] ?? ''),
                ];
            } else {
                $groups[$scanId]['scan'] = $scan;
            }

            $scanItems = isset($scan['shadow_changed_items']) && is_array($scan['shadow_changed_items'])
                ? $this->filterShadowChangedItems($scan['shadow_changed_items'])
                : [];
            $groups[$scanId]['changed_count'] = max(
                (int) ($groups[$scanId]['changed_count'] ?? 0),
                count($scanItems),
                (int) ($scan['shadow_changes_pending'] ?? 0),
                (int) ($scan['shadow_changed_count'] ?? 0)
            );

            $timestamp = strtotime((string) ($scan['completed_at'] ?? $scan['started_at'] ?? '')) ?: 0;
            $groups[$scanId]['sort'] = max((int) $groups[$scanId]['sort'], $timestamp);
        }

        uasort($groups, static function (array $left, array $right): int {
            return ((int) ($right['sort'] ?? 0)) <=> ((int) ($left['sort'] ?? 0));
        });

        $repository = ScanRepository::fromWordPress();
        $sessions = [];
        foreach (array_slice($groups, 0, 12, true) as $group) {
            $scanId = (string) ($group['scan_id'] ?? '');
            $scan = isset($group['scan']) && is_array($group['scan']) && !empty($group['scan']) ? $group['scan'] : ($scanId !== '' ? $repository->scanById($scanId) : null);
            if (!is_array($scan)) {
                $scan = [];
            }

            if (!isset($scan['scan_id'])) {
                $scan['scan_id'] = $scanId;
            }
            if (!isset($scan['scope_label'])) {
                $scan['scope_label'] = __('Previous shadow scan', 'xerotact-shield-x-security');
            }
            if (!isset($scan['started_at']) && (string) ($group['scan_started_at'] ?? '') !== '') {
                $scan['started_at'] = (string) $group['scan_started_at'];
            }

            $sessions[] = [
                'scan_id' => $scanId,
                'scan' => $scan,
                'items' => isset($group['items']) && is_array($group['items']) ? array_values($group['items']) : [],
                'changed_count' => max(
                    count(isset($group['items']) && is_array($group['items']) ? $group['items'] : []),
                    (int) ($group['changed_count'] ?? 0)
                ),
            ];
        }

        return $sessions;
    }

    private function sanitizeShadowScanId(string $scanId): string
    {
        $scanId = strtolower(trim($scanId));

        return preg_match('/^[a-f0-9]{32}$/', $scanId) === 1 ? $scanId : '';
    }

    /**
     * @param array<string,mixed> $compare
     */
    private function renderShadowCompare(array $compare): void
    {
        $error = (string) ($compare['error'] ?? '');
        $path = (string) ($compare['path'] ?? '');

        if ($error !== '') {
            ?>
            <div id="shieldx-shadow-compare" class="shieldx-core-compare shieldx-core-compare--error">
                <h3><?php echo esc_html($path !== '' ? $path : __('Shadow compare', 'xerotact-shield-x-security')); ?></h3>
                <p><?php echo esc_html($error); ?></p>
            </div>
            <?php
            return;
        }

        $currentExists = !empty($compare['current_exists']);
        $shadowExists = !empty($compare['shadow_exists']);
        $matches = !empty($compare['matches']);
        ?>
        <div id="shieldx-shadow-compare" class="shieldx-core-compare">
            <div class="shieldx-core-compare-head">
                <div>
                    <h3><?php echo esc_html($path); ?></h3>
                    <p>
                        <?php
                        if (!$shadowExists) {
                            echo esc_html__('No trusted shadow copy exists for this file yet.', 'xerotact-shield-x-security');
                        } elseif (!$currentExists) {
                            echo esc_html__('The current site file is missing. Compare can only show the trusted shadow metadata.', 'xerotact-shield-x-security');
                        } elseif ($matches) {
                            echo esc_html__('Current file matches the trusted SHIELD-X shadow copy.', 'xerotact-shield-x-security');
                        } else {
                            echo esc_html__('Current file differs from the trusted SHIELD-X shadow copy.', 'xerotact-shield-x-security');
                        }
                        ?>
                    </p>
                </div>
                <div class="shieldx-core-compare-actions">
                    <a class="button button-secondary" href="<?php echo esc_url($this->closeShadowCompareUrl()); ?>">
                        <?php echo esc_html__('Close compare', 'xerotact-shield-x-security'); ?>
                    </a>
                </div>
            </div>
            <div class="shieldx-core-hashes">
                <span><strong><?php echo esc_html__('Current', 'xerotact-shield-x-security'); ?></strong> <code><?php echo esc_html(substr((string) ($compare['current_hash'] ?? ''), 0, 16)); ?></code> <?php echo esc_html((string) ($compare['current_size'] ?? 0) . ' bytes'); ?></span>
                <span><strong><?php echo esc_html__('Trusted shadow', 'xerotact-shield-x-security'); ?></strong> <code><?php echo esc_html(substr((string) ($compare['shadow_hash'] ?? ''), 0, 16)); ?></code> <?php echo esc_html((string) ($compare['shadow_size'] ?? 0) . ' bytes'); ?></span>
                <?php if (!empty($compare['first_changed_line'])) : ?>
                    <?php
                    $firstChangedLineLabel = sprintf(
                        /* translators: %d: first changed line number between the current file and trusted shadow copy. */
                        __('Shadow line %d', 'xerotact-shield-x-security'),
                        (int) $compare['first_changed_line']
                    );
                    ?>
                    <span><strong><?php echo esc_html__('First change', 'xerotact-shield-x-security'); ?></strong> <?php echo esc_html($firstChangedLineLabel); ?></span>
                <?php endif; ?>
            </div>
            <?php if (!empty($compare['text_comparable'])) : ?>
                <div class="shieldx-diff-grid" data-shieldx-linked-scroll>
                    <div>
                        <h4><?php echo esc_html__('Current file', 'xerotact-shield-x-security'); ?></h4>
                        <pre><?php echo esc_html($this->formatExcerpt((array) ($compare['current_excerpt'] ?? []))); ?></pre>
                    </div>
                    <div>
                        <h4><?php echo esc_html__('Trusted shadow copy', 'xerotact-shield-x-security'); ?></h4>
                        <pre><?php echo esc_html($this->formatExcerpt((array) ($compare['shadow_excerpt'] ?? []))); ?></pre>
                    </div>
                </div>
                <?php if (!empty($compare['excerpt_windowed'])) : ?>
                    <p class="shieldx-muted"><?php echo esc_html__('Preview is windowed for browser performance: first lines plus context around the first changed line are shown.', 'xerotact-shield-x-security'); ?></p>
                <?php endif; ?>
            <?php else : ?>
                <p class="shieldx-empty"><?php echo esc_html__('Binary or missing file preview is skipped; hashes and sizes are shown for comparison.', 'xerotact-shield-x-security'); ?></p>
            <?php endif; ?>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $scan
     * @param array<int,array<string,mixed>> $items
     */
    private function renderShadowScanSessionCard(string $scanId, array $scan, array $items, int $pendingCount = -1, int $changedCount = -1): void
    {
        $itemCount = count($items);
        $pendingCount = $pendingCount >= 0 ? $pendingCount : $itemCount;
        $changedCount = $changedCount >= 0 ? $changedCount : $pendingCount;
        $batchCount = min(25, $itemCount);
        $session = $scanId !== '' ? $scanId : (string) ($scan['scan_id'] ?? '');
        $completedAt = (string) ($scan['completed_at'] ?? $scan['started_at'] ?? '');
        $scope = (string) ($scan['scope_label'] ?? __('Selected folders', 'xerotact-shield-x-security'));
        ?>
        <div class="shieldx-shadow-session-card">
            <div class="shieldx-shadow-session-head">
                <span class="shieldx-shadow-session-icon">
                    <?php echo wp_kses(Iconoir::svg('clock-rotate-right'), $this->allowedIconSvg()); ?>
                </span>
                <div class="shieldx-shadow-session-copy">
                    <div class="shieldx-shadow-session-title">
                        <h3><?php echo esc_html__('Shadow scan session', 'xerotact-shield-x-security'); ?></h3>
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <span><?php echo esc_html(sprintf(_n('%d file changed', '%d files changed', $changedCount, 'xerotact-shield-x-security'), $changedCount)); ?></span>
                    </div>
                    <p>
                        <?php
                        echo esc_html(sprintf(
                            /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                            __('%1$s. Session %2$s%3$s', 'xerotact-shield-x-security'),
                            $scope,
                            $session !== '' ? $session : __('not recorded', 'xerotact-shield-x-security'),
                            $completedAt !== '' ? ' - ' . $this->formatTime($completedAt) : ''
                        ));
                        ?>
                    </p>
                </div>
                <?php if ($itemCount > 0) : ?>
                    <div class="shieldx-shadow-session-actions">
                        <form class="shieldx-inline-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Trust the next reviewed batch of changed files and update their shadow copies?', 'xerotact-shield-x-security')); ?>');">
                            <input type="hidden" name="action" value="shieldx_confirm_shadow_session">
                            <input type="hidden" name="shadow_scan" value="<?php echo esc_attr($session); ?>">
                            <input type="hidden" name="confirm_scope" value="next">
                            <?php wp_nonce_field('shieldx_confirm_shadow_session'); ?>
                            <button type="submit" class="button button-primary button-small" <?php disabled($session === ''); ?>>
                                <?php
                                /* translators: %d: number of files in the next confirmation batch. */
                                echo esc_html(sprintf(__('Accept next %d', 'xerotact-shield-x-security'), $batchCount));
                                ?>
                            </button>
                        </form>
                        <?php if ($pendingCount > $batchCount) : ?>
                            <form class="shieldx-inline-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" data-shieldx-shadow-bulk-form data-shieldx-submit-confirm="<?php echo esc_attr__('Trust every pending changed file in this shadow scan session and update all of their shadow copies?', 'xerotact-shield-x-security'); ?>">
                                <input type="hidden" name="action" value="shieldx_confirm_shadow_session">
                                <input type="hidden" name="shadow_scan" value="<?php echo esc_attr($session); ?>">
                                <input type="hidden" name="confirm_scope" value="all">
                                <?php wp_nonce_field('shieldx_confirm_shadow_session'); ?>
                                <button type="submit" class="button button-secondary button-small" data-running-label="<?php echo esc_attr__('Accepting files...', 'xerotact-shield-x-security'); ?>" <?php disabled($session === ''); ?>>
                                    <?php echo esc_html__('Accept all', 'xerotact-shield-x-security'); ?>
                                </button>
                            </form>
                        <?php endif; ?>
                    </div>
                <?php endif; ?>
            </div>
            <details class="shieldx-shadow-session-review" open>
                <summary>
                    <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                    <span><?php echo esc_html($pendingCount > 0 ? sprintf(_n('Review %d changed file', 'Review %d changed files', $pendingCount, 'xerotact-shield-x-security'), $pendingCount) : __('Review complete', 'xerotact-shield-x-security')); ?></span>
                    <small><?php echo esc_html($pendingCount > $batchCount ? __('Restore individual files, confirm them one by one, or accept the full session.', 'xerotact-shield-x-security') : ($pendingCount > 0 ? __('Restore individual files or confirm them one by one.', 'xerotact-shield-x-security') : __('No changed files remain pending in this session.', 'xerotact-shield-x-security'))); ?></small>
                </summary>
                <?php if (empty($items)) : ?>
                    <p class="shieldx-empty">
                        <?php echo esc_html($changedCount > 0
                            ? __('No changed files remain pending in this session.', 'xerotact-shield-x-security')
                            : __('No file changes were detected in this shadow scan session.', 'xerotact-shield-x-security')); ?>
                    </p>
                <?php else : ?>
                    <div class="shieldx-remediation-list">
                        <?php foreach ($items as $item) : ?>
                            <?php $this->renderShadowCopyItem($item, true); ?>
                        <?php endforeach; ?>
                    </div>
                <?php endif; ?>
            </details>
        </div>
        <?php
    }

    /**
     * @param array<int,array<string,mixed>> $fileScanScopes
     */
    private function renderShadowCopyActions(array $fileScanScopes): void
    {
        ?>
        <div class="shieldx-section-head-actions">
            <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="shieldx-inline-action-form shieldx-shadow-scan-form" data-shieldx-shadow-scan-form data-shieldx-progress-poll-delay="150" data-shieldx-progress-refresh-selector="#shieldx-file-shadow-copies" data-shieldx-progress-refresh-url="<?php echo esc_url(admin_url('admin.php?page=shield-x-backup')); ?>" data-shieldx-progress-running-title="<?php echo esc_attr__('Shadow scan in progress', 'xerotact-shield-x-security'); ?>" data-shieldx-progress-running-intro="<?php echo esc_attr__('SHIELD-X is checking file metadata in small batches, hashing only changed candidates, then listing real shadow-copy changes.', 'xerotact-shield-x-security'); ?>" data-shieldx-progress-complete-title="<?php echo esc_attr__('Shadow scan complete', 'xerotact-shield-x-security'); ?>" data-shieldx-progress-complete-intro="<?php echo esc_attr__('The shadow scan finished. Review changed files or confirm trusted changes from Backup.', 'xerotact-shield-x-security'); ?>">
                <input type="hidden" name="action" value="shieldx_run_scan">
                <input type="hidden" name="redirect_page" value="shield-x-backup">
                <input type="hidden" name="scan_mode" value="shadow">
                <?php foreach ($fileScanScopes as $scope) : ?>
                    <?php if (isset($scope['id'])) : ?>
                        <input type="hidden" name="scan_scopes[]" value="<?php echo esc_attr((string) $scope['id']); ?>">
                    <?php endif; ?>
                <?php endforeach; ?>
                <?php wp_nonce_field('shieldx_run_scan'); ?>
                <button type="submit" class="button button-primary button-small" data-shieldx-shadow-scan-button data-running-label="<?php echo esc_attr__('Scanning...', 'xerotact-shield-x-security'); ?>" <?php disabled(empty($fileScanScopes)); ?>>
                    <?php echo esc_html__('Run shadow scan', 'xerotact-shield-x-security'); ?>
                </button>
            </form>
            <a class="button button-small" href="<?php echo esc_url(admin_url('admin.php?page=shield-x-scan#shieldx-file-scanner')); ?>">
                <?php echo esc_html__('Choose folders', 'xerotact-shield-x-security'); ?>
            </a>
            <div class="shieldx-shadow-progress" data-shieldx-shadow-progress hidden aria-live="polite">
                <span class="shieldx-shadow-progress-track" role="progressbar" aria-label="<?php echo esc_attr__('Shadow scan progress', 'xerotact-shield-x-security'); ?>">
                    <span></span>
                </span>
                <small><?php echo esc_html__('Checking metadata first, hashing only changed files, then updating shadow-copy review.', 'xerotact-shield-x-security'); ?></small>
            </div>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $item
     */
    private function renderShadowCopyItem(array $item, bool $pendingOnly): void
    {
        $path = (string) ($item['path'] ?? '');
        $status = (string) ($item['status'] ?? 'trusted');
        $trustedAt = (string) ($item['trusted_at'] ?? '');
        $changedAt = (string) ($item['changed_at'] ?? '');
        $lastSeenAt = (string) ($item['last_seen_at'] ?? '');
        $trustedHash = (string) ($item['trusted_hash'] ?? $item['previous_hash'] ?? '');
        $currentHash = (string) ($item['current_hash'] ?? '');
        $hasShadow = !empty($item['shadow_available']);
        if ($path === '') {
            return;
        }
        ?>
        <div class="shieldx-remediation-item shieldx-remediation-item--shadow">
            <div>
                <strong><?php echo esc_html($this->formatLabel($status)); ?></strong>
                <code><?php echo esc_html($path); ?></code>
                <span>
                    <?php
                    echo esc_html(sprintf(
                        /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                        __('Trusted %1$s / current %2$s', 'xerotact-shield-x-security'),
                        substr($trustedHash, 0, 12),
                        substr($currentHash, 0, 12)
                    ));
                    ?>
                </span>
                <span>
                    <?php
                    echo esc_html($status === 'pending' && $changedAt !== ''
                        /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                        ? sprintf(__('Changed %s', 'xerotact-shield-x-security'), $this->formatTime($changedAt))
                        /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                        : sprintf(__('Trusted %s', 'xerotact-shield-x-security'), $this->formatTime($trustedAt !== '' ? $trustedAt : $lastSeenAt)));
                    ?>
                </span>
                <?php if (!$hasShadow) : ?>
                    <span class="shieldx-muted"><?php echo esc_html__('No previous shadow file exists yet. Confirming this file will seed the shadow copy.', 'xerotact-shield-x-security'); ?></span>
                <?php endif; ?>
            </div>
            <div class="shieldx-row-actions">
                <?php if ($hasShadow) : ?>
                    <a class="button button-small shieldx-row-action-button" href="<?php echo esc_url($this->shadowCompareUrl($path, (string) ($item['last_scan_id'] ?? ''))); ?>">
                        <?php echo esc_html__('Compare', 'xerotact-shield-x-security'); ?>
                    </a>
                <?php endif; ?>
                <?php
                $shadowActions = function_exists('apply_filters') ? apply_filters('shieldx_shadow_copy_actions', [], [
                    'path' => $path,
                    'status' => $status,
                    'trusted_hash' => $trustedHash,
                    'current_hash' => $currentHash,
                    'shadow_available' => $hasShadow,
                    'last_scan_id' => (string) ($item['last_scan_id'] ?? ''),
                ], $pendingOnly ? 'shadow_session' : 'shadow_history') : [];
                ?>
                <?php foreach (is_array($shadowActions) ? $shadowActions : [] as $shadowAction) : ?>
                    <?php
                    $shadowAction = is_array($shadowAction) ? $shadowAction : [];
                    $shadowActionLabel = isset($shadowAction['label']) && is_scalar($shadowAction['label']) ? (string) $shadowAction['label'] : '';
                    $shadowActionUrl = isset($shadowAction['url']) && is_scalar($shadowAction['url']) ? (string) $shadowAction['url'] : '';
                    $shadowActionClass = isset($shadowAction['class']) && is_scalar($shadowAction['class']) ? (string) $shadowAction['class'] : 'button button-small shieldx-row-action-button';
                    ?>
                    <?php if ($shadowActionLabel !== '' && $shadowActionUrl !== '') : ?>
                        <a class="<?php echo esc_attr($shadowActionClass); ?>" href="<?php echo esc_url($shadowActionUrl); ?>">
                            <?php echo esc_html($shadowActionLabel); ?>
                        </a>
                    <?php endif; ?>
                <?php endforeach; ?>
                <?php if ($hasShadow && $status === 'pending') : ?>
                    <form class="shieldx-row-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Restore this file from its previous trusted SHIELD-X shadow copy?', 'xerotact-shield-x-security')); ?>');">
                        <input type="hidden" name="action" value="shieldx_restore_shadow_copy">
                        <input type="hidden" name="file_path" value="<?php echo esc_attr($path); ?>">
                        <?php wp_nonce_field('shieldx_restore_shadow_copy'); ?>
                        <button type="submit" class="button button-small shieldx-row-action-button shieldx-button-warning"><?php echo esc_html__('Restore shadow', 'xerotact-shield-x-security'); ?></button>
                    </form>
                <?php endif; ?>
                <?php if ($status === 'pending' || !$pendingOnly) : ?>
                    <form class="shieldx-row-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Trust the current file and update its SHIELD-X shadow copy?', 'xerotact-shield-x-security')); ?>');">
                        <input type="hidden" name="action" value="shieldx_confirm_shadow_copy">
                        <input type="hidden" name="file_path" value="<?php echo esc_attr($path); ?>">
                        <?php wp_nonce_field('shieldx_confirm_shadow_copy'); ?>
                        <button type="submit" class="button button-small shieldx-row-action-button"><?php echo esc_html__('Confirm current', 'xerotact-shield-x-security'); ?></button>
                    </form>
                <?php endif; ?>
            </div>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $backup
     */
    private function renderManualDatabaseBackupItem(array $backup): void
    {
        $backupId = (string) ($backup['backup_id'] ?? '');
        $createdAt = (string) ($backup['created_at'] ?? '');
        $details = sprintf(
            /* translators: 1: database table count, 2: exported row count, 3: formatted backup file size. */
            __('%1$d tables / %2$d rows / %3$s', 'xerotact-shield-x-security'),
            (int) ($backup['table_count'] ?? 0),
            (int) ($backup['rows_exported'] ?? 0),
            $this->formatBytes((int) ($backup['size'] ?? 0))
        );
        ?>
        <div class="shieldx-remediation-item shieldx-remediation-item--db">
            <div>
                <strong><?php echo esc_html__('Manual database backup', 'xerotact-shield-x-security'); ?></strong>
                <code><?php echo esc_html($backupId); ?></code>
                <span><?php echo esc_html($details); ?></span>
                <?php $this->renderDatabaseBackupWarnings($backup); ?>
                <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                <span><?php echo esc_html(sprintf(__('Created %s', 'xerotact-shield-x-security'), $this->formatTime($createdAt))); ?></span>
                <?php $this->renderStorageBadges($backup); ?>
            </div>
            <?php if ($backupId !== '') : ?>
                <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Restore the database from this SHIELD-X backup? A restore point will be created first.', 'xerotact-shield-x-security')); ?>');">
                    <input type="hidden" name="action" value="shieldx_restore_manual_db_backup">
                    <input type="hidden" name="backup_id" value="<?php echo esc_attr($backupId); ?>">
                    <?php wp_nonce_field('shieldx_restore_manual_db_backup'); ?>
                    <button type="submit" class="button button-small shieldx-row-action-button shieldx-button-warning"><?php echo esc_html__('Restore database', 'xerotact-shield-x-security'); ?></button>
                </form>
            <?php endif; ?>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $backup
     */
    private function renderDatabaseBackupWarnings(array $backup): void
    {
        $warningCount = max(0, (int) ($backup['warning_count'] ?? 0));
        $warnings = [];
        foreach ((array) ($backup['warning_details'] ?? []) as $warning) {
            $warning = trim((string) $warning);
            if ($warning !== '') {
                $warnings[] = $warning;
            }
        }

        if ($warningCount <= 0 && empty($warnings)) {
            return;
        }

        if (empty($warnings)) {
            $warnings[] = sprintf(
                /* translators: %d: number of database backup warnings. */
                _n('%d database backup warning was recorded.', '%d database backup warnings were recorded.', max(1, $warningCount), 'xerotact-shield-x-security'),
                max(1, $warningCount)
            );
        }
        ?>
        <span class="shieldx-muted"><?php echo esc_html(sprintf(
            /* translators: %d: number of database backup warnings. */
            _n('Warnings: %d', 'Warnings: %d', max(1, $warningCount ?: count($warnings)), 'xerotact-shield-x-security'),
            max(1, $warningCount ?: count($warnings))
        )); ?></span>
        <ul class="shieldx-inline-list shieldx-database-backup-warnings">
            <?php foreach (array_slice($warnings, 0, 5) as $warning) : ?>
                <li><?php echo esc_html($warning); ?></li>
            <?php endforeach; ?>
        </ul>
        <?php
    }

    /**
     * @param array<string,mixed> $backup
     */
    private function renderDatabaseBackupItem(array $backup): void
    {
        $backupId = (string) ($backup['backup_id'] ?? '');
        $path = (string) ($backup['path'] ?? '');
        $action = (string) ($backup['action'] ?? '');
        $createdAt = (string) ($backup['created_at'] ?? '');
        $restoredAt = (string) ($backup['restored_at'] ?? '');
        ?>
        <div class="shieldx-remediation-item shieldx-remediation-item--db">
            <div>
                <strong><?php echo esc_html($this->formatLabel($action !== '' ? $action : __('Database backup', 'xerotact-shield-x-security'))); ?></strong>
                <code><?php echo esc_html($path !== '' ? $path : $backupId); ?></code>
                <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                <span><?php echo esc_html(sprintf(__('Backed up %s', 'xerotact-shield-x-security'), $this->formatTime($createdAt))); ?></span>
                <?php $this->renderDatabaseBackupWarnings($backup); ?>
                <?php if ($restoredAt !== '') : ?>
                    <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                    <span><?php echo esc_html(sprintf(__('Restored %s', 'xerotact-shield-x-security'), $this->formatTime($restoredAt))); ?></span>
                <?php endif; ?>
            </div>
            <?php if ($backupId !== '' && $restoredAt === '') : ?>
                <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Restore this database row from its SHIELD-X backup?', 'xerotact-shield-x-security')); ?>');">
                    <input type="hidden" name="action" value="shieldx_restore_db_backup">
                    <input type="hidden" name="backup_id" value="<?php echo esc_attr($backupId); ?>">
                    <?php wp_nonce_field('shieldx_restore_db_backup'); ?>
                    <button type="submit" class="button button-small shieldx-row-action-button"><?php echo esc_html__('Restore', 'xerotact-shield-x-security'); ?></button>
                </form>
            <?php else : ?>
                <span class="shieldx-muted"><?php echo esc_html__('Restored', 'xerotact-shield-x-security'); ?></span>
            <?php endif; ?>
        </div>
        <?php
    }

    /**
     * @param array<int,array<string,mixed>> $restorePoints
     */
    private function renderRestorePointPanel(array $restorePoints, ?array $restorePointDetail = null, string $requestedRestoreId = '', ?array $restorePointPreview = null, string $previewPath = ''): void
    {
        $restorePointCleanupDefault = gmdate('Y-m-d', time() - (30 * 86400));
        ?>
        <div class="shieldx-remediation-grid shieldx-remediation-grid--restore">
            <div class="shieldx-remediation-box">
                <h3><?php echo esc_html__('Create Restore Point', 'xerotact-shield-x-security'); ?></h3>
                <p class="shieldx-muted"><?php echo esc_html__('Creates a full baseline first. Later restore points are incremental, storing only changed, new, or deleted files while keeping a SHA-256 index for comparison.', 'xerotact-shield-x-security'); ?></p>
                <div class="shieldx-restore-point-create-actions">
                    <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="shieldx-form-actions" data-shieldx-backup-progress-form data-shieldx-progress-poll-delay="150" data-shieldx-progress-refresh-selector="#shieldx-restore-points-list" data-shieldx-progress-refresh-url="<?php echo esc_url(admin_url('admin.php?page=shield-x-backup')); ?>" data-shieldx-progress-running-title="<?php echo esc_attr__('Restore point in progress', 'xerotact-shield-x-security'); ?>" data-shieldx-progress-running-intro="<?php echo esc_attr__('SHIELD-X is building this restore point in small batches so large sites stay below timeout limits.', 'xerotact-shield-x-security'); ?>" data-shieldx-progress-complete-title="<?php echo esc_attr__('Restore point complete', 'xerotact-shield-x-security'); ?>" data-shieldx-progress-complete-intro="<?php echo esc_attr__('The restore point finished. Open Backup to review it.', 'xerotact-shield-x-security'); ?>">
                        <input type="hidden" name="action" value="shieldx_create_restore_point">
                        <?php wp_nonce_field('shieldx_create_restore_point'); ?>
                        <input type="hidden" name="restore_reason" value="manual">
                        <button type="submit" class="button button-primary" data-shieldx-backup-progress-button data-running-label="<?php echo esc_attr__('Creating restore point...', 'xerotact-shield-x-security'); ?>"><?php echo esc_html__('Create restore point', 'xerotact-shield-x-security'); ?></button>
                    </form>
                    <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="shieldx-form-actions" data-shieldx-backup-progress-form data-shieldx-progress-poll-delay="150" data-shieldx-progress-refresh-selector="#shieldx-restore-points-list" data-shieldx-progress-refresh-url="<?php echo esc_url(admin_url('admin.php?page=shield-x-backup')); ?>" data-shieldx-progress-running-title="<?php echo esc_attr__('Full backup in progress', 'xerotact-shield-x-security'); ?>" data-shieldx-progress-running-intro="<?php echo esc_attr__('SHIELD-X is capturing every file type selected in Backup policy across the WordPress root and exporting every database row.', 'xerotact-shield-x-security'); ?>" data-shieldx-progress-complete-title="<?php echo esc_attr__('Full backup complete', 'xerotact-shield-x-security'); ?>" data-shieldx-progress-complete-intro="<?php echo esc_attr__('The full-root file and complete database backup finished. Open Backup to review it.', 'xerotact-shield-x-security'); ?>">
                        <input type="hidden" name="action" value="shieldx_create_restore_point">
                        <?php wp_nonce_field('shieldx_create_restore_point'); ?>
                        <input type="hidden" name="restore_reason" value="manual_full">
                        <input type="hidden" name="restore_point_mode" value="full">
                        <button type="submit" class="button button-secondary" data-shieldx-backup-progress-button data-running-label="<?php echo esc_attr__('Creating full backup...', 'xerotact-shield-x-security'); ?>"><?php echo esc_html__('Create full backup', 'xerotact-shield-x-security'); ?></button>
                    </form>
                </div>
                <div class="shieldx-database-backup-cleanup shieldx-restore-point-cleanup">
                    <h3><?php echo esc_html__('Clean Up Restore Points', 'xerotact-shield-x-security'); ?></h3>
                    <p class="shieldx-muted"><?php echo esc_html__('Deletes local restore point files created before the selected date. In-progress restore points and older bases required by newer incrementals are kept. Remote catalog entries and linked database backup files are retained.', 'xerotact-shield-x-security'); ?></p>
                    <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="shieldx-form-actions shieldx-database-backup-cleanup-form" onsubmit="return confirm('<?php echo esc_js(__('Delete local restore point files older than the selected date? This cannot be undone.', 'xerotact-shield-x-security')); ?>');">
                        <input type="hidden" name="action" value="shieldx_delete_restore_points_before">
                        <?php wp_nonce_field('shieldx_delete_restore_points_before'); ?>
                        <label class="shieldx-database-backup-cleanup-date">
                            <span class="shieldx-database-backup-cleanup-label"><?php echo esc_html__('Delete restore points before', 'xerotact-shield-x-security'); ?></span>
                            <input type="date" name="delete_before_date" value="<?php echo esc_attr($restorePointCleanupDefault); ?>" max="<?php echo esc_attr(gmdate('Y-m-d')); ?>" required>
                        </label>
                        <button type="submit" class="button button-secondary"><?php echo esc_html__('Clean restore points', 'xerotact-shield-x-security'); ?></button>
                    </form>
                </div>
            </div>
            <div id="shieldx-restore-points-list" class="shieldx-remediation-box shieldx-remediation-box--wide">
                <h3><?php echo esc_html__('Recent Restore Points', 'xerotact-shield-x-security'); ?></h3>
                <?php if (empty($restorePoints)) : ?>
                    <p class="shieldx-empty"><?php echo esc_html__('No full restore points have been created yet.', 'xerotact-shield-x-security'); ?></p>
                <?php else : ?>
                    <?php $restorePointVisibleLimit = 2; ?>
                    <div class="shieldx-remediation-list shieldx-restore-points-list" data-shieldx-restore-points data-visible-step="<?php echo esc_attr((string) $restorePointVisibleLimit); ?>">
                        <?php foreach ($restorePoints as $restorePointIndex => $restorePoint) : ?>
                            <div data-shieldx-restore-point-item<?php if ($restorePointIndex >= $restorePointVisibleLimit) : ?> hidden<?php endif; ?>>
                                <?php $this->renderRestorePointItem(is_array($restorePoint) ? $restorePoint : []); ?>
                            </div>
                        <?php endforeach; ?>
                        <?php if (count($restorePoints) > $restorePointVisibleLimit) : ?>
                            <div class="shieldx-restore-points-actions">
                                <button type="button" class="button button-secondary button-small shieldx-restore-points-more" data-shieldx-restore-points-more data-label="<?php echo esc_attr__('Load more', 'xerotact-shield-x-security'); ?>" data-collapse-label="<?php echo esc_attr__('Show less', 'xerotact-shield-x-security'); ?>">
                                    <?php echo esc_html__('Load more', 'xerotact-shield-x-security'); ?>
                                </button>
                            </div>
                        <?php endif; ?>
                    </div>
                <?php endif; ?>
            </div>
        </div>
        <?php $this->renderRestorePointDetails($restorePointDetail, $requestedRestoreId, $restorePointPreview, $previewPath); ?>
        <?php
    }

    /**
     * @param array<string,mixed> $restorePoint
     */
    private function renderRestorePointItem(array $restorePoint): void
    {
        $restoreId = (string) ($restorePoint['restore_id'] ?? '');
        $createdAt = (string) ($restorePoint['created_at'] ?? '');
        $restoredAt = (string) ($restorePoint['restored_at'] ?? '');
        $reason = (string) ($restorePoint['reason'] ?? 'manual');
        $snapshotMode = (string) ($restorePoint['snapshot_mode'] ?? 'full');
        $provider = (string) ($restorePoint['storage_provider'] ?? 'local');
        $localAvailable = !array_key_exists('local_exists', $restorePoint) || !empty($restorePoint['local_exists']);
        $googleAvailable = in_array($provider, ['google', 'both'], true) && (string) ($restorePoint['remote_status'] ?? '') === 'uploaded';
        $recoveryAvailable = $this->isolatedRecoveryRestoreAvailable();
        $deleteModalId = 'shieldx-delete-restore-point-' . sanitize_key($restoreId);
        $deleteTitleId = $deleteModalId . '-title';
        $deleteDescId = $deleteModalId . '-description';
        $deletePasswordId = $deleteModalId . '-password';
        $localRestoreModalId = 'shieldx-confirm-restore-local-' . sanitize_key($restoreId);
        $googleRestoreModalId = 'shieldx-confirm-restore-google-' . sanitize_key($restoreId);
        ?>
        <div class="shieldx-remediation-item shieldx-remediation-item--restore">
            <div>
                <strong><?php echo esc_html($this->formatLabel($reason)); ?> <span class="shieldx-muted"><?php echo esc_html($this->formatLabel($snapshotMode)); ?></span></strong>
                <code><?php echo esc_html($restoreId); ?></code>
                <span>
                    <?php
                    echo esc_html(sprintf(
                        /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                        __('%1$d changed files / %2$d indexed / %3$s / DB %4$s', 'xerotact-shield-x-security'),
                        (int) ($restorePoint['files_count'] ?? 0),
                        (int) ($restorePoint['indexed_files_count'] ?? $restorePoint['hashes_count'] ?? 0),
                        $this->formatBytes((int) ($restorePoint['bytes_exported'] ?? 0)),
                        (string) ($restorePoint['database_backup_id'] ?? __('none', 'xerotact-shield-x-security'))
                    ));
                    ?>
                </span>
                <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                <span><?php echo esc_html(sprintf(__('Created %s', 'xerotact-shield-x-security'), $this->formatTime($createdAt))); ?></span>
                <?php $this->renderStorageBadges($restorePoint); ?>
                <?php if (!empty($restorePoint['truncated'])) : ?>
                    <span class="shieldx-muted"><?php echo esc_html__('File snapshot hit the shared-hosting safety cap.', 'xerotact-shield-x-security'); ?></span>
                <?php endif; ?>
                <?php $this->renderDatabaseBackupWarnings([
                    'warning_count' => max(0, (int) ($restorePoint['database_warning_count'] ?? 0)),
                    'warning_details' => (array) ($restorePoint['database_warning_details'] ?? []),
                ]); ?>
                <?php if ($restoredAt !== '') : ?>
                    <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                    <span><?php echo esc_html(sprintf(__('Last restored %s', 'xerotact-shield-x-security'), $this->formatTime($restoredAt))); ?></span>
                <?php endif; ?>
            </div>
            <?php if ($restoreId !== '') : ?>
                <div class="shieldx-remediation-actions">
                    <div class="shieldx-restore-point-primary-actions">
                        <a class="button button-small shieldx-row-action-button" href="<?php echo esc_url(add_query_arg(['page' => 'shield-x-backup-restore', 'shieldx_restore_view' => $restoreId], admin_url('admin.php'))); ?>"><?php echo esc_html__('View contents', 'xerotact-shield-x-security'); ?></a>
                        <button
                            type="button"
                            class="button button-small shieldx-row-action-button shieldx-button-danger shieldx-restore-point-delete-button"
                            data-shieldx-form-modal-open
                            aria-controls="<?php echo esc_attr($deleteModalId); ?>"
                            aria-label="<?php echo esc_attr__('Delete local restore point', 'xerotact-shield-x-security'); ?>"
                            title="<?php echo esc_attr($localAvailable ? __('Delete local restore point', 'xerotact-shield-x-security') : __('Local restore point is unavailable', 'xerotact-shield-x-security')); ?>"
                            <?php disabled(!$localAvailable); ?>
                        >
                            <?php echo wp_kses(Iconoir::svg('trash'), $this->allowedIconSvg()); ?>
                        </button>
                    </div>
                    <?php if ($recoveryAvailable) : ?>
                        <div class="shieldx-restore-point-isolated-actions">
                            <button type="button" class="button button-small shieldx-row-action-button shieldx-button-warning" data-shieldx-form-modal-open aria-controls="<?php echo esc_attr($localRestoreModalId); ?>" <?php disabled(!$localAvailable); ?>><?php echo esc_html__('Restore local', 'xerotact-shield-x-security'); ?></button>
                            <button type="button" class="button button-small shieldx-row-action-button" data-shieldx-form-modal-open aria-controls="<?php echo esc_attr($googleRestoreModalId); ?>" <?php disabled(!$googleAvailable); ?>><?php echo esc_html__('Restore Google', 'xerotact-shield-x-security'); ?></button>
                        </div>
                    <?php else : ?>
                        <div class="shieldx-restore-point-manual-actions">
                            <button
                                type="button"
                                class="button button-small shieldx-row-action-button shieldx-button-warning"
                                data-shieldx-form-modal-open
                                aria-controls="<?php echo esc_attr('shieldx-manual-restore-local-' . sanitize_key($restoreId)); ?>"
                                <?php disabled(!$localAvailable); ?>
                            ><?php echo esc_html__('Restore local', 'xerotact-shield-x-security'); ?></button>
                            <button
                                type="button"
                                class="button button-small shieldx-row-action-button"
                                data-shieldx-form-modal-open
                                aria-controls="<?php echo esc_attr('shieldx-manual-restore-google-' . sanitize_key($restoreId)); ?>"
                                <?php disabled(!$googleAvailable); ?>
                            ><?php echo esc_html__('Restore Google', 'xerotact-shield-x-security'); ?></button>
                        </div>
                    <?php endif; ?>
                </div>
            <?php endif; ?>
        </div>
        <?php if ($restoreId !== '' && $localAvailable) : ?>
            <div id="<?php echo esc_attr($deleteModalId); ?>" class="shieldx-form-modal shieldx-restore-delete-modal" data-shieldx-form-modal hidden>
                <div class="shieldx-form-modal-backdrop" data-shieldx-form-modal-close></div>
                <section class="shieldx-form-modal-card" role="dialog" aria-modal="true" aria-labelledby="<?php echo esc_attr($deleteTitleId); ?>" aria-describedby="<?php echo esc_attr($deleteDescId); ?>">
                    <button type="button" class="shieldx-modal-close" data-shieldx-form-modal-close aria-label="<?php echo esc_attr__('Close dialog', 'xerotact-shield-x-security'); ?>">
                        <span aria-hidden="true">&times;</span>
                    </button>
                    <p class="shieldx-modal-kicker"><?php echo esc_html__('Permanent deletion', 'xerotact-shield-x-security'); ?></p>
                    <h2 id="<?php echo esc_attr($deleteTitleId); ?>"><?php echo esc_html__('Delete restore point?', 'xerotact-shield-x-security'); ?></h2>
                    <p id="<?php echo esc_attr($deleteDescId); ?>" class="shieldx-muted">
                        <?php echo esc_html__('Confirm this destructive action with the current administrator password.', 'xerotact-shield-x-security'); ?>
                    </p>
                    <div class="shieldx-restore-delete-alert" role="alert">
                        <span class="shieldx-restore-delete-alert-icon"><?php echo wp_kses(Iconoir::svg('warning-triangle'), $this->allowedIconSvg()); ?></span>
                        <span>
                            <strong><?php echo esc_html__('This cannot be undone.', 'xerotact-shield-x-security'); ?></strong>
                            <span><?php echo esc_html__('The local restore point files will be deleted permanently. Linked database backups and remote copies will be retained.', 'xerotact-shield-x-security'); ?></span>
                        </span>
                    </div>
                    <p class="shieldx-restore-delete-id"><code><?php echo esc_html($restoreId); ?></code></p>
                    <form class="shieldx-modal-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                        <input type="hidden" name="action" value="shieldx_delete_restore_point">
                        <input type="hidden" name="restore_id" value="<?php echo esc_attr($restoreId); ?>">
                        <?php wp_nonce_field('shieldx_delete_restore_point'); ?>
                        <label class="shieldx-field shieldx-field--compact" for="<?php echo esc_attr($deletePasswordId); ?>">
                            <span><?php echo esc_html__('Current administrator password', 'xerotact-shield-x-security'); ?></span>
                            <input
                                id="<?php echo esc_attr($deletePasswordId); ?>"
                                type="password"
                                name="shieldx_current_password"
                                autocomplete="current-password"
                                data-shieldx-modal-initial-focus
                                required
                            >
                        </label>
                        <div class="shieldx-modal-actions">
                            <button type="button" class="button" data-shieldx-form-modal-close><?php echo esc_html__('Cancel', 'xerotact-shield-x-security'); ?></button>
                            <button type="submit" class="button shieldx-destructive-submit"><?php echo esc_html__('Delete restore point', 'xerotact-shield-x-security'); ?></button>
                        </div>
                    </form>
                </section>
            </div>
        <?php endif; ?>
        <?php if ($restoreId !== '' && $recoveryAvailable) : ?>
            <?php $this->renderIsolatedRestoreConfirmationModal($restorePoint, 'local', $localAvailable); ?>
            <?php $this->renderIsolatedRestoreConfirmationModal($restorePoint, 'google', $googleAvailable); ?>
        <?php endif; ?>
        <?php if ($restoreId !== '' && !$recoveryAvailable) : ?>
            <?php $this->renderManualCliRestoreModal($restorePoint, 'local', $localAvailable); ?>
            <?php $this->renderManualCliRestoreModal($restorePoint, 'google', $googleAvailable); ?>
        <?php endif; ?>
        <?php
    }

    /**
     * Render an accessible in-page confirmation for a destructive full-site
     * restore. Native browser confirm dialogs can be suppressed or stranded by
     * optimization and embedded-admin environments, so the restore must not
     * depend on them.
     *
     * @param array<string,mixed> $restorePoint
     */
    private function renderIsolatedRestoreConfirmationModal(array $restorePoint, string $source, bool $available): void
    {
        $restoreId = (string) ($restorePoint['restore_id'] ?? '');
        if ($restoreId === '' || !$available) {
            return;
        }

        $source = $source === 'google' ? 'google' : 'local';
        $modalId = 'shieldx-confirm-restore-' . $source . '-' . sanitize_key($restoreId);
        $titleId = $modalId . '-title';
        $descriptionId = $modalId . '-description';
        $acknowledgementId = $modalId . '-acknowledgement';
        ?>
        <div id="<?php echo esc_attr($modalId); ?>" class="shieldx-form-modal shieldx-isolated-restore-confirm-modal" data-shieldx-form-modal hidden>
            <div class="shieldx-form-modal-backdrop" data-shieldx-form-modal-close></div>
            <section class="shieldx-form-modal-card" role="dialog" aria-modal="true" aria-labelledby="<?php echo esc_attr($titleId); ?>" aria-describedby="<?php echo esc_attr($descriptionId); ?>">
                <button type="button" class="shieldx-modal-close" data-shieldx-form-modal-close aria-label="<?php echo esc_attr__('Close dialog', 'xerotact-shield-x-security'); ?>">
                    <span aria-hidden="true">&times;</span>
                </button>
                <p class="shieldx-modal-kicker"><?php echo esc_html__('Full-site recovery', 'xerotact-shield-x-security'); ?></p>
                <h2 id="<?php echo esc_attr($titleId); ?>"><?php echo esc_html__('Restore files and database?', 'xerotact-shield-x-security'); ?></h2>
                <p id="<?php echo esc_attr($descriptionId); ?>" class="shieldx-muted">
                    <?php echo esc_html__('SHIELD-X will enter maintenance mode, verify the backup, and overlay every captured file plus the complete database through the isolated Recovery controller.', 'xerotact-shield-x-security'); ?>
                </p>
                <div class="shieldx-restore-delete-alert" role="alert">
                    <span class="shieldx-restore-delete-alert-icon"><?php echo wp_kses(Iconoir::svg('warning-triangle'), $this->allowedIconSvg()); ?></span>
                    <span>
                        <strong><?php echo esc_html__('Captured files are overwritten; other files are kept.', 'xerotact-shield-x-security'); ?></strong>
                        <span><?php echo esc_html__('Files omitted by backup filters are never deleted. Keep this browser open to monitor every restore phase; rollback data remains available until the restored site passes its health check.', 'xerotact-shield-x-security'); ?></span>
                    </span>
                </div>
                <p class="shieldx-restore-delete-id"><code><?php echo esc_html($restoreId); ?></code></p>
                <form class="shieldx-modal-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                    <input type="hidden" name="action" value="shieldx_restore_restore_point">
                    <input type="hidden" name="restore_id" value="<?php echo esc_attr($restoreId); ?>">
                    <input type="hidden" name="restore_source" value="<?php echo esc_attr($source); ?>">
                    <?php wp_nonce_field('shieldx_restore_restore_point'); ?>
                    <label class="shieldx-check-row" for="<?php echo esc_attr($acknowledgementId); ?>">
                        <input id="<?php echo esc_attr($acknowledgementId); ?>" type="checkbox" required data-shieldx-modal-initial-focus>
                        <span><?php echo esc_html__('I understand that this restores all captured files and the complete database.', 'xerotact-shield-x-security'); ?></span>
                    </label>
                    <div class="shieldx-modal-actions">
                        <button type="button" class="button" data-shieldx-form-modal-close><?php echo esc_html__('Cancel', 'xerotact-shield-x-security'); ?></button>
                        <button type="submit" class="button shieldx-destructive-submit"><?php echo esc_html__('Start full restore', 'xerotact-shield-x-security'); ?></button>
                    </div>
                </form>
            </section>
        </div>
        <?php
    }

    /**
     * Render instructions only. A core installation never starts a browser
     * full restore because it cannot provide Recovery's isolated controller.
     *
     * @param array<string,mixed> $restorePoint
     */
    private function renderManualCliRestoreModal(array $restorePoint, string $source, bool $available): void
    {
        $restoreId = (string) ($restorePoint['restore_id'] ?? '');
        if ($restoreId === '' || !$available) {
            return;
        }

        $source = $source === 'google' ? 'google' : 'local';
        $modalId = 'shieldx-manual-restore-' . $source . '-' . sanitize_key($restoreId);
        $titleId = $modalId . '-title';
        $descriptionId = $modalId . '-description';
        $listCommand = 'wp shield-x db restore-points --user=<administrator>';
        $restoreCommand = 'wp shield-x db restore-point-manual ' . $restoreId . ' --source=' . $source . ' --user=<administrator>';
        ?>
        <div id="<?php echo esc_attr($modalId); ?>" class="shieldx-form-modal shieldx-manual-restore-modal" data-shieldx-form-modal hidden>
            <div class="shieldx-form-modal-backdrop" data-shieldx-form-modal-close></div>
            <section class="shieldx-form-modal-card" role="dialog" aria-modal="true" aria-labelledby="<?php echo esc_attr($titleId); ?>" aria-describedby="<?php echo esc_attr($descriptionId); ?>">
                <button type="button" class="shieldx-modal-close" data-shieldx-form-modal-close aria-label="<?php echo esc_attr__('Close dialog', 'xerotact-shield-x-security'); ?>">
                    <span aria-hidden="true">&times;</span>
                </button>
                <p class="shieldx-modal-kicker"><?php echo esc_html__('Recovery fallback', 'xerotact-shield-x-security'); ?></p>
                <h2 id="<?php echo esc_attr($titleId); ?>"><?php echo esc_html__('Restore without terminal access', 'xerotact-shield-x-security'); ?></h2>
                <p id="<?php echo esc_attr($descriptionId); ?>" class="shieldx-muted">
                    <?php echo esc_html__('SHIELD-X cannot safely begin a full live restore from normal WordPress without the isolated Recovery controller. Use one of the safe routes below. Advanced WP-CLI instructions remain available for a server administrator.', 'xerotact-shield-x-security'); ?>
                </p>
                <p class="shieldx-manual-restore-id"><strong><?php echo esc_html__('Selected restore point', 'xerotact-shield-x-security'); ?></strong> <code><?php echo esc_html($restoreId); ?></code></p>
                <div class="shieldx-manual-restore-options">
                    <section class="shieldx-manual-restore-option shieldx-manual-restore-option--primary">
                        <h3><?php echo esc_html__('Recommended: enable isolated browser recovery', 'xerotact-shield-x-security'); ?></h3>
                        <p><?php echo esc_html__('Install or activate SHIELD-X Recovery, then return here and start a checkpointed browser restore with progress and recovery controls.', 'xerotact-shield-x-security'); ?></p>
                        <a class="button button-primary" href="<?php echo esc_url(admin_url('admin.php?page=shield-x-addons')); ?>"><?php echo esc_html__('Open Extensions', 'xerotact-shield-x-security'); ?></a>
                    </section>
                    <section class="shieldx-manual-restore-option">
                        <h3><?php echo esc_html__('Use your hosting control panel', 'xerotact-shield-x-security'); ?></h3>
                        <p><?php echo esc_html__('Ask your host to restore the site files and database from a hosting backup taken before the incident. The restore ID identifies this SHIELD-X point for your records; it is not a host backup file by itself.', 'xerotact-shield-x-security'); ?></p>
                        <div class="shieldx-manual-restore-option-actions">
                            <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                                <input type="hidden" name="action" value="shieldx_download_restore_handoff">
                                <input type="hidden" name="restore_id" value="<?php echo esc_attr($restoreId); ?>">
                                <input type="hidden" name="restore_source" value="<?php echo esc_attr($source); ?>">
                                <?php wp_nonce_field('shieldx_download_restore_handoff'); ?>
                                <button type="submit" class="button"><?php echo esc_html__('Download host handoff', 'xerotact-shield-x-security'); ?></button>
                            </form>
                            <button type="button" class="button" data-shieldx-copy="<?php echo esc_attr(sprintf('Hello, I need help restoring my WordPress site from a hosting backup taken before an incident. SHIELD-X restore point: %s. Please restore the WordPress files and database together when possible. Please do not change permissions on the WordPress root directory or any parent directory. I can provide the attached SHIELD-X restore handoff if needed.', $restoreId)); ?>"><?php echo esc_html__('Copy support request', 'xerotact-shield-x-security'); ?></button>
                        </div>
                    </section>
                </div>
                <details class="shieldx-manual-restore-advanced">
                    <summary><?php echo esc_html__('Advanced: restore with WP-CLI', 'xerotact-shield-x-security'); ?></summary>
                    <p><?php echo esc_html__('Use these commands only if you have SSH access and understand the interruption risk. Run them from the WordPress root as an administrator.', 'xerotact-shield-x-security'); ?></p>
                    <ol class="shieldx-manual-restore-steps">
                        <li>
                            <span><?php echo esc_html__('List available restore points if you need to choose a different one.', 'xerotact-shield-x-security'); ?></span>
                            <div class="shieldx-recovery-command shieldx-manual-restore-command">
                                <code><?php echo esc_html($listCommand); ?></code>
                                <button type="button" class="button button-small" data-shieldx-copy="<?php echo esc_attr($listCommand); ?>"><?php echo esc_html__('Copy command', 'xerotact-shield-x-security'); ?></button>
                            </div>
                        </li>
                        <li>
                            <span><?php echo esc_html__('Run the manual restore for this selected restore point.', 'xerotact-shield-x-security'); ?></span>
                            <div class="shieldx-recovery-command shieldx-manual-restore-command">
                                <code><?php echo esc_html($restoreCommand); ?></code>
                                <button type="button" class="button button-small" data-shieldx-copy="<?php echo esc_attr($restoreCommand); ?>"><?php echo esc_html__('Copy command', 'xerotact-shield-x-security'); ?></button>
                            </div>
                        </li>
                    </ol>
                    <div class="shieldx-restore-delete-alert" role="note">
                        <span class="shieldx-restore-delete-alert-icon"><?php echo wp_kses(Iconoir::svg('warning-triangle'), $this->allowedIconSvg()); ?></span>
                        <span>
                            <strong><?php echo esc_html__('Manual restore has no resume or rollback.', 'xerotact-shield-x-security'); ?></strong>
                            <span><?php echo esc_html__('WP-CLI requires an administrator context and will ask you to type RESTORE LIVE SITE. SHIELD-X and recovery runtime files are preserved so the control path remains available.', 'xerotact-shield-x-security'); ?></span>
                        </span>
                    </div>
                </details>
                <div class="shieldx-modal-actions">
                    <button type="button" class="button" data-shieldx-form-modal-close><?php echo esc_html__('Close', 'xerotact-shield-x-security'); ?></button>
                </div>
            </section>
        </div>
        <?php
    }

    private function isolatedRecoveryRestoreAvailable(): bool
    {
        if (!class_exists('\ShieldXRecovery\IsolatedRestoreCoordinator')) {
            return false;
        }

        $manifests = AddonRegistry::fromWordPress()->manifests();

        return isset($manifests['shieldx-recovery']);
    }

    /**
     * @param array<string,mixed>|null $detail
     */
    private function renderRestorePointDetails(?array $detail, string $requestedRestoreId, ?array $restorePointPreview = null, string $previewPath = '', string $pageSlug = 'shield-x-backup', bool $showClose = true): void
    {
        if ($requestedRestoreId === '' && $detail === null) {
            return;
        }

        ?>
        <div id="shieldx-restore-point-details" class="shieldx-restore-point-detail">
            <?php if ($showClose) : ?>
            <div class="shieldx-section-head shieldx-section-head--compact">
                <h3><?php echo esc_html__('Restore Point Contents', 'xerotact-shield-x-security'); ?></h3>
                <a class="button button-small shieldx-row-action-button" href="<?php echo esc_url(admin_url('admin.php?page=shield-x-backup#shieldx-restore-points')); ?>"><?php echo esc_html__('Close details', 'xerotact-shield-x-security'); ?></a>
            </div>
            <?php endif; ?>
            <?php if ($detail === null) : ?>
                <p class="shieldx-empty"><?php echo esc_html__('This restore point could not be found or its manifest signature is not valid.', 'xerotact-shield-x-security'); ?></p>
            <?php else : ?>
                <?php
                $files = isset($detail['files']) && is_array($detail['files']) ? $detail['files'] : [];
                $database = isset($detail['database']) && is_array($detail['database']) ? $detail['database'] : null;
                $restoreId = (string) ($detail['restore_id'] ?? $requestedRestoreId);
                $fileSearch = $this->requestedRestorePointFileSearch();
                $filePageSize = 15;
                $filesPaginated = !empty($detail['files_paginated']);
                if ($filesPaginated) {
                    $filteredFiles = $files;
                    $fileTotal = max(0, (int) ($detail['files_total'] ?? $detail['files_count'] ?? 0));
                    $fileMatched = max(0, (int) ($detail['files_matched'] ?? count($files)));
                    $fileTotalPages = max(1, (int) ($detail['file_total_pages'] ?? 1));
                    $filePage = min(max(1, (int) ($detail['file_page'] ?? 1)), $fileTotalPages);
                    $visibleFiles = $files;
                } else {
                    $filteredFiles = $this->filterRestorePointFiles($files, $fileSearch);
                    $fileTotal = count($files);
                    $fileMatched = count($filteredFiles);
                    $fileTotalPages = max(1, (int) ceil($fileMatched / $filePageSize));
                    $filePage = min(max(1, $this->requestedRestorePointFilePage()), $fileTotalPages);
                    $visibleFiles = array_slice($filteredFiles, ($filePage - 1) * $filePageSize, $filePageSize);
                }
                $restoreFileFormId = 'shieldx-restore-file-form-' . sanitize_key($restoreId !== '' ? $restoreId : 'restore-point');
                $restoreDatabaseFormId = 'shieldx-restore-database-form-' . sanitize_key($restoreId !== '' ? $restoreId : 'restore-point');
                $restoreStorage = $restoreId !== '' ? BackupStorageManager::fromWordPress()->restorePointStorageState($restoreId) : [];
                $localRestoreAvailable = !empty($restoreStorage['local_exists']);
                $googleRestoreAvailable = !empty($restoreStorage['google_cataloged']) && !empty($restoreStorage['google_connected']);
                $localDatabaseRestoreAvailable = $localRestoreAvailable && is_array($database) && !empty($database['available']);
                ?>
                <dl class="shieldx-detail-list shieldx-detail-list--compact">
                    <?php $this->renderDetail(__('Restore ID', 'xerotact-shield-x-security'), (string) ($detail['restore_id'] ?? '')); ?>
                    <?php $this->renderDetail(__('Reason', 'xerotact-shield-x-security'), $this->formatLabel((string) ($detail['reason'] ?? ''))); ?>
                    <?php $this->renderDetail(__('Created', 'xerotact-shield-x-security'), $this->formatTime((string) ($detail['created_at'] ?? ''))); ?>
                    <?php /* translators: 1: file count, 2: formatted byte size. */ ?>
                    <?php $this->renderDetail(__('Files', 'xerotact-shield-x-security'), sprintf(__('%1$d files / %2$s', 'xerotact-shield-x-security'), (int) ($detail['files_count'] ?? count($files)), $this->formatBytes((int) ($detail['bytes_exported'] ?? 0)))); ?>
                    <?php $this->renderDetail(__('Snapshot scope', 'xerotact-shield-x-security'), $this->formatLabel((string) ($detail['snapshot_scope'] ?? ''))); ?>
                    <?php $this->renderDetail(__('Snapshot mode', 'xerotact-shield-x-security'), $this->formatLabel((string) ($detail['snapshot_mode'] ?? 'full'))); ?>
                    <?php if (!empty($detail['base_restore_id'])) : ?>
                        <?php $this->renderDetail(__('Base restore point', 'xerotact-shield-x-security'), (string) ($detail['base_restore_id'] ?? '')); ?>
                    <?php endif; ?>
                    <?php $this->renderDetail(__('File hashes', 'xerotact-shield-x-security'), (string) (int) ($detail['hashes_count'] ?? 0) . ' / ' . (string) ($detail['hash_algorithm'] ?? 'sha256')); ?>
                    <?php $this->renderDetail(__('Indexed files', 'xerotact-shield-x-security'), (string) (int) ($detail['indexed_files_count'] ?? 0)); ?>
                    <?php $this->renderDetail(__('Incremental checks', 'xerotact-shield-x-security'), sprintf(
                        /* translators: 1: files checked by metadata, 2: files skipped because metadata was unchanged, 3: files hashed after metadata changed. */
                        __('%1$d metadata / %2$d skipped / %3$d hashed', 'xerotact-shield-x-security'),
                        (int) ($detail['metadata_checked_count'] ?? 0),
                        (int) ($detail['metadata_skipped_count'] ?? 0),
                        (int) ($detail['hash_checks_count'] ?? 0)
                    )); ?>
                    <?php $this->renderDetail(__('Database', 'xerotact-shield-x-security'), $database ? (string) ($database['backup_id'] ?? '') : __('Not included', 'xerotact-shield-x-security')); ?>
                    <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                    <?php $this->renderDetail(__('Limits', 'xerotact-shield-x-security'), sprintf(__('%1$d files / %2$s cap', 'xerotact-shield-x-security'), (int) ($detail['limits']['max_files'] ?? 0), $this->formatBytes((int) ($detail['limits']['max_bytes'] ?? 0)))); ?>
                    <?php $this->renderDetail(__('Truncated', 'xerotact-shield-x-security'), !empty($detail['truncated']) ? __('Yes', 'xerotact-shield-x-security') : __('No', 'xerotact-shield-x-security')); ?>
                    <?php if (!empty($detail['restored_at'])) : ?>
                        <?php $this->renderDetail(__('Last restored', 'xerotact-shield-x-security'), $this->formatTime((string) ($detail['restored_at'] ?? ''))); ?>
                    <?php endif; ?>
                    <?php $protectedSkipped = isset($detail['last_restore_protected_paths']) && is_array($detail['last_restore_protected_paths']) ? count($detail['last_restore_protected_paths']) : 0; ?>
                    <?php if ($protectedSkipped > 0) : ?>
                        <?php /* translators: %d: number of protected SHIELD-X plugin files skipped during restore. */ ?>
                        <?php $this->renderDetail(__('Protected skipped', 'xerotact-shield-x-security'), sprintf(_n('%d SHIELD-X plugin file', '%d SHIELD-X plugin files', $protectedSkipped, 'xerotact-shield-x-security'), $protectedSkipped)); ?>
                    <?php endif; ?>
                </dl>

                <form id="<?php echo esc_attr($restoreFileFormId); ?>" class="shieldx-restore-selection-form shieldx-restore-selection-form--hidden" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Restore only the selected files from this restore point?', 'xerotact-shield-x-security')); ?>');">
                    <input type="hidden" name="action" value="shieldx_restore_restore_point">
                    <input type="hidden" name="restore_id" value="<?php echo esc_attr($restoreId); ?>">
                    <input type="hidden" name="restore_selection_mode" value="selected">
                    <input type="hidden" name="restore_scope" value="files">
                    <?php wp_nonce_field('shieldx_restore_restore_point'); ?>
                </form>
                <?php if ($database) : ?>
                    <form id="<?php echo esc_attr($restoreDatabaseFormId); ?>" class="shieldx-restore-selection-form shieldx-restore-selection-form--hidden" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Restore only the selected database tables from this restore point?', 'xerotact-shield-x-security')); ?>');">
                        <input type="hidden" name="action" value="shieldx_restore_restore_point">
                        <input type="hidden" name="restore_id" value="<?php echo esc_attr($restoreId); ?>">
                        <input type="hidden" name="restore_selection_mode" value="selected">
                        <input type="hidden" name="restore_scope" value="database">
                        <?php wp_nonce_field('shieldx_restore_restore_point'); ?>
                    </form>
                <?php endif; ?>

                <div class="shieldx-restore-selection-form">
                    <div class="shieldx-restore-select-head">
                        <div>
                            <h4><?php echo esc_html__('Selective restore', 'xerotact-shield-x-security'); ?></h4>
                            <p class="shieldx-muted"><?php echo esc_html__('Checked files are restored here. Database tables use their own restore button in the Database Backup section.', 'xerotact-shield-x-security'); ?></p>
                        </div>
                        <div class="shieldx-restore-source-actions">
                            <button type="submit" form="<?php echo esc_attr($restoreFileFormId); ?>" name="restore_source" value="local" class="button button-primary shieldx-button-warning" <?php disabled(!$localRestoreAvailable); ?>><?php echo esc_html__('Restore files from local', 'xerotact-shield-x-security'); ?></button>
                            <button type="submit" form="<?php echo esc_attr($restoreFileFormId); ?>" name="restore_source" value="google" class="button" <?php disabled(!$googleRestoreAvailable); ?>><?php echo esc_html__('Restore files from Google', 'xerotact-shield-x-security'); ?></button>
                        </div>
                    </div>

                    <div class="shieldx-restore-detail-block">
                        <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                        <h4><?php echo esc_html(sprintf(__('%d Files In This Restore Point', 'xerotact-shield-x-security'), $fileTotal)); ?></h4>
                        <?php $this->renderRestorePointFileControls($restoreId, $fileSearch, $filePage, $fileTotalPages, $fileMatched, $fileTotal, $restoreFileFormId, $pageSlug, $filesPaginated); ?>
                        <?php if (!$filesPaginated) : ?>
                            <?php $this->renderRestoreSearchSelectionInputs($filteredFiles, $restoreFileFormId, $fileSearch); ?>
                        <?php endif; ?>
                        <?php if (empty($visibleFiles)) : ?>
                            <p class="shieldx-empty"><?php echo esc_html($fileSearch !== '' ? __('No files match this restore point search.', 'xerotact-shield-x-security') : __('No changed files are stored in this restore point.', 'xerotact-shield-x-security')); ?></p>
                        <?php else : ?>
                            <?php $this->renderRestorePointFileTable($visibleFiles, $restoreId, $previewPath, true, $fileSearch, $filePage, $restoreFileFormId, $pageSlug); ?>
                        <?php endif; ?>
                        <?php $this->renderRestorePointFilePreview($restorePointPreview, $previewPath, $pageSlug); ?>
                    </div>

                    <?php if ($database) : ?>
                    <div class="shieldx-restore-detail-block">
                        <div class="shieldx-restore-database-head">
                            <h4><?php echo esc_html__('Database Backup', 'xerotact-shield-x-security'); ?></h4>
                            <div class="shieldx-restore-source-actions">
                                <button type="submit" form="<?php echo esc_attr($restoreDatabaseFormId); ?>" name="restore_source" value="local" class="button button-primary shieldx-button-warning" <?php disabled(!$localDatabaseRestoreAvailable); ?>><?php echo esc_html__('Restore tables from local', 'xerotact-shield-x-security'); ?></button>
                                <button type="submit" form="<?php echo esc_attr($restoreDatabaseFormId); ?>" name="restore_source" value="google" class="button" <?php disabled(!$googleRestoreAvailable); ?>><?php echo esc_html__('Restore tables from Google', 'xerotact-shield-x-security'); ?></button>
                            </div>
                        </div>
                        <dl class="shieldx-detail-list shieldx-detail-list--compact">
                            <?php $this->renderDetail(__('Backup ID', 'xerotact-shield-x-security'), (string) ($database['backup_id'] ?? '')); ?>
                            <?php $this->renderDetail(__('Status', 'xerotact-shield-x-security'), !empty($database['available']) ? __('Available', 'xerotact-shield-x-security') : __('Missing backup file', 'xerotact-shield-x-security')); ?>
                            <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                            <?php $this->renderDetail(__('Tables', 'xerotact-shield-x-security'), sprintf(__('%1$d tables / %2$d rows', 'xerotact-shield-x-security'), (int) ($database['table_count'] ?? 0), (int) ($database['rows_exported'] ?? 0))); ?>
                            <?php $this->renderDetail(__('Size', 'xerotact-shield-x-security'), $this->formatBytes((int) ($database['size'] ?? 0))); ?>
                        </dl>
                        <?php $this->renderDatabaseBackupWarnings($database); ?>
                        <?php $this->renderRestorePointDatabaseTables(isset($database['tables']) && is_array($database['tables']) ? $database['tables'] : [], true, $restoreDatabaseFormId); ?>
                    </div>
                    <?php endif; ?>
                </div>
            <?php endif; ?>
        </div>
        <?php
    }

    /**
     * @param array<int,array<string,mixed>> $files
     * @return array<int,array<string,mixed>>
     */
    private function filterRestorePointFiles(array $files, string $search): array
    {
        $search = strtolower(trim($search));
        if ($search === '') {
            return array_values($files);
        }

        $filtered = [];
        foreach ($files as $file) {
            if (!is_array($file)) {
                continue;
            }

            $haystack = strtolower(implode(' ', [
                (string) ($file['relative_path'] ?? ''),
                (string) ($file['backup_relative_path'] ?? ''),
                (string) ($file['hash'] ?? ''),
            ]));

            if (strpos($haystack, $search) !== false) {
                $filtered[] = $file;
            }
        }

        return $filtered;
    }

    private function renderRestorePointFileControls(string $restoreId, string $search, int $page, int $totalPages, int $matched, int $total, string $formId, string $pageSlug = 'shield-x-backup', bool $serverPaginated = false): void
    {
        $baseQuery = [
            'page' => $pageSlug,
            'shieldx_restore_view' => $restoreId,
        ];
        if ($search !== '') {
            $baseQuery['shieldx_restore_file_search'] = $search;
        }

        $start = $matched > 0 ? (($page - 1) * 15) + 1 : 0;
        $end = min($matched, $page * 15);
        ?>
        <div class="shieldx-restore-file-browser shieldx-restore-file-toolbar">
            <form class="shieldx-restore-file-search" method="get" action="<?php echo esc_url(admin_url('admin.php')); ?>">
                <input type="hidden" name="page" value="<?php echo esc_attr($pageSlug); ?>">
                <input type="hidden" name="shieldx_restore_view" value="<?php echo esc_attr($restoreId); ?>">
                <label class="shieldx-restore-file-search-field" for="shieldx-restore-file-search">
                    <span class="shieldx-screen-reader-text"><?php echo esc_html__('Search restore files', 'xerotact-shield-x-security'); ?></span>
                    <span class="shieldx-restore-file-search-icon"><?php echo wp_kses(Iconoir::svg('page-search'), $this->allowedIconSvg()); ?></span>
                    <input id="shieldx-restore-file-search" type="search" name="shieldx_restore_file_search" value="<?php echo esc_attr($search); ?>" placeholder="<?php echo esc_attr__('Search path or hash', 'xerotact-shield-x-security'); ?>">
                </label>
                <button type="submit" class="button shieldx-row-action-button shieldx-restore-toolbar-button"><?php echo esc_html__('Search', 'xerotact-shield-x-security'); ?></button>
                <?php if ($search !== '') : ?>
                    <a class="button shieldx-row-action-button shieldx-restore-toolbar-button" href="<?php echo esc_url(add_query_arg(['page' => $pageSlug, 'shieldx_restore_view' => $restoreId], admin_url('admin.php')) . '#shieldx-restore-point-details'); ?>"><?php echo esc_html__('Clear', 'xerotact-shield-x-security'); ?></a>
                <?php endif; ?>
            </form>
            <div class="shieldx-restore-file-pager">
                <div class="shieldx-restore-selection-tools" data-shieldx-restore-select-tools data-restore-form="<?php echo esc_attr($formId); ?>" data-restore-id="<?php echo esc_attr($restoreId); ?>">
                    <button type="button" class="button button-small shieldx-row-action-button shieldx-restore-toolbar-button" data-shieldx-restore-select-action="none"><?php echo esc_html__('Unselect all', 'xerotact-shield-x-security'); ?></button>
                    <button type="button" class="button button-small shieldx-row-action-button shieldx-restore-toolbar-button" data-shieldx-restore-select-action="search" <?php disabled($search === '' || $matched === 0); ?> title="<?php echo esc_attr($search === '' ? __('Search files first, then select matched results.', 'xerotact-shield-x-security') : ($serverPaginated ? __('Select the matching files visible on this page.', 'xerotact-shield-x-security') : sprintf(
                        /* translators: %d is the number of restore point files matching the active search. */
                        _n('Select %d file matching this search.', 'Select all %d files matching this search.', $matched, 'xerotact-shield-x-security'),
                        $matched
                    ))); ?>"><?php echo esc_html($serverPaginated ? __('Select page', 'xerotact-shield-x-security') : __('Select results', 'xerotact-shield-x-security')); ?></button>
                    <button type="button" class="button button-small shieldx-row-action-button shieldx-restore-toolbar-button" data-shieldx-restore-export><?php echo esc_html__('Export CSV', 'xerotact-shield-x-security'); ?></button>
                    <span class="shieldx-restore-selected-count" data-shieldx-restore-selected-count data-selected-template="<?php /* translators: %d is the number of selected items. */ echo esc_attr__('%d selected', 'xerotact-shield-x-security'); ?>" aria-live="polite"><?php echo esc_html__('0 selected', 'xerotact-shield-x-security'); ?></span>
                </div>
                <span class="shieldx-restore-file-count" title="<?php echo esc_attr(sprintf(
                    /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                    __('Showing %1$d-%2$d of %3$d matched files (%4$d total).', 'xerotact-shield-x-security'),
                    $start,
                    $end,
                    $matched,
                    $total
                )); ?>">
                    <?php
                    echo esc_html(sprintf(
                        /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                        __('%1$d-%2$d / %3$d', 'xerotact-shield-x-security'),
                        $start,
                        $end,
                        $matched
                    ));
                    ?>
                </span>
                <div class="shieldx-pagination">
                    <?php if ($page > 1) : ?>
                        <a class="button button-small shieldx-row-action-button shieldx-restore-toolbar-button" href="<?php echo esc_url(add_query_arg(array_merge($baseQuery, ['shieldx_restore_file_page' => $page - 1]), admin_url('admin.php')) . '#shieldx-restore-point-details'); ?>"><?php echo esc_html__('Prev', 'xerotact-shield-x-security'); ?></a>
                    <?php else : ?>
                        <span class="button button-small shieldx-row-action-button shieldx-restore-toolbar-button disabled" aria-disabled="true"><?php echo esc_html__('Prev', 'xerotact-shield-x-security'); ?></span>
                    <?php endif; ?>
                    <span class="shieldx-page-count">
                        <?php
                        echo esc_html(sprintf(
                            /* translators: 1: current count, 2: total count. */
                            __('%1$d / %2$d', 'xerotact-shield-x-security'),
                            $page,
                            $totalPages
                        ));
                        ?>
                    </span>
                    <?php if ($page < $totalPages) : ?>
                        <a class="button button-small shieldx-row-action-button shieldx-restore-toolbar-button" href="<?php echo esc_url(add_query_arg(array_merge($baseQuery, ['shieldx_restore_file_page' => $page + 1]), admin_url('admin.php')) . '#shieldx-restore-point-details'); ?>"><?php echo esc_html__('Next', 'xerotact-shield-x-security'); ?></a>
                    <?php else : ?>
                        <span class="button button-small shieldx-row-action-button shieldx-restore-toolbar-button disabled" aria-disabled="true"><?php echo esc_html__('Next', 'xerotact-shield-x-security'); ?></span>
                    <?php endif; ?>
                </div>
            </div>
        </div>
        <?php
    }

    /**
     * @param array<int,array<string,mixed>> $files
     */
    private function renderRestoreSearchSelectionInputs(array $files, string $formId, string $search): void
    {
        if ($formId === '' || $search === '' || empty($files)) {
            return;
        }

        ?>
        <span class="shieldx-restore-search-selection-inputs" hidden aria-hidden="true">
            <?php foreach ($files as $file) : ?>
                <?php $path = $this->normalizeReviewPath((string) ($file['relative_path'] ?? '')); ?>
                <?php if ($path === '') : ?>
                    <?php continue; ?>
                <?php endif; ?>
                <?php
                $modifiedAt = (string) ($file['modified_at'] ?? '');
                $changeType = $this->formatLabel((string) ($file['change_type'] ?? 'full'));
                $restoreData = !empty($file['backup_available']) ? __('Available', 'xerotact-shield-x-security') : (!empty($file['existed']) ? __('Missing', 'xerotact-shield-x-security') : __('Kept unchanged', 'xerotact-shield-x-security'));
                ?>
                <input
                    type="hidden"
                    name="restore_files[]"
                    value="<?php echo esc_attr($path); ?>"
                    form="<?php echo esc_attr($formId); ?>"
                    data-shieldx-restore-search-input
                    data-restore-path="<?php echo esc_attr($path); ?>"
                    data-restore-size="<?php echo esc_attr((string) (int) ($file['size'] ?? 0)); ?>"
                    data-restore-size-human="<?php echo esc_attr($this->formatBytes((int) ($file['size'] ?? 0))); ?>"
                    data-restore-modified="<?php echo esc_attr($modifiedAt !== '' ? $this->formatTime($modifiedAt) : __('Unknown time', 'xerotact-shield-x-security')); ?>"
                    data-restore-change="<?php echo esc_attr($changeType); ?>"
                    data-restore-hash="<?php echo esc_attr((string) ($file['hash'] ?? '')); ?>"
                    data-restore-data="<?php echo esc_attr($restoreData); ?>"
                    disabled
                >
            <?php endforeach; ?>
        </span>
        <?php
    }

    /**
     * @param array<int,array<string,mixed>> $tables
     */
    private function renderRestorePointDatabaseTables(array $tables, bool $selectable = false, string $formId = ''): void
    {
        if (empty($tables)) {
            echo '<p class="shieldx-empty">' . esc_html__('No database table inventory is available for this restore point.', 'xerotact-shield-x-security') . '</p>';
            return;
        }

        ?>
        <div class="shieldx-compat-table-wrap">
            <table class="widefat striped shieldx-compat-table shieldx-restore-table">
                <thead>
                    <tr>
                        <?php if ($selectable) : ?>
                            <th class="shieldx-restore-check-column"><?php echo esc_html__('Restore', 'xerotact-shield-x-security'); ?></th>
                        <?php endif; ?>
                        <th><?php echo esc_html__('Table', 'xerotact-shield-x-security'); ?></th>
                        <th><?php echo esc_html__('Rows', 'xerotact-shield-x-security'); ?></th>
                        <th><?php echo esc_html__('Status', 'xerotact-shield-x-security'); ?></th>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach ($tables as $table) : ?>
                        <?php $tableName = (string) ($table['name'] ?? ''); ?>
                        <tr>
                            <?php if ($selectable) : ?>
                                <td class="shieldx-restore-check-cell">
                                    <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                                    <input type="checkbox" name="restore_database_tables[]" value="<?php echo esc_attr($tableName); ?>" checked<?php if ($formId !== '') : ?> form="<?php echo esc_attr($formId); ?>"<?php endif; ?> aria-label="<?php echo esc_attr(sprintf(__('Restore table %s', 'xerotact-shield-x-security'), $tableName)); ?>">
                                </td>
                            <?php endif; ?>
                            <td><code><?php echo esc_html($tableName); ?></code></td>
                            <td><?php echo esc_html((string) (int) ($table['rows_exported'] ?? 0)); ?></td>
                            <td><?php echo esc_html(!empty($table['truncated']) ? __('Truncated', 'xerotact-shield-x-security') : __('Complete', 'xerotact-shield-x-security')); ?></td>
                        </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
        </div>
        <?php
    }

    /**
     * @param array<int,array<string,mixed>> $files
     */
    private function renderRestorePointFileTable(array $files, string $restoreId = '', string $previewPath = '', bool $selectable = false, string $search = '', int $page = 1, string $formId = '', string $pageSlug = 'shield-x-backup'): void
    {
        if (empty($files)) {
            echo '<p class="shieldx-empty">' . esc_html__('No changed files are stored in this restore point.', 'xerotact-shield-x-security') . '</p>';
            return;
        }

        ?>
        <div class="shieldx-compat-table-wrap">
            <table class="widefat striped shieldx-compat-table shieldx-restore-table">
                <thead>
                    <tr>
                        <?php if ($selectable) : ?>
                            <th class="shieldx-restore-check-column"><?php echo esc_html__('Restore', 'xerotact-shield-x-security'); ?></th>
                        <?php endif; ?>
                        <th><?php echo esc_html__('Path', 'xerotact-shield-x-security'); ?></th>
                        <th><?php echo esc_html__('Change', 'xerotact-shield-x-security'); ?></th>
                        <th><?php echo esc_html__('Size', 'xerotact-shield-x-security'); ?></th>
                        <th><?php echo esc_html__('Modified', 'xerotact-shield-x-security'); ?></th>
                        <th><?php echo esc_html__('SHA-256', 'xerotact-shield-x-security'); ?></th>
                        <th><?php echo esc_html__('Restore data', 'xerotact-shield-x-security'); ?></th>
                        <th><?php echo esc_html__('Action', 'xerotact-shield-x-security'); ?></th>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach ($files as $file) : ?>
                        <?php $hash = (string) ($file['hash'] ?? ''); ?>
                        <?php $path = (string) ($file['relative_path'] ?? ''); ?>
                        <?php $modifiedAt = (string) ($file['modified_at'] ?? ''); ?>
                        <?php $changeType = $this->formatLabel((string) ($file['change_type'] ?? 'full')); ?>
                        <?php $restoreData = !empty($file['backup_available']) ? __('Available', 'xerotact-shield-x-security') : (!empty($file['existed']) ? __('Missing', 'xerotact-shield-x-security') : __('Kept unchanged', 'xerotact-shield-x-security')); ?>
                        <?php $canPreview = $restoreId !== '' && $path !== '' && !empty($file['backup_available']); ?>
                        <?php $isPreviewed = $canPreview && $path === $previewPath; ?>
                        <?php
                        $previewQuery = [
                            'page' => $pageSlug,
                            'shieldx_restore_view' => $restoreId,
                            'shieldx_restore_preview' => $path,
                            'shieldx_restore_file_page' => $page,
                        ];
                        if ($search !== '') {
                            $previewQuery['shieldx_restore_file_search'] = $search;
                        }
                        ?>
                        <tr>
                            <?php if ($selectable) : ?>
                                <td class="shieldx-restore-check-cell">
                                    <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                                    <input
                                        type="checkbox"
                                        name="restore_files[]"
                                        value="<?php echo esc_attr($path); ?>"
                                        <?php checked(!empty($file['existed'])); ?>
                                        <?php disabled(empty($file['existed'])); ?>
                                        <?php if ($formId !== '') : ?>
                                            form="<?php echo esc_attr($formId); ?>"
                                        <?php endif; ?>
                                        <?php /* translators: %s is the relative path of the file selected for restore. */ ?>
                                        aria-label="<?php echo esc_attr(sprintf(__('Restore file %s', 'xerotact-shield-x-security'), $path)); ?>"
                                        data-shieldx-restore-file-input
                                        data-restore-path="<?php echo esc_attr($path); ?>"
                                        data-restore-size="<?php echo esc_attr((string) (int) ($file['size'] ?? 0)); ?>"
                                        data-restore-size-human="<?php echo esc_attr($this->formatBytes((int) ($file['size'] ?? 0))); ?>"
                                        data-restore-modified="<?php echo esc_attr($modifiedAt !== '' ? $this->formatTime($modifiedAt) : __('Unknown time', 'xerotact-shield-x-security')); ?>"
                                        data-restore-change="<?php echo esc_attr($changeType); ?>"
                                        data-restore-hash="<?php echo esc_attr($hash); ?>"
                                        data-restore-data="<?php echo esc_attr($restoreData); ?>"
                                    >
                                </td>
                            <?php endif; ?>
                            <td><code><?php echo esc_html($path); ?></code></td>
                            <td><?php echo esc_html($changeType); ?></td>
                            <td><?php echo esc_html($this->formatBytes((int) ($file['size'] ?? 0))); ?></td>
                            <td><?php echo esc_html($modifiedAt !== '' ? $this->formatTime($modifiedAt) : __('Unknown time', 'xerotact-shield-x-security')); ?></td>
                            <td><code title="<?php echo esc_attr($hash); ?>"><?php echo esc_html($hash !== '' ? substr($hash, 0, 24) : __('new file marker', 'xerotact-shield-x-security')); ?></code></td>
                            <td><?php echo esc_html($restoreData); ?></td>
                            <td>
                                <?php if ($canPreview) : ?>
                                    <a class="<?php echo esc_attr('button button-small shieldx-row-action-button' . ($isPreviewed ? ' is-active' : '')); ?>" href="<?php echo esc_url(add_query_arg($previewQuery, admin_url('admin.php')) . '#shieldx-restore-file-preview'); ?>">
                                        <?php echo esc_html($isPreviewed ? __('Previewing', 'xerotact-shield-x-security') : __('Preview', 'xerotact-shield-x-security')); ?>
                                    </a>
                                <?php else : ?>
                                    <span class="shieldx-muted"><?php echo esc_html__('No snapshot', 'xerotact-shield-x-security'); ?></span>
                                <?php endif; ?>
                            </td>
                        </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed>|null $preview
     */
    private function renderRestorePointFilePreview(?array $preview, string $previewPath, string $pageSlug = 'shield-x-backup'): void
    {
        if ($previewPath === '' && $preview === null) {
            return;
        }

        $error = (string) ($preview['error'] ?? '');
        ?>
        <div id="shieldx-restore-file-preview" class="<?php echo esc_attr($error !== '' ? 'shieldx-file-review shieldx-file-review--error shieldx-restore-file-preview' : 'shieldx-file-review shieldx-restore-file-preview'); ?>">
            <div class="shieldx-file-review-head">
                <div>
                    <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                    <h4><?php echo esc_html($previewPath !== '' ? sprintf(__('Snapshot preview: %s', 'xerotact-shield-x-security'), $previewPath) : __('Snapshot preview', 'xerotact-shield-x-security')); ?></h4>
                    <p><?php echo esc_html($error !== '' ? $error : __('Read-only preview of the backed-up file in this restore point.', 'xerotact-shield-x-security')); ?></p>
                </div>
                <a class="button button-small shieldx-row-action-button" href="<?php echo esc_url($this->adminPageUrlWithout(['shieldx_restore_preview'], $pageSlug) . '#shieldx-restore-point-details'); ?>">
                    <?php echo esc_html__('Close preview', 'xerotact-shield-x-security'); ?>
                </a>
            </div>

            <?php if ($preview !== null && $error === '') : ?>
                <div class="shieldx-file-review-meta">
                    <span><strong><?php echo esc_html__('Size', 'xerotact-shield-x-security'); ?></strong> <?php echo esc_html($this->formatBytes((int) ($preview['size'] ?? 0))); ?></span>
                    <span><strong><?php echo esc_html__('Backed up', 'xerotact-shield-x-security'); ?></strong> <?php echo esc_html($this->formatTimestamp((int) ($preview['modified'] ?? 0))); ?></span>
                    <span><strong><?php echo esc_html__('SHA-256', 'xerotact-shield-x-security'); ?></strong> <code><?php echo esc_html(substr((string) ($preview['hash'] ?? ''), 0, 24)); ?></code></span>
                    <span><strong><?php echo esc_html__('Restore ID', 'xerotact-shield-x-security'); ?></strong> <code><?php echo esc_html((string) ($preview['restore_id'] ?? '')); ?></code></span>
                </div>

                <?php if (!empty($preview['previewable'])) : ?>
                    <pre><?php echo esc_html($this->formatExcerpt((array) ($preview['excerpt'] ?? []))); ?></pre>
                    <?php if (!empty($preview['redacted'])) : ?>
                        <p class="shieldx-muted"><?php echo esc_html__('sensitive values redacted', 'xerotact-shield-x-security'); ?></p>
                    <?php endif; ?>
                <?php else : ?>
                    <p class="shieldx-empty"><?php echo esc_html__('Binary or unreadable text preview is skipped; metadata is shown above.', 'xerotact-shield-x-security'); ?></p>
                <?php endif; ?>
            <?php endif; ?>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $issue
     */
    private function renderQuarantinedIssue(array $issue): void
    {
        $threatId = (string) ($issue['threat_id'] ?? '');
        $path = (string) ($issue['affected_path'] ?? '');
        ?>
        <div class="shieldx-remediation-item">
            <div>
                <strong><?php echo esc_html($this->formatLabel((string) ($issue['type'] ?? 'scanner'))); ?></strong>
                <code><?php echo esc_html($path); ?></code>
                <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                <span><?php echo esc_html(sprintf(__('Quarantined %s', 'xerotact-shield-x-security'), $this->formatTime((string) ($issue['resolved_at'] ?? '')))); ?></span>
            </div>
            <?php if ($threatId !== '') : ?>
                <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" onsubmit="return confirm('<?php echo esc_js(__('Restore this quarantined file to its original path?', 'xerotact-shield-x-security')); ?>');">
                    <input type="hidden" name="action" value="shieldx_restore_quarantined_issue">
                    <input type="hidden" name="threat_id" value="<?php echo esc_attr($threatId); ?>">
                    <?php wp_nonce_field('shieldx_restore_quarantined_issue'); ?>
                    <button type="submit" class="button button-small shieldx-row-action-button"><?php echo esc_html__('Restore', 'xerotact-shield-x-security'); ?></button>
                </form>
            <?php endif; ?>
        </div>
        <?php
    }

    /**
     * @param array<string,mixed> $scan
     */
    private function renderScanRunRow(array $scan): void
    {
        $scanId = (string) ($scan['scan_id'] ?? '');
        ?>
        <tr>
            <td><?php echo esc_html($this->formatTime((string) ($scan['started_at'] ?? ''))); ?></td>
            <td><?php $this->renderStatusBadge((string) ($scan['status'] ?? 'warning')); ?></td>
            <td><?php echo esc_html((string) ($scan['scope_label'] ?? 'Full site baseline')); ?></td>
            <td>
                <?php echo esc_html((string) ($scan['files_scanned'] ?? 0) . ' / ' . (string) ($scan['files_total'] ?? 0)); ?>
                <?php if (!empty($scan['database_selected'])) : ?>
                    <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                    <small><?php echo esc_html(sprintf(__('%s DB checks', 'xerotact-shield-x-security'), (string) ($scan['database_items_scanned'] ?? 0))); ?></small>
                <?php endif; ?>
            </td>
            <td><?php echo esc_html((string) ($scan['files_skipped'] ?? 0)); ?></td>
            <td><?php echo esc_html((string) ($scan['threats_found'] ?? 0)); ?></td>
            <td><code><?php echo esc_html($scanId); ?></code></td>
            <td>
                <?php if ($scanId !== '') : ?>
                    <a class="button button-small" href="<?php echo esc_url(add_query_arg(['page' => 'shield-x-scan', 'shieldx_scan_id' => $scanId], admin_url('admin.php'))); ?>">
                        <?php echo esc_html__('View details', 'xerotact-shield-x-security'); ?>
                    </a>
                <?php endif; ?>
            </td>
        </tr>
        <?php
    }

    /**
     * @param array<string,mixed> $scan
     */
    private function renderScanDetail(array $scan): void
    {
        $issues = isset($scan['issues']) && is_array($scan['issues']) ? $scan['issues'] : [];
        ?>
        <dl class="shieldx-detail-list">
            <?php $this->renderDetail(__('Type', 'xerotact-shield-x-security'), $this->formatLabel((string) ($scan['type'] ?? ''))); ?>
            <?php $this->renderDetail(__('Status', 'xerotact-shield-x-security'), $this->formatLabel((string) ($scan['status'] ?? ''))); ?>
            <?php $this->renderDetail(__('Started', 'xerotact-shield-x-security'), $this->formatTime((string) ($scan['started_at'] ?? ''))); ?>
            <?php $this->renderDetail(__('Completed', 'xerotact-shield-x-security'), $this->formatTime((string) ($scan['completed_at'] ?? ''))); ?>
            <?php $this->renderDetail(__('Scope', 'xerotact-shield-x-security'), (string) ($scan['scope_label'] ?? '')); ?>
            <?php $this->renderDetail(__('Files', 'xerotact-shield-x-security'), (string) ($scan['files_scanned'] ?? 0) . ' / ' . (string) ($scan['files_total'] ?? 0)); ?>
            <?php $this->renderDetail(__('Database checks', 'xerotact-shield-x-security'), !empty($scan['database_selected']) ? (string) ($scan['database_items_scanned'] ?? 0) . ' rows/hooks, ' . (string) ($scan['database_issues_found'] ?? 0) . ' issues' : __('Not selected', 'xerotact-shield-x-security')); ?>
            <?php $this->renderDetail(__('Skipped', 'xerotact-shield-x-security'), (string) ($scan['files_skipped'] ?? 0)); ?>
            <?php $this->renderDetail(__('Hash checks', 'xerotact-shield-x-security'), (string) ($scan['files_hash_checked'] ?? 0)); ?>
            <?php $this->renderDetail(__('New / changed', 'xerotact-shield-x-security'), (string) ($scan['files_new'] ?? 0) . ' / ' . (string) ($scan['files_changed'] ?? 0)); ?>
            <?php $this->renderDetail(__('Duration', 'xerotact-shield-x-security'), (string) ($scan['duration_ms'] ?? 0) . ' ms'); ?>
        </dl>
        <?php if (!empty($scan['error'])) : ?>
            <p class="shieldx-empty"><?php echo esc_html((string) $scan['error']); ?></p>
        <?php endif; ?>
        <?php if (empty($issues)) : ?>
            <p class="shieldx-empty"><?php echo esc_html__('No issues were recorded in this scan run.', 'xerotact-shield-x-security'); ?></p>
        <?php else : ?>
            <div class="shieldx-compat-table-wrap">
                <table class="widefat striped shieldx-issue-table">
                    <thead>
                        <tr>
                            <th><?php echo esc_html__('Severity', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Issue', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Path', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Evidence', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Action', 'xerotact-shield-x-security'); ?></th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ($issues as $issue) : ?>
                            <?php $this->renderScanDetailIssueRow(is_array($issue) ? $issue : []); ?>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        <?php endif; ?>
        <?php
    }

    /**
     * @param array<string,mixed> $issue
     */
    private function renderScanDetailIssueRow(array $issue): void
    {
        $path = (string) ($issue['affected_path'] ?? '');
        $isVirtualIssue = $this->isVirtualScannerPath($path);
        ?>
        <tr>
            <td><span class="<?php echo esc_attr('shieldx-severity shieldx-severity--' . strtolower((string) ($issue['severity'] ?? 'medium'))); ?>"><?php echo esc_html($this->formatLabel((string) ($issue['severity'] ?? 'medium'))); ?></span></td>
            <td>
                <strong><?php echo esc_html($this->formatLabel((string) ($issue['type'] ?? 'scanner'))); ?></strong>
                <span><?php echo esc_html((string) ($issue['description'] ?? '')); ?></span>
            </td>
            <td><code><?php echo esc_html($path); ?></code></td>
            <td><?php echo esc_html((string) ($issue['evidence'] ?? '')); ?></td>
            <td>
                <?php if ($path !== '' && strpos($path, 'db:') !== 0 && !$isVirtualIssue) : ?>
                    <a class="button button-small" href="<?php echo esc_url($this->fileReviewUrl($path)); ?>">
                        <?php echo esc_html__('Review file', 'xerotact-shield-x-security'); ?>
                    </a>
                <?php elseif (strpos($path, 'db:') === 0) : ?>
                    <div class="shieldx-row-actions">
                        <a class="button button-small" href="<?php echo esc_url($this->databaseReviewUrl((string) ($issue['threat_id'] ?? ''))); ?>">
                            <?php echo esc_html__('Review DB', 'xerotact-shield-x-security'); ?>
                        </a>
                    </div>
                <?php elseif ($isVirtualIssue) : ?>
                    <div class="shieldx-row-actions">
                        <a class="button button-small" href="<?php echo esc_url($this->virtualIssueReviewUrl((string) ($issue['threat_id'] ?? ''), $path)); ?>">
                            <?php echo esc_html__('Review advisory', 'xerotact-shield-x-security'); ?>
                        </a>
                    </div>
                <?php else : ?>
                    <span class="shieldx-muted"><?php echo esc_html__('No file', 'xerotact-shield-x-security'); ?></span>
                <?php endif; ?>
            </td>
        </tr>
        <?php
    }

    /**
     * @param array<string,mixed> $scope
     * @param array<int,string>   $selectedScopes
     */
    private function renderScanScopeCard(array $scope, array $selectedScopes, string $fieldName = 'scan_scopes'): void
    {
        $id = (string) ($scope['id'] ?? '');

        if ($id === '') {
            return;
        }

        $label = (string) ($scope['label'] ?? $id);
        $path = (string) ($scope['relative_path'] ?? '');
        $description = (string) ($scope['description'] ?? '');
        $hasFileCount = array_key_exists('file_count', $scope);
        $fileCount = (int) ($scope['file_count'] ?? 0);
        $countLabelPending = false;
        if (isset($scope['count_label']) && is_scalar($scope['count_label'])) {
            $countLabel = (string) $scope['count_label'];
        } elseif (!$hasFileCount && !empty($scope['database'])) {
            $countLabel = __('Database checks', 'xerotact-shield-x-security');
        } elseif (!$hasFileCount) {
            $countLabel = __('File count calculated when scan starts', 'xerotact-shield-x-security');
            $countLabelPending = true;
        } else {
            /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
            $countLabel = sprintf(_n('%d file', '%d files', $fileCount, 'xerotact-shield-x-security'), $fileCount);
        }
        $isSelected = in_array($id, $selectedScopes, true);
        ?>
        <label class="<?php echo esc_attr($isSelected ? 'shieldx-scope-card is-selected' : 'shieldx-scope-card'); ?>">
            <input type="checkbox" name="<?php echo esc_attr($fieldName); ?>[]" value="<?php echo esc_attr($id); ?>" <?php checked($isSelected); ?>>
            <span>
                <strong><?php echo esc_html($label); ?></strong>
                <em class="<?php echo esc_attr($countLabelPending ? 'is-pending' : ''); ?>"><?php echo esc_html($countLabel); ?></em>
                <?php if ($path !== '') : ?>
                    <code><?php echo esc_html($path); ?></code>
                <?php endif; ?>
                <?php if ($description !== '') : ?>
                    <small><?php echo esc_html($description); ?></small>
                <?php endif; ?>
            </span>
        </label>
        <?php
    }

    /**
     * @param array<int,mixed> $scanScopes
     * @return array<int,string>
     */
    private function selectedScanScopesFromRequest(array $scanScopes): array
    {
        $available = [];

        foreach ($scanScopes as $scope) {
            if (is_array($scope) && isset($scope['id'])) {
                $available[] = (string) $scope['id'];
            }
        }

        if (empty($available)) {
            return [];
        }

        $scopeQuery = $this->queryText('shieldx_scopes');
        if ($scopeQuery === '') {
            return $available;
        }

        $posted = explode(',', $scopeQuery);
        $selected = [];

        foreach ($posted as $scope) {
            $scope = sanitize_key($scope);

            if (in_array($scope, $available, true)) {
                $selected[] = $scope;
            }
        }

        return $selected ?: $available;
    }

    /**
     * @param array<int,mixed> $scanScopes
     * @return array<int,array<string,mixed>>
     */
    private function filterScanScopes(array $scanScopes, bool $databaseOnly): array
    {
        $filtered = [];

        foreach ($scanScopes as $scope) {
            if (!is_array($scope)) {
                continue;
            }

            $isDatabase = !empty($scope['database']);
            if ($databaseOnly === $isDatabase) {
                $filtered[] = $scope;
            }
        }

        return $filtered;
    }

    /**
     * @param array<int,array<string,mixed>> $scanScopes
     * @return array<int,array<string,mixed>>
     */
    private function defaultShadowScanScopes(array $scanScopes): array
    {
        $filtered = [];

        foreach ($scanScopes as $scope) {
            if ((string) ($scope['id'] ?? '') === 'uploads') {
                continue;
            }

            $filtered[] = $scope;
        }

        return $filtered ?: $scanScopes;
    }

    /**
     * @param array<int,mixed>  $scanScopes
     * @param array<int,string> $settingsScopes
     * @return array<int,string>
     */
    private function selectedScanScopesFromSettings(array $scanScopes, array $settingsScopes): array
    {
        $available = [];

        foreach ($scanScopes as $scope) {
            if (is_array($scope) && isset($scope['id'])) {
                $available[] = (string) $scope['id'];
            }
        }

        if (empty($available)) {
            return [];
        }

        $selected = [];

        foreach ($settingsScopes as $scope) {
            $scope = function_exists('sanitize_key') ? sanitize_key($scope) : strtolower((string) preg_replace('~[^a-z0-9_-]+~', '', $scope));

            if (in_array($scope, $available, true)) {
                $selected[] = $scope;
            }
        }

        return $selected ?: $available;
    }

    private function requestedScanId(): string
    {
        $scanId = $this->queryText('shieldx_scan_id');

        return preg_match('/^[a-f0-9]{32}$/i', $scanId) === 1 ? $scanId : '';
    }

    private function requestedRestorePointViewId(): string
    {
        return $this->sanitizeRestorePointId($this->queryText('shieldx_restore_view'));
    }

    private function requestedRestorePointPreviewPath(): string
    {
        return $this->normalizeReviewPath($this->queryText('shieldx_restore_preview'));
    }

    private function requestedRestorePointFileSearch(): string
    {
        return substr($this->queryText('shieldx_restore_file_search'), 0, 120);
    }

    private function requestedRestorePointFilePage(): int
    {
        return max(1, $this->queryInt('shieldx_restore_file_page', 1));
    }

    private function sanitizeRestorePointId(string $restoreId): string
    {
        $restoreId = sanitize_text_field($restoreId);

        return preg_match('/^restore-[0-9]{8}-[0-9]{6}-[a-f0-9]{32}$/', $restoreId) === 1 ? $restoreId : '';
    }

    /**
     * @return array<int,string>
     */
    private function postedRestoreFilePaths(): array
    {
        $paths = [];
        foreach ($this->postedArray('restore_files') as $path) {
            $path = $this->normalizeReviewPath($path);
            if ($path === '') {
                continue;
            }

            if (function_exists('validate_file') && validate_file($path) !== 0) {
                continue;
            }

            $paths[] = $path;
        }

        return array_values(array_unique($paths));
    }

    /**
     * @return array<int,string>
     */
    private function postedRestoreTableNames(): array
    {
        $tables = [];
        foreach ($this->postedArray('restore_database_tables') as $table) {
            $table = trim($table);
            if (preg_match('/^[A-Za-z0-9_]+$/', $table) === 1) {
                $tables[] = $table;
            }
        }

        return array_values(array_unique($tables));
    }

    private function renderStatusBadge(string $status): void
    {
        $normalized = [
            'active' => 'ready',
            'completed' => 'ready',
            'resolved' => 'ready',
            'running' => 'warning',
            'pending' => 'warning',
            'failed' => 'missing',
        ];
        $badgeStatus = $normalized[$status] ?? $status;
        $badgeStatus = in_array($badgeStatus, ['ready', 'warning', 'missing', 'optional'], true) ? $badgeStatus : 'warning';
        ?>
        <span class="<?php echo esc_attr('shieldx-status-badge shieldx-status-badge--' . $badgeStatus); ?>">
            <?php echo esc_html($status === 'ready' ? __('Ready', 'xerotact-shield-x-security') : $this->formatLabel($status)); ?>
        </span>
        <?php
    }

    /**
     * @param array<string,mixed> $status
     * @return array<string,mixed>
     */
    private function compatibility(array $status): array
    {
        return isset($status['environment']['compatibility']) && is_array($status['environment']['compatibility'])
            ? $status['environment']['compatibility']
            : [];
    }

    /**
     * @param array<string,mixed> $summary
     */
    private function formatCompatibilitySummary(array $summary): string
    {
        return sprintf(
            /* translators: 1: overall status, 2: ready check count, 3: warning check count, 4: missing check count, 5: ready module count, 6: warning module count, 7: missing module count. */
            __('%1$s: checks %2$s ready/%3$s warn/%4$s missing, modules %5$s ready/%6$s warn/%7$s missing', 'xerotact-shield-x-security'),
            $this->formatLabel((string) ($summary['status'] ?? 'unknown')),
            (string) ($summary['ready'] ?? 0),
            (string) ($summary['warning'] ?? 0),
            (string) ($summary['missing'] ?? 0),
            (string) ($summary['module_ready'] ?? 0),
            (string) ($summary['module_warning'] ?? 0),
            (string) ($summary['module_missing'] ?? 0)
        );
    }

    /**
     * @param mixed $value
     */
    private function lineListValue($value): string
    {
        if (is_array($value)) {
            return implode("\n", array_map('strval', $value));
        }

        return is_scalar($value) ? (string) $value : '';
    }

    /**
     * @return array<int,string>
     */
    private function lineListArray(string $value): array
    {
        $lines = preg_split('~[\r\n,]+~', $value) ?: [];
        $clean = [];

        foreach ($lines as $line) {
            $line = trim((string) $line);

            if ($line !== '') {
                $clean[$line] = $line;
            }
        }

        return array_values($clean);
    }

    private function appendLineListValue(string $list, string $value): string
    {
        $value = trim($value);

        if ($value === '') {
            return $list;
        }

        $lines = preg_split('~[\r\n,]+~', $list) ?: [];
        $clean = [];

        foreach ($lines as $line) {
            $line = trim((string) $line);

            if ($line !== '') {
                $clean[$line] = $line;
            }
        }

        $clean[$value] = $value;

        return implode("\n", array_values($clean));
    }

    private function removeLineListValue(string $list, string $value): string
    {
        $value = trim($value);

        if ($value === '') {
            return $list;
        }

        $lines = preg_split('~[\r\n,]+~', $list) ?: [];
        $clean = [];

        foreach ($lines as $line) {
            $line = trim((string) $line);

            if ($line !== '' && $line !== $value) {
                $clean[$line] = $line;
            }
        }

        return implode("\n", array_values($clean));
    }

    /**
     * @param array<int,mixed> $blocks
     * @return array<int,mixed>
     */
    private function removeTemporaryIpBlocks(array $blocks, string $ip): array
    {
        $clean = [];

        foreach ($blocks as $block) {
            if (!is_array($block)) {
                continue;
            }

            if ((string) ($block['type'] ?? '') === 'ip' && trim((string) ($block['value'] ?? '')) === $ip) {
                continue;
            }

            $clean[] = $block;
        }

        return $clean;
    }

    /**
     * @param array<int,mixed> $blocks
     * @return array<int,mixed>
     */
    private function removeTemporaryBlockById(array $blocks, string $id): array
    {
        $clean = [];

        foreach ($blocks as $block) {
            if (!is_array($block)) {
                continue;
            }

            if ((string) ($block['id'] ?? '') === $id) {
                continue;
            }

            $clean[] = $block;
        }

        return $clean;
    }

    private function renderTextareaField(string $name, string $label, string $value, string $hint): void
    {
        ?>
        <label class="shieldx-field shieldx-field--textarea">
            <span><?php echo esc_html($label); ?></span>
            <textarea name="<?php echo esc_attr($name); ?>" rows="5"><?php echo esc_textarea($value); ?></textarea>
            <small><?php echo esc_html($hint); ?></small>
        </label>
        <?php
    }

    /**
     * @param array<int,array<string,string>> $suggestions
     */
    private function renderTokenTextareaField(string $name, string $label, string $value, string $hint, array $suggestions, string $placeholder, bool $allowCustom = true, int $suggestionLimit = 12): void
    {
        $suggestionLimit = max(0, $suggestionLimit);
        ?>
        <label class="shieldx-field shieldx-field--textarea shieldx-field--token">
            <span><?php echo esc_html($label); ?></span>
            <textarea
                class="shieldx-token-source"
                name="<?php echo esc_attr($name); ?>"
                rows="5"
                data-shieldx-token-field
                data-shieldx-suggestions="<?php echo esc_attr((string) wp_json_encode($suggestions)); ?>"
                data-shieldx-placeholder="<?php echo esc_attr($placeholder); ?>"
                data-shieldx-allow-custom="<?php echo esc_attr($allowCustom ? '1' : '0'); ?>"
                data-shieldx-suggestion-limit="<?php echo esc_attr((string) $suggestionLimit); ?>"
            ><?php echo esc_textarea($value); ?></textarea>
            <small><?php echo esc_html($hint); ?></small>
        </label>
        <?php
    }

    /**
     * @param array<string,mixed> $files
     * @return array<int,array<string,string>>
     */
    private function logFileSuggestions(array $files): array
    {
        $items = isset($files['items']) && is_array($files['items'])
            ? $files['items']
            : (isset($files['recent']) && is_array($files['recent']) ? $files['recent'] : []);
        $suggestions = [];

        foreach ($items as $file) {
            if (!is_array($file)) {
                continue;
            }

            $name = trim((string) ($file['name'] ?? ''));
            if ($name === '') {
                continue;
            }

            $meta = [];
            $meta[] = $this->formatBytes((int) ($file['size'] ?? 0));
            $modified = trim((string) ($file['modified_at'] ?? ''));
            if ($modified !== '') {
                /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                $meta[] = sprintf(__('Modified %s', 'xerotact-shield-x-security'), $modified);
            }

            $this->addSuggestion($suggestions, $name, $name, implode(' / ', $meta));
        }

        return array_values($suggestions);
    }

    /**
     * @param array<int,mixed> $rules
     * @return array<string,string>
     */
    private function wafRuleLabels(array $rules): array
    {
        $labels = [];

        foreach ($rules as $rule) {
            if (!is_array($rule)) {
                continue;
            }

            $id = (string) ($rule['id'] ?? '');
            if ($id === '') {
                continue;
            }

            $labels[$id] = (string) ($rule['name'] ?? $id);
        }

        return $labels;
    }

    private function wafRuleAnchor(string $ruleId): string
    {
        $slug = sanitize_html_class($ruleId);

        if ($slug === '') {
            $slug = substr(hash('sha256', $ruleId), 0, 12);
        }

        return 'shieldx-rule-' . $slug;
    }

    /**
     * @param array<string,mixed> $currentClient
     * @param array<int,mixed> $recentClients
     * @param array<int,mixed> $lastEvents
     * @return array{ips:array<int,array<string,string>>,countries:array<int,array<string,string>>,user_agents:array<int,array<string,string>>,referrers:array<int,array<string,string>>,hostnames:array<int,array<string,string>>}
     */
    private function wafBlockingSuggestions(array $currentClient, array $recentClients, array $lastEvents): array
    {
        $clients = array_merge([$currentClient], array_filter($recentClients, 'is_array'));
        $ips = [];
        $hostnames = [];
        $userAgents = [];
        $referrers = [];

        foreach ($clients as $client) {
            if (!is_array($client)) {
                continue;
            }

            $ip = trim((string) ($client['ip'] ?? ''));
            if ($ip !== '' && $ip !== 'unknown') {
                $browser = trim((string) ($client['browser'] ?? ''));
                $this->addSuggestion($ips, $ip, $ip, $browser !== '' ? $browser : __('Recent client', 'xerotact-shield-x-security'));
            }

            $userAgent = trim((string) ($client['user_agent'] ?? ''));
            if ($userAgent !== '') {
                $browser = trim((string) ($client['browser'] ?? ''));
                $this->addSuggestion($userAgents, $userAgent, $browser !== '' ? $browser : __('Browser signature', 'xerotact-shield-x-security'), $this->shortText($userAgent, 86));
            }

            foreach (['hostname', 'remote_host'] as $field) {
                $hostname = trim((string) ($client[$field] ?? ''));
                if ($hostname !== '') {
                    $this->addSuggestion($hostnames, $hostname, $this->shortText($hostname, 70), __('Observed hostname', 'xerotact-shield-x-security'));
                }
            }
        }

        foreach ($lastEvents as $event) {
            if (!is_array($event)) {
                continue;
            }

            $matched = trim((string) ($event['matched_value'] ?? ''));
            $reason = trim((string) ($event['reason'] ?? ''));

            if ($matched !== '' && $reason !== '') {
                $this->addSuggestion($referrers, $matched, $this->shortText($matched, 70), $reason);
                if (in_array($reason, ['hostname_blocklist', 'reputation_hostname_blocklist'], true)) {
                    $this->addSuggestion($hostnames, $matched, $this->shortText($matched, 70), __('Matched hostname', 'xerotact-shield-x-security'));
                }
            }
        }

        $currentReferrer = filter_input(INPUT_SERVER, 'HTTP_REFERER', FILTER_SANITIZE_URL);
        $currentReferrer = is_string($currentReferrer) ? trim($currentReferrer) : '';
        if ($currentReferrer !== '') {
            $this->addSuggestion($referrers, $currentReferrer, $this->shortText($currentReferrer, 70), __('Current referrer', 'xerotact-shield-x-security'));
        }

        foreach (['curl', 'python-requests', 'sqlmap', 'nikto', 'nmap', 'masscan', 'Go-http-client', 'libwww-perl', 'AhrefsBot', 'SemrushBot', 'MJ12bot', 'DotBot', 'Bytespider', 'GPTBot', 'ClaudeBot', 'Amazonbot'] as $agentToken) {
            $this->addSuggestion($userAgents, $agentToken, $agentToken, __('Common scanner or crawler token', 'xerotact-shield-x-security'));
        }

        foreach (['semalt', 'darodar', 'buttons-for-website', 'free-share-buttons', 'ranksonic', 'site-auditor.online', 'trafficmonetize.org', 'adf.ly', 'bit.ly'] as $referrerToken) {
            $this->addSuggestion($referrers, $referrerToken, $referrerToken, __('Common referrer-spam token', 'xerotact-shield-x-security'));
        }

        return [
            'ips' => array_values($ips),
            'countries' => $this->countrySuggestions(),
            'user_agents' => array_values($userAgents),
            'referrers' => array_values($referrers),
            'hostnames' => array_values($hostnames),
        ];
    }

    /**
     * @param array<string,array<string,string>> $suggestions
     */
    private function addSuggestion(array &$suggestions, string $value, string $label, string $meta = ''): void
    {
        $value = trim($value);

        if ($value === '') {
            return;
        }

        $key = strtolower($value);
        if (isset($suggestions[$key])) {
            return;
        }

        $suggestions[$key] = [
            'value' => $value,
            'label' => $label !== '' ? $label : $value,
            'meta' => $meta,
        ];
    }

    private function shortText(string $value, int $limit): string
    {
        if (function_exists('mb_strlen') && function_exists('mb_substr')) {
            return mb_strlen($value) > $limit ? mb_substr($value, 0, $limit - 1) . '...' : $value;
        }

        return strlen($value) > $limit ? substr($value, 0, $limit - 1) . '...' : $value;
    }

    /**
     * @return array<int,array<string,string>>
     */
    private function countrySuggestions(): array
    {
        $countries = [
            'AF' => 'Afghanistan',
            'AX' => 'Aland Islands',
            'AL' => 'Albania',
            'DZ' => 'Algeria',
            'AS' => 'American Samoa',
            'AD' => 'Andorra',
            'AO' => 'Angola',
            'AI' => 'Anguilla',
            'AQ' => 'Antarctica',
            'AG' => 'Antigua and Barbuda',
            'AR' => 'Argentina',
            'AM' => 'Armenia',
            'AW' => 'Aruba',
            'AU' => 'Australia',
            'AT' => 'Austria',
            'AZ' => 'Azerbaijan',
            'BS' => 'Bahamas',
            'BH' => 'Bahrain',
            'BD' => 'Bangladesh',
            'BB' => 'Barbados',
            'BY' => 'Belarus',
            'BE' => 'Belgium',
            'BZ' => 'Belize',
            'BJ' => 'Benin',
            'BM' => 'Bermuda',
            'BT' => 'Bhutan',
            'BO' => 'Bolivia',
            'BQ' => 'Bonaire, Sint Eustatius and Saba',
            'BA' => 'Bosnia and Herzegovina',
            'BW' => 'Botswana',
            'BV' => 'Bouvet Island',
            'BR' => 'Brazil',
            'IO' => 'British Indian Ocean Territory',
            'BN' => 'Brunei Darussalam',
            'BG' => 'Bulgaria',
            'BF' => 'Burkina Faso',
            'BI' => 'Burundi',
            'KH' => 'Cambodia',
            'CM' => 'Cameroon',
            'CA' => 'Canada',
            'CV' => 'Cape Verde',
            'KY' => 'Cayman Islands',
            'CF' => 'Central African Republic',
            'TD' => 'Chad',
            'CL' => 'Chile',
            'CN' => 'China',
            'CX' => 'Christmas Island',
            'CC' => 'Cocos Islands',
            'CO' => 'Colombia',
            'KM' => 'Comoros',
            'CG' => 'Congo',
            'CD' => 'Congo, Democratic Republic',
            'CK' => 'Cook Islands',
            'CR' => 'Costa Rica',
            'CI' => 'Cote d Ivoire',
            'HR' => 'Croatia',
            'CU' => 'Cuba',
            'CW' => 'Curacao',
            'CY' => 'Cyprus',
            'CZ' => 'Czechia',
            'DK' => 'Denmark',
            'DJ' => 'Djibouti',
            'DM' => 'Dominica',
            'DO' => 'Dominican Republic',
            'EC' => 'Ecuador',
            'EG' => 'Egypt',
            'SV' => 'El Salvador',
            'GQ' => 'Equatorial Guinea',
            'ER' => 'Eritrea',
            'EE' => 'Estonia',
            'SZ' => 'Eswatini',
            'ET' => 'Ethiopia',
            'FK' => 'Falkland Islands',
            'FO' => 'Faroe Islands',
            'FJ' => 'Fiji',
            'FI' => 'Finland',
            'FR' => 'France',
            'GF' => 'French Guiana',
            'PF' => 'French Polynesia',
            'TF' => 'French Southern Territories',
            'GA' => 'Gabon',
            'GM' => 'Gambia',
            'GE' => 'Georgia',
            'DE' => 'Germany',
            'GH' => 'Ghana',
            'GI' => 'Gibraltar',
            'GR' => 'Greece',
            'GL' => 'Greenland',
            'GD' => 'Grenada',
            'GP' => 'Guadeloupe',
            'GU' => 'Guam',
            'GT' => 'Guatemala',
            'GG' => 'Guernsey',
            'GN' => 'Guinea',
            'GW' => 'Guinea-Bissau',
            'GY' => 'Guyana',
            'HT' => 'Haiti',
            'HM' => 'Heard Island and McDonald Islands',
            'VA' => 'Holy See',
            'HN' => 'Honduras',
            'HK' => 'Hong Kong',
            'HU' => 'Hungary',
            'IS' => 'Iceland',
            'IN' => 'India',
            'ID' => 'Indonesia',
            'IR' => 'Iran',
            'IQ' => 'Iraq',
            'IE' => 'Ireland',
            'IM' => 'Isle of Man',
            'IL' => 'Israel',
            'IT' => 'Italy',
            'JM' => 'Jamaica',
            'JP' => 'Japan',
            'JE' => 'Jersey',
            'JO' => 'Jordan',
            'KZ' => 'Kazakhstan',
            'KE' => 'Kenya',
            'KI' => 'Kiribati',
            'KP' => 'Korea, Democratic People\'s Republic',
            'KR' => 'Korea, Republic',
            'KW' => 'Kuwait',
            'KG' => 'Kyrgyzstan',
            'LA' => 'Lao People\'s Democratic Republic',
            'LV' => 'Latvia',
            'LB' => 'Lebanon',
            'LS' => 'Lesotho',
            'LR' => 'Liberia',
            'LY' => 'Libya',
            'LI' => 'Liechtenstein',
            'LT' => 'Lithuania',
            'LU' => 'Luxembourg',
            'MO' => 'Macao',
            'MG' => 'Madagascar',
            'MW' => 'Malawi',
            'MY' => 'Malaysia',
            'MV' => 'Maldives',
            'ML' => 'Mali',
            'MT' => 'Malta',
            'MH' => 'Marshall Islands',
            'MQ' => 'Martinique',
            'MR' => 'Mauritania',
            'MU' => 'Mauritius',
            'YT' => 'Mayotte',
            'MX' => 'Mexico',
            'FM' => 'Micronesia',
            'MD' => 'Moldova',
            'MC' => 'Monaco',
            'MN' => 'Mongolia',
            'ME' => 'Montenegro',
            'MS' => 'Montserrat',
            'MA' => 'Morocco',
            'MZ' => 'Mozambique',
            'MM' => 'Myanmar',
            'NA' => 'Namibia',
            'NR' => 'Nauru',
            'NP' => 'Nepal',
            'NL' => 'Netherlands',
            'NC' => 'New Caledonia',
            'NZ' => 'New Zealand',
            'NI' => 'Nicaragua',
            'NE' => 'Niger',
            'NG' => 'Nigeria',
            'NU' => 'Niue',
            'NF' => 'Norfolk Island',
            'MK' => 'North Macedonia',
            'MP' => 'Northern Mariana Islands',
            'NO' => 'Norway',
            'OM' => 'Oman',
            'PK' => 'Pakistan',
            'PW' => 'Palau',
            'PS' => 'Palestine',
            'PA' => 'Panama',
            'PG' => 'Papua New Guinea',
            'PY' => 'Paraguay',
            'PE' => 'Peru',
            'PH' => 'Philippines',
            'PN' => 'Pitcairn',
            'PL' => 'Poland',
            'PT' => 'Portugal',
            'PR' => 'Puerto Rico',
            'QA' => 'Qatar',
            'RE' => 'Reunion',
            'RO' => 'Romania',
            'RU' => 'Russian Federation',
            'RW' => 'Rwanda',
            'BL' => 'Saint Barthelemy',
            'SH' => 'Saint Helena',
            'KN' => 'Saint Kitts and Nevis',
            'LC' => 'Saint Lucia',
            'MF' => 'Saint Martin',
            'PM' => 'Saint Pierre and Miquelon',
            'VC' => 'Saint Vincent and the Grenadines',
            'WS' => 'Samoa',
            'SM' => 'San Marino',
            'ST' => 'Sao Tome and Principe',
            'SA' => 'Saudi Arabia',
            'SN' => 'Senegal',
            'RS' => 'Serbia',
            'SC' => 'Seychelles',
            'SL' => 'Sierra Leone',
            'SG' => 'Singapore',
            'SX' => 'Sint Maarten',
            'SK' => 'Slovakia',
            'SI' => 'Slovenia',
            'SB' => 'Solomon Islands',
            'SO' => 'Somalia',
            'ZA' => 'South Africa',
            'GS' => 'South Georgia and South Sandwich Islands',
            'SS' => 'South Sudan',
            'ES' => 'Spain',
            'LK' => 'Sri Lanka',
            'SD' => 'Sudan',
            'SR' => 'Suriname',
            'SJ' => 'Svalbard and Jan Mayen',
            'SE' => 'Sweden',
            'CH' => 'Switzerland',
            'SY' => 'Syrian Arab Republic',
            'TW' => 'Taiwan',
            'TJ' => 'Tajikistan',
            'TZ' => 'Tanzania',
            'TH' => 'Thailand',
            'TL' => 'Timor-Leste',
            'TG' => 'Togo',
            'TK' => 'Tokelau',
            'TO' => 'Tonga',
            'TT' => 'Trinidad and Tobago',
            'TN' => 'Tunisia',
            'TR' => 'Turkiye',
            'TM' => 'Turkmenistan',
            'TC' => 'Turks and Caicos Islands',
            'TV' => 'Tuvalu',
            'UG' => 'Uganda',
            'UA' => 'Ukraine',
            'AE' => 'United Arab Emirates',
            'GB' => 'United Kingdom',
            'US' => 'United States',
            'UM' => 'United States Minor Outlying Islands',
            'UY' => 'Uruguay',
            'UZ' => 'Uzbekistan',
            'VU' => 'Vanuatu',
            'VE' => 'Venezuela',
            'VN' => 'Viet Nam',
            'VG' => 'Virgin Islands, British',
            'VI' => 'Virgin Islands, U.S.',
            'WF' => 'Wallis and Futuna',
            'EH' => 'Western Sahara',
            'YE' => 'Yemen',
            'ZM' => 'Zambia',
            'ZW' => 'Zimbabwe',
        ];

        $suggestions = [];
        foreach ($countries as $code => $name) {
            $suggestions[] = [
                'value' => $code,
                'label' => $code . ' - ' . $name,
                'meta' => __('Country code', 'xerotact-shield-x-security'),
            ];
        }

        return $suggestions;
    }

    /**
     * @return array<int,string>
     */
    private function automaticWafServerIps(): array
    {
        $ips = [
            '127.0.0.1',
            '::1',
        ];

        foreach (MinimalWaf::serverIpsFromServer($_SERVER) as $ip) {
            if (filter_var($ip, FILTER_VALIDATE_IP) && !in_array($ip, $ips, true)) {
                $ips[] = $ip;
            }
        }

        return $ips;
    }

    /**
     * @return array<string,mixed>
     */
    private function currentClientForWafPanel(): array
    {
        $settings = WafSettings::fromWordPress();
        $request = MinimalWaf::requestFromGlobals($settings);

        return [
            'time' => function_exists('current_time') ? current_time('mysql', true) : gmdate('Y-m-d H:i:s'),
            'ip' => (string) ($request['ip'] ?? 'unknown'),
            'browser' => (string) ($request['browser'] ?? 'Unknown'),
            'user_agent' => (string) ($request['user_agent'] ?? ''),
            'remote_host' => (string) ($request['remote_host'] ?? ''),
            'method' => (string) ($request['method'] ?? ''),
            'uri' => (string) ($request['uri'] ?? ''),
            'proxy_headers_present' => $settings->hasProxyHeaders($request),
            'action' => 'current',
        ];
    }

    /**
     * @param array<string,mixed> $currentClient
     */
    private function isCurrentWafClientIp(string $ip, array $currentClient = []): bool
    {
        $ip = trim($ip);
        if (!filter_var($ip, FILTER_VALIDATE_IP)) {
            return false;
        }

        if (empty($currentClient)) {
            $currentClient = $this->currentClientForWafPanel();
        }

        $currentIp = trim((string) ($currentClient['ip'] ?? ''));
        if (!filter_var($currentIp, FILTER_VALIDATE_IP)) {
            return false;
        }

        if (function_exists('inet_pton')) {
            $packedIp = inet_pton($ip);
            $packedCurrent = inet_pton($currentIp);
            if (is_string($packedIp) && is_string($packedCurrent)) {
                return hash_equals($packedCurrent, $packedIp);
            }
        }

        return hash_equals($currentIp, $ip);
    }

    /**
     * @param array<string,mixed> $currentClient
     */
    private function removeCurrentWafClientIpMatchesFromList(string $list, array $currentClient): string
    {
        $currentIp = trim((string) ($currentClient['ip'] ?? ''));
        if (!filter_var($currentIp, FILTER_VALIDATE_IP)) {
            return $list;
        }

        $kept = [];
        foreach ($this->lineListArray($list) as $entry) {
            if ($this->wafIpEntryMatchesClientIp($entry, $currentIp)) {
                continue;
            }

            $kept[$entry] = $entry;
        }

        return implode("\n", array_values($kept));
    }

    private function wafIpEntryMatchesClientIp(string $entry, string $ip): bool
    {
        $entry = trim($entry);
        if ($entry === '') {
            return false;
        }

        if (strpos($entry, '/') !== false) {
            $range = ClientIpResolver::sanitizeIpRange($entry);

            return $range !== '' && ClientIpResolver::ipInCidr($ip, $range);
        }

        if (!filter_var($entry, FILTER_VALIDATE_IP)) {
            return false;
        }

        if (function_exists('inet_pton')) {
            $packedEntry = inet_pton($entry);
            $packedIp = inet_pton($ip);
            if (is_string($packedEntry) && is_string($packedIp)) {
                return hash_equals($packedIp, $packedEntry);
            }
        }

        return hash_equals($ip, $entry);
    }

    /**
     * @param array<string,mixed> $currentClient
     */
    private function isCurrentWafClientUserAgent(string $userAgent, array $currentClient = []): bool
    {
        $userAgent = trim($userAgent);
        if ($userAgent === '') {
            return false;
        }

        if (empty($currentClient)) {
            $currentClient = $this->currentClientForWafPanel();
        }

        $currentUserAgent = trim((string) ($currentClient['user_agent'] ?? ''));

        return $currentUserAgent !== '' && hash_equals($currentUserAgent, $userAgent);
    }

    /**
     * @param array<string,mixed> $client
     */
    private function renderClientShortcutRow(array $client, bool $current): void
    {
        $ip = (string) ($client['ip'] ?? '');

        if ($ip === '' || $ip === 'unknown') {
            return;
        }

        $userAgent = (string) ($client['user_agent'] ?? '');
        $browser = (string) ($client['browser'] ?? 'Unknown');
        $requestLine = trim((string) ($client['method'] ?? '') . ' ' . (string) ($client['uri'] ?? ''));
        ?>
        <article class="<?php echo esc_attr($current ? 'shieldx-client-row shieldx-client-row--current' : 'shieldx-client-row'); ?>">
            <div class="shieldx-client-main">
                <strong><?php echo esc_html($ip); ?></strong>
                <span><?php echo esc_html($browser); ?></span>
                <?php if ($current) : ?>
                    <em><?php echo esc_html__('Current', 'xerotact-shield-x-security'); ?></em>
                <?php endif; ?>
            </div>
            <div class="shieldx-client-meta">
                <span><?php echo esc_html($this->formatTime((string) ($client['time'] ?? ''))); ?></span>
                <?php if ($requestLine !== '') : ?>
                    <code><?php echo esc_html($requestLine); ?></code>
                <?php endif; ?>
            </div>
            <?php if ($userAgent !== '') : ?>
                <p><?php echo esc_html($userAgent); ?></p>
            <?php endif; ?>
            <div class="shieldx-client-actions-row">
                <button type="submit" class="button shieldx-client-action" name="quick_ip_allow" value="<?php echo esc_attr($ip); ?>"><?php echo esc_html__('Whitelist IP', 'xerotact-shield-x-security'); ?></button>
                <?php if (!$current) : ?>
                    <button type="submit" class="button shieldx-client-action shieldx-button-danger" name="quick_ip_block" value="<?php echo esc_attr($ip); ?>"><?php echo esc_html__('Block IP', 'xerotact-shield-x-security'); ?></button>
                    <?php if ($userAgent !== '') : ?>
                        <button type="submit" class="button shieldx-client-action shieldx-button-danger" name="quick_ua_block" value="<?php echo esc_attr($userAgent); ?>"><?php echo esc_html__('Block browser', 'xerotact-shield-x-security'); ?></button>
                    <?php endif; ?>
                <?php endif; ?>
            </div>
        </article>
        <?php
    }

    /**
     * @param array<string,mixed> $block
     */
    private function renderTemporaryBlockRow(array $block): void
    {
        $id = (string) ($block['id'] ?? '');

        if ($id === '') {
            return;
        }

        ?>
        <tr>
            <td>
                <label class="shieldx-inline-check">
                    <input type="checkbox" name="remove_temporary_blocks[]" value="<?php echo esc_attr($id); ?>">
                    <span><?php echo esc_html__('Remove', 'xerotact-shield-x-security'); ?></span>
                </label>
            </td>
            <td><?php echo esc_html($this->formatLabel((string) ($block['type'] ?? ''))); ?></td>
            <td><code><?php echo esc_html((string) ($block['value'] ?? '')); ?></code></td>
            <td><?php echo esc_html($this->formatTimestamp((int) ($block['expires_at'] ?? 0))); ?></td>
        </tr>
        <?php
    }

    /**
     * @param array<string,mixed> $profile
     */
    private function renderUserProfileListRow(array $profile, bool $selected): void
    {
        $key = $this->profileKey($profile);
        $country = $this->countryBadgeForProfile($profile);
        $status = $this->profileStatus((string) ($profile['last_seen'] ?? ''));
        $panelId = 'shieldx-profile-panel-' . sanitize_html_class($key);
        ?>
        <tr class="shieldx-user-row<?php echo esc_attr($selected ? ' is-selected' : ''); ?>" data-shieldx-profile-key="<?php echo esc_attr($key); ?>">
            <td>
                <div class="shieldx-user-identity">
                    <span class="shieldx-country-flag" title="<?php echo esc_attr($country['label']); ?>" aria-label="<?php echo esc_attr($country['label']); ?>"><?php echo esc_html($country['flag']); ?></span>
                    <div>
                        <strong><?php echo esc_html((string) ($profile['ip'] ?? 'unknown')); ?></strong>
                        <span><?php echo esc_html((string) ($profile['browser'] ?? 'Unknown')); ?></span>
                    </div>
                </div>
            </td>
            <td><?php echo esc_html((string) ($profile['visit_count'] ?? 0)); ?></td>
            <td><?php echo esc_html((string) ($profile['unique_urls'] ?? 0)); ?></td>
            <td><?php echo esc_html($this->formatTime((string) ($profile['first_seen'] ?? ''))); ?></td>
            <td><?php echo esc_html($this->formatTime((string) ($profile['last_seen'] ?? ''))); ?></td>
            <td><span class="shieldx-user-status shieldx-user-status--<?php echo esc_attr($status['type']); ?>"><?php echo esc_html($status['label']); ?></span></td>
            <td><button type="button" class="button-link shieldx-profile-view" data-shieldx-profile-key="<?php echo esc_attr($key); ?>" aria-controls="<?php echo esc_attr($panelId); ?>" aria-expanded="<?php echo esc_attr($selected ? 'true' : 'false'); ?>"><?php echo esc_html__('View profile', 'xerotact-shield-x-security'); ?></button></td>
        </tr>
        <?php
    }

    /**
     * @param array<string,mixed> $profile
     */
    private function renderUserProfileDetailRow(array $profile, bool $selected): void
    {
        $key = $this->profileKey($profile);
        $panelId = 'shieldx-profile-panel-' . sanitize_html_class($key);
        ?>
        <tr class="shieldx-user-detail-row<?php echo esc_attr($selected ? ' is-active' : ''); ?>" data-shieldx-profile-detail="<?php echo esc_attr($key); ?>"<?php if (!$selected) : ?> hidden<?php endif; ?>>
            <td colspan="7">
                <?php $this->renderUserProfilePanel($profile, $selected, $panelId); ?>
            </td>
        </tr>
        <?php
    }

    /**
     * @param array<string,mixed> $profile
     */
    private function renderUserProfilePanel(array $profile, bool $selected, string $panelId): void
    {
        $visitorId = (string) ($profile['visitor_id'] ?? '');
        $events = isset($profile['recent_events']) && is_array($profile['recent_events']) ? $profile['recent_events'] : [];
        $key = $this->profileKey($profile);
        ?>
        <article id="<?php echo esc_attr($panelId); ?>" class="shieldx-profile-panel<?php echo esc_attr($selected ? ' is-active' : ''); ?>" data-shieldx-profile-panel="<?php echo esc_attr($key); ?>"<?php if (!$selected) : ?> hidden<?php endif; ?>>
            <div class="shieldx-profile-panel-head">
                <span><?php echo esc_html__('Visitor ID', 'xerotact-shield-x-security'); ?></span>
                <code><?php echo esc_html($this->shortVisitorId($visitorId)); ?></code>
            </div>
            <div class="shieldx-profile-stats" role="list">
                <div role="listitem">
                    <span><?php echo esc_html__('Recorded visits', 'xerotact-shield-x-security'); ?></span>
                    <strong><?php echo esc_html((string) ($profile['visit_count'] ?? 0)); ?></strong>
                </div>
                <div role="listitem">
                    <span><?php echo esc_html__('Recorded URLs', 'xerotact-shield-x-security'); ?></span>
                    <strong><?php echo esc_html((string) ($profile['unique_urls'] ?? 0)); ?></strong>
                </div>
                <div role="listitem">
                    <span><?php echo esc_html__('Last seen', 'xerotact-shield-x-security'); ?></span>
                    <strong><?php echo esc_html($this->formatTime((string) ($profile['last_seen'] ?? ''))); ?></strong>
                </div>
            </div>
            <?php if (!empty($events)) : ?>
                <h3><?php echo esc_html__('Recent profile activity', 'xerotact-shield-x-security'); ?></h3>
                <div class="shieldx-profile-trail">
                    <?php foreach ($events as $event) : ?>
                        <?php if (!is_array($event)) { continue; } ?>
                        <div>
                            <span><?php echo esc_html($this->formatTime((string) ($event['visited_at'] ?? ''))); ?></span>
                            <code><?php echo esc_html(trim((string) ($event['method'] ?? '') . ' ' . (string) ($event['path'] ?? ''))); ?></code>
                            <?php if (!empty($event['query_string'])) : ?>
                                <small><?php echo esc_html('?' . (string) $event['query_string']); ?></small>
                            <?php endif; ?>
                        </div>
                    <?php endforeach; ?>
                </div>
            <?php endif; ?>
            <?php if (!empty($profile['user_agent'])) : ?>
                <div class="shieldx-profile-agent">
                    <span><?php echo esc_html__('Browser signature', 'xerotact-shield-x-security'); ?></span>
                    <p><?php echo esc_html((string) $profile['user_agent']); ?></p>
                </div>
            <?php endif; ?>
        </article>
        <?php
    }

    /**
     * @param array<string,mixed> $timeline
     */
    private function activityLogEvents(array $timeline, int $limit = 16): array
    {
        $events = isset($timeline['events']) && is_array($timeline['events']) ? $timeline['events'] : [];
        $activityEvents = [];

        foreach ($events as $event) {
            if (!is_array($event) || !$this->isActivityLogEvent($event)) {
                continue;
            }

            $activityEvents[] = $event;
            if (count($activityEvents) >= $limit) {
                break;
            }
        }

        return $activityEvents;
    }

    /**
     * @param array<int,array<string,mixed>> $events
     * @return array{total:int,logins:int,content:int}
     */
    private function activityLogStats(array $events): array
    {
        $stats = [
            'total' => count($events),
            'logins' => 0,
            'content' => 0,
        ];

        foreach ($events as $event) {
            $sourceEvent = (string) ($event['source_event'] ?? '');
            $category = (string) ($event['category'] ?? '');
            if ($category === 'login' || strpos($sourceEvent, 'login.') === 0 || strpos($sourceEvent, 'login_security.') === 0 || in_array($sourceEvent, ['activity.user_login', 'activity.user_logout'], true)) {
                $stats['logins']++;
            }

            if (strpos($sourceEvent, 'activity.post_') === 0) {
                $stats['content']++;
            }
        }

        return $stats;
    }

    /**
     * @param array<string,mixed> $event
     */
    private function isActivityLogEvent(array $event): bool
    {
        $sourceEvent = (string) ($event['source_event'] ?? '');
        $category = (string) ($event['category'] ?? '');

        return $category === 'activity'
            || $category === 'login'
            || strpos($sourceEvent, 'login.') === 0
            || strpos($sourceEvent, 'login_security.') === 0;
    }

    /**
     * @param array<int,array<string,mixed>> $events
     */
    private function renderActivityLogCard(array $events): void
    {
        $stats = $this->activityLogStats($events);
        ?>
        <section class="shieldx-panel shieldx-panel--full shieldx-activity-log-panel" data-shieldx-accordion-open="1">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Activity Log', 'xerotact-shield-x-security'); ?> <span aria-hidden="true">&#9733;</span></h2>
                <?php /* translators: %d: number of activity audit trail records. */ ?>
                <span><?php echo esc_html(sprintf(_n('Audit trail: who did what and when. %d record.', 'Audit trail: who did what and when. %d records.', (int) $stats['total'], 'xerotact-shield-x-security'), (int) $stats['total'])); ?></span>
            </div>

            <div class="shieldx-rule-table-wrap shieldx-activity-log-table-wrap">
                <table class="widefat striped shieldx-activity-log-table">
                    <thead>
                        <tr>
                            <th><?php echo esc_html__('Time', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Actor', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Action', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Target', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Details', 'xerotact-shield-x-security'); ?></th>
                            <th><?php echo esc_html__('Source', 'xerotact-shield-x-security'); ?></th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php if (empty($events)) : ?>
                            <tr class="shieldx-activity-log-empty-row">
                                <td colspan="6">
                                    <p class="shieldx-empty shieldx-activity-log-empty"><?php echo esc_html__('Logins, content edits, user changes, settings, plugins, themes, multisite, database-table changes, and file-change scans will appear here as an audit trail of who did what.', 'xerotact-shield-x-security'); ?></p>
                                </td>
                            </tr>
                        <?php else : ?>
                            <?php foreach ($events as $event) : ?>
                                <?php $this->renderActivityLogRow(is_array($event) ? $event : []); ?>
                            <?php endforeach; ?>
                        <?php endif; ?>
                    </tbody>
                </table>
            </div>
        </section>
        <?php
    }

    /**
     * @param array<string,mixed> $event
     */
    private function renderActivityLogRow(array $event): void
    {
        $severity = strtolower((string) ($event['severity'] ?? 'info'));
        $category = (string) ($event['category'] ?? 'activity');
        $meta = isset($event['meta']) && is_array($event['meta']) ? $event['meta'] : [];
        $action = $this->activityLogAction($event);
        $actor = $this->activityLogActor($event);
        $verb = $this->activityLogVerb($event);
        $target = $this->activityLogTarget($event);
        $targetDetail = $this->activityLogTargetDetail($event);
        $details = $this->activityLogDetails($event);
        $source = $this->activityLogSource($event);
        ?>
        <tr class="<?php echo esc_attr('shieldx-activity-log-row shieldx-activity-log-row--' . $severity); ?>">
            <td class="shieldx-activity-log-time">
                <time><?php echo esc_html($this->formatTime((string) ($event['time'] ?? ''))); ?></time>
            </td>
            <td class="shieldx-activity-log-actor">
                <strong><?php echo esc_html($actor['label']); ?></strong>
                <?php if ($actor['detail'] !== '') : ?>
                    <small><?php echo esc_html($actor['detail']); ?></small>
                <?php endif; ?>
            </td>
            <td class="shieldx-activity-log-action-cell">
                <div class="shieldx-activity-log-title">
                    <strong><?php echo esc_html($verb); ?></strong>
                    <span class="<?php echo esc_attr('shieldx-severity shieldx-severity--' . $severity); ?>"><?php echo esc_html($this->formatLabel($severity)); ?></span>
                </div>
                <small><?php echo esc_html((string) ($event['title'] ?? $this->formatLabel($category))); ?></small>
            </td>
            <td class="shieldx-activity-log-target">
                <?php if ($target !== '') : ?>
                    <code><?php echo esc_html($target); ?></code>
                <?php else : ?>
                    <span class="shieldx-muted"><?php echo esc_html__('Unknown target', 'xerotact-shield-x-security'); ?></span>
                <?php endif; ?>
                <?php if ($targetDetail !== '') : ?>
                    <small><?php echo esc_html($targetDetail); ?></small>
                <?php endif; ?>
            </td>
            <td class="shieldx-activity-log-detail">
                <?php if ($details !== '') : ?>
                    <span><?php echo esc_html($details); ?></span>
                <?php endif; ?>
                <?php if (!empty($meta)) : ?>
                    <div class="shieldx-activity-log-meta">
                        <?php foreach (array_slice($meta, 0, 5, true) as $key => $value) : ?>
                            <span><strong><?php echo esc_html($this->formatLabel((string) $key)); ?></strong><?php echo esc_html((string) $value); ?></span>
                        <?php endforeach; ?>
                    </div>
                <?php endif; ?>
                <?php if ($action['url'] !== '') : ?>
                    <a class="button button-small" href="<?php echo esc_url($action['url']); ?>"><?php echo esc_html($action['label']); ?></a>
                <?php endif; ?>
            </td>
            <td class="shieldx-activity-log-source">
                <?php if ($source['ip'] !== '') : ?>
                    <code><?php echo esc_html($source['ip']); ?></code>
                <?php endif; ?>
                <small><?php echo esc_html($source['event']); ?></small>
            </td>
        </tr>
        <?php
    }

    /**
     * @param array<string,mixed> $event
     * @return array{label:string,detail:string}
     */
    private function activityLogActor(array $event): array
    {
        $sourceEvent = (string) ($event['source_event'] ?? '');
        $meta = isset($event['meta']) && is_array($event['meta']) ? $event['meta'] : [];
        $userLogin = (string) ($meta['user_login'] ?? '');
        $userId = (string) ($meta['user_id'] ?? '');
        $role = (string) ($meta['role'] ?? '');

        if ($userLogin === '' && strpos($sourceEvent, 'login.') === 0) {
            $userLogin = (string) ($meta['username'] ?? $event['summary'] ?? '');
        }

        if ($userLogin === '' && $userId !== '') {
            $userLogin = 'User #' . $userId;
        }

        if ($userLogin === '') {
            $userLogin = (string) ($event['subject'] ?? '');
        }

        if ($userLogin === '') {
            $userLogin = __('Unknown user', 'xerotact-shield-x-security');
        }

        $detail = $role;
        if ($detail === '' && $userId !== '' && strpos($userLogin, 'User #') !== 0) {
            $detail = 'User #' . $userId;
        }

        return [
            'label' => $userLogin,
            'detail' => $detail,
        ];
    }

    /**
     * @param array<string,mixed> $event
     */
    private function activityLogVerb(array $event): string
    {
        $sourceEvent = (string) ($event['source_event'] ?? '');

        if ($sourceEvent === 'activity.user_login') {
            return __('Logged in', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'activity.user_logout') {
            return __('Logged out', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'activity.post_published') {
            return __('Published', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'activity.post_updated') {
            return __('Updated', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'activity.post_status_changed') {
            return __('Changed status', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'activity.post_deleted') {
            return __('Deleted', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'activity.post_meta_added') {
            return __('Added metadata', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'activity.post_meta_updated') {
            return __('Updated metadata', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'activity.post_meta_deleted') {
            return __('Deleted metadata', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'activity.post_terms_changed') {
            return __('Changed terms', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'activity.setting_added') {
            return __('Added setting', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'activity.setting_updated') {
            return __('Updated setting', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'activity.setting_deleted') {
            return __('Deleted setting', 'xerotact-shield-x-security');
        }

        if (strpos($sourceEvent, 'activity.plugin_') === 0) {
            return $this->formatLabel(str_replace('activity.plugin_', 'plugin_', $sourceEvent));
        }

        if (strpos($sourceEvent, 'activity.theme_') === 0) {
            return $this->formatLabel(str_replace('activity.theme_', 'theme_', $sourceEvent));
        }

        if ($sourceEvent === 'activity.core_updated') {
            return __('Updated WordPress core', 'xerotact-shield-x-security');
        }

        if (strpos($sourceEvent, 'activity.user_') === 0) {
            return $this->formatLabel(str_replace('activity.user_', 'user_', $sourceEvent));
        }

        if (strpos($sourceEvent, 'activity.term_') === 0) {
            return $this->formatLabel(str_replace('activity.term_', 'term_', $sourceEvent));
        }

        if (strpos($sourceEvent, 'activity.menu_') === 0) {
            return $this->formatLabel(str_replace('activity.menu_', 'menu_', $sourceEvent));
        }

        if (strpos($sourceEvent, 'activity.network_') === 0) {
            return $this->formatLabel(str_replace('activity.network_', 'network_', $sourceEvent));
        }

        if ($sourceEvent === 'activity.database_tables_changed') {
            return __('Changed tables', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'activity.site_files_changed') {
            return __('Changed files', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'login.failure') {
            return __('Failed login', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'login.lockout_created') {
            return __('Triggered lockout', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'login.lockout_block') {
            return __('Was blocked by lockout', 'xerotact-shield-x-security');
        }

        return (string) ($event['title'] ?? __('Activity event', 'xerotact-shield-x-security'));
    }

    /**
     * @param array<string,mixed> $event
     */
    private function activityLogTarget(array $event): string
    {
        $sourceEvent = (string) ($event['source_event'] ?? '');
        $meta = isset($event['meta']) && is_array($event['meta']) ? $event['meta'] : [];

        if (strpos($sourceEvent, 'activity.post_') === 0) {
            $title = (string) ($event['subject'] ?? '');
            $postType = (string) ($meta['post_type'] ?? __('Post', 'xerotact-shield-x-security'));
            $postId = (string) ($meta['post_id'] ?? '');
            if ($title !== '') {
                return $title;
            }

            return trim($postType . ($postId !== '' ? ' #' . $postId : ''));
        }

        if (strpos($sourceEvent, 'login.') === 0 || in_array($sourceEvent, ['activity.user_login', 'activity.user_logout'], true)) {
            return __('WordPress account', 'xerotact-shield-x-security');
        }

        return (string) ($event['subject'] ?? '');
    }

    /**
     * @param array<string,mixed> $event
     */
    private function activityLogTargetDetail(array $event): string
    {
        $sourceEvent = (string) ($event['source_event'] ?? '');
        $meta = isset($event['meta']) && is_array($event['meta']) ? $event['meta'] : [];

        if (strpos($sourceEvent, 'activity.post_') === 0) {
            $parts = array_filter([
                (string) ($meta['post_type'] ?? ''),
                isset($meta['post_id']) ? '#' . (string) $meta['post_id'] : '',
                (string) ($meta['post_status'] ?? ''),
            ]);

            return implode(' ', $parts);
        }

        if (strpos($sourceEvent, 'activity.setting_') === 0) {
            return (string) ($meta['setting_area'] ?? __('WordPress settings', 'xerotact-shield-x-security'));
        }

        if (strpos($sourceEvent, 'activity.plugin_') === 0) {
            return __('Plugin lifecycle', 'xerotact-shield-x-security');
        }

        if (strpos($sourceEvent, 'activity.theme_') === 0) {
            return __('Theme lifecycle', 'xerotact-shield-x-security');
        }

        if (strpos($sourceEvent, 'activity.term_') === 0) {
            return (string) ($meta['taxonomy'] ?? __('Taxonomy', 'xerotact-shield-x-security'));
        }

        if (strpos($sourceEvent, 'activity.menu_') === 0) {
            return __('Navigation menu', 'xerotact-shield-x-security');
        }

        if (strpos($sourceEvent, 'activity.network_') === 0) {
            return __('Multisite network', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'activity.database_tables_changed') {
            return __('Database inventory', 'xerotact-shield-x-security');
        }

        if ($sourceEvent === 'activity.site_files_changed') {
            return __('File shadow scan', 'xerotact-shield-x-security');
        }

        if (strpos($sourceEvent, 'login.') === 0 || in_array($sourceEvent, ['activity.user_login', 'activity.user_logout'], true)) {
            return __('Authentication event', 'xerotact-shield-x-security');
        }

        return '';
    }

    /**
     * @param array<string,mixed> $event
     */
    private function activityLogDetails(array $event): string
    {
        $sourceEvent = (string) ($event['source_event'] ?? '');
        $meta = isset($event['meta']) && is_array($event['meta']) ? $event['meta'] : [];
        $summary = (string) ($event['summary'] ?? '');

        if ($sourceEvent === 'activity.post_updated' && isset($meta['changed_fields'])) {
            return sprintf(
                /* translators: %s: comma-separated post fields changed. */
                __('Changed fields: %s', 'xerotact-shield-x-security'),
                (string) $meta['changed_fields']
            );
        }

        if ($sourceEvent === 'activity.post_status_changed') {
            return sprintf(
                /* translators: 1: old post status, 2: new post status. */
                __('Status: %1$s to %2$s', 'xerotact-shield-x-security'),
                (string) ($meta['old_status'] ?? ''),
                (string) ($meta['new_status'] ?? '')
            );
        }

        if (isset($meta['changed_fields'])) {
            return sprintf(
                /* translators: %s: comma-separated fields changed. */
                __('Fields changed: %s', 'xerotact-shield-x-security'),
                (string) $meta['changed_fields']
            );
        }

        if (isset($meta['old_value']) || isset($meta['new_value'])) {
            return sprintf(
                /* translators: 1: previous setting value, 2: new setting value. */
                __('Value: %1$s to %2$s', 'xerotact-shield-x-security'),
                (string) ($meta['old_value'] ?? ''),
                (string) ($meta['new_value'] ?? '')
            );
        }

        if (isset($meta['old_role']) || isset($meta['new_role'])) {
            return sprintf(
                /* translators: 1: previous role, 2: new role. */
                __('Role: %1$s to %2$s', 'xerotact-shield-x-security'),
                (string) ($meta['old_role'] ?? ''),
                (string) ($meta['new_role'] ?? '')
            );
        }

        if ($sourceEvent === 'activity.database_tables_changed') {
            return trim(sprintf(
                /* translators: 1: added database tables, 2: removed database tables. */
                __('Added: %1$s Removed: %2$s', 'xerotact-shield-x-security'),
                (string) ($meta['tables_added'] ?? ''),
                (string) ($meta['tables_removed'] ?? '')
            ));
        }

        if ($sourceEvent === 'activity.site_files_changed' && isset($meta['file_changes'])) {
            return sprintf(
                /* translators: %s: number of changed files. */
                __('Changed files: %s', 'xerotact-shield-x-security'),
                (string) $meta['file_changes']
            );
        }

        if (strpos($sourceEvent, 'login.') === 0 && !empty($event['subject'])) {
            return trim($summary . ' ' . sprintf(
                /* translators: %s: IP address involved in a login event. */
                __('from %s', 'xerotact-shield-x-security'),
                (string) $event['subject']
            ));
        }

        return $summary;
    }

    /**
     * @param array<string,mixed> $event
     * @return array{ip:string,event:string}
     */
    private function activityLogSource(array $event): array
    {
        $meta = isset($event['meta']) && is_array($event['meta']) ? $event['meta'] : [];
        $ip = (string) ($meta['ip'] ?? '');
        if ($ip === '') {
            $subject = (string) ($event['subject'] ?? '');
            if (filter_var($subject, FILTER_VALIDATE_IP)) {
                $ip = $subject;
            }
        }

        return [
            'ip' => $ip,
            'event' => (string) ($event['source_event'] ?? ''),
        ];
    }

    /**
     * @param array<string,mixed> $event
     * @return array{url:string,label:string}
     */
    private function activityLogAction(array $event): array
    {
        $sourceEvent = (string) ($event['source_event'] ?? '');
        $category = (string) ($event['category'] ?? '');
        $meta = isset($event['meta']) && is_array($event['meta']) ? $event['meta'] : [];

        if (strpos($sourceEvent, 'activity.post_') === 0) {
            $postId = (int) ($meta['post_id'] ?? 0);
            if ($postId > 0 && function_exists('get_edit_post_link')) {
                $url = get_edit_post_link($postId, '');
                if (is_string($url) && $url !== '') {
                    return [
                        'url' => $url,
                        'label' => __('Open post', 'xerotact-shield-x-security'),
                    ];
                }
            }
        }

        if ($category === 'login' || strpos($sourceEvent, 'login.') === 0 || strpos($sourceEvent, 'login_security.') === 0 || in_array($sourceEvent, ['activity.user_login', 'activity.user_logout'], true)) {
            return [
                'url' => add_query_arg(['page' => 'shield-x-login-security'], admin_url('admin.php')),
                'label' => __('Login security', 'xerotact-shield-x-security'),
            ];
        }

        if (strpos($sourceEvent, 'activity.user_') === 0 || strpos($sourceEvent, 'activity.network_user_') === 0) {
            return [
                'url' => admin_url('users.php'),
                'label' => __('Users', 'xerotact-shield-x-security'),
            ];
        }

        if (strpos($sourceEvent, 'activity.plugin_') === 0) {
            return [
                'url' => admin_url('plugins.php'),
                'label' => __('Plugins', 'xerotact-shield-x-security'),
            ];
        }

        if (strpos($sourceEvent, 'activity.theme_') === 0) {
            return [
                'url' => admin_url('themes.php'),
                'label' => __('Themes', 'xerotact-shield-x-security'),
            ];
        }

        if (strpos($sourceEvent, 'activity.menu_') === 0) {
            return [
                'url' => admin_url('nav-menus.php'),
                'label' => __('Menus', 'xerotact-shield-x-security'),
            ];
        }

        if (strpos($sourceEvent, 'activity.setting_') === 0) {
            $area = (string) ($meta['setting_area'] ?? '');
            $url = $area === 'Widgets' ? admin_url('widgets.php') : admin_url('options-general.php');

            return [
                'url' => $url,
                'label' => $area === 'Widgets' ? __('Widgets', 'xerotact-shield-x-security') : __('Settings', 'xerotact-shield-x-security'),
            ];
        }

        if ($sourceEvent === 'activity.site_files_changed') {
            return [
                'url' => admin_url('admin.php?page=shield-x-backup#shieldx-file-shadow-copies'),
                'label' => __('Review files', 'xerotact-shield-x-security'),
            ];
        }

        return ['url' => '', 'label' => ''];
    }

    /**
     * @param array<string,mixed> $timeline
     */
    private function renderIncidentTimeline(array $timeline): void
    {
        $events = isset($timeline['events']) && is_array($timeline['events']) ? $timeline['events'] : [];
        $categoryCounts = isset($timeline['category_counts']) && is_array($timeline['category_counts']) ? $timeline['category_counts'] : [];
        $severityCounts = isset($timeline['severity_counts']) && is_array($timeline['severity_counts']) ? $timeline['severity_counts'] : [];
        $criticalHigh = (int) ($severityCounts['critical'] ?? 0) + (int) ($severityCounts['high'] ?? 0);
        ?>
        <section class="shieldx-panel shieldx-panel--full shieldx-incident-panel" data-shieldx-accordion-open="1">
            <div class="shieldx-section-head">
                <h2><?php echo esc_html__('Incident Timeline', 'xerotact-shield-x-security'); ?></h2>
                <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                <span><?php echo esc_html(sprintf(__('%d merged security events', 'xerotact-shield-x-security'), count($events))); ?></span>
            </div>

            <div class="shieldx-incident-summary">
                <div>
                    <span><?php echo esc_html__('Critical / high', 'xerotact-shield-x-security'); ?></span>
                    <strong><?php echo esc_html((string) $criticalHigh); ?></strong>
                </div>
                <div>
                    <span><?php echo esc_html__('WAF detections / blocks', 'xerotact-shield-x-security'); ?></span>
                    <strong><?php echo esc_html((string) ((int) ($categoryCounts['waf'] ?? 0) + (int) ($categoryCounts['blocking'] ?? 0))); ?></strong>
                </div>
                <div>
                    <span><?php echo esc_html__('Login', 'xerotact-shield-x-security'); ?></span>
                    <strong><?php echo esc_html((string) ((int) ($categoryCounts['login'] ?? 0))); ?></strong>
                </div>
                <div>
                    <span><?php echo esc_html__('Scanner / DB', 'xerotact-shield-x-security'); ?></span>
                    <strong><?php echo esc_html((string) ((int) ($categoryCounts['scanner'] ?? 0) + (int) ($categoryCounts['database'] ?? 0))); ?></strong>
                </div>
            </div>

            <?php if (empty($events)) : ?>
                <p class="shieldx-empty"><?php echo esc_html__('No incident timeline events are available yet. WAF hits, login failures, scan findings, and lockdown changes will appear here.', 'xerotact-shield-x-security'); ?></p>
            <?php else : ?>
                <div class="shieldx-incident-timeline">
                    <div class="shieldx-incident-grid-head" aria-hidden="true">
                        <span><?php echo esc_html__('Time', 'xerotact-shield-x-security'); ?></span>
                        <span><?php echo esc_html__('Type', 'xerotact-shield-x-security'); ?></span>
                        <span><?php echo esc_html__('Event', 'xerotact-shield-x-security'); ?></span>
                        <span><?php echo esc_html__('Subject', 'xerotact-shield-x-security'); ?></span>
                        <span><?php echo esc_html__('Action', 'xerotact-shield-x-security'); ?></span>
                    </div>
                    <?php foreach ($events as $event) : ?>
                        <?php $this->renderIncidentTimelineRow(is_array($event) ? $event : []); ?>
                    <?php endforeach; ?>
                </div>
            <?php endif; ?>
        </section>
        <?php
    }

    /**
     * @param array<string,mixed> $event
     */
    private function renderIncidentTimelineRow(array $event): void
    {
        $severity = strtolower((string) ($event['severity'] ?? 'info'));
        $category = (string) ($event['category'] ?? 'system');
        $meta = isset($event['meta']) && is_array($event['meta']) ? $event['meta'] : [];
        $metaLimit = in_array($category, ['waf', 'blocking'], true) ? 6 : 3;
        $showMetaLabels = !in_array($category, ['waf', 'blocking'], true);
        $action = $this->incidentTimelineAction($event);
        $actions = $action['url'] !== '' ? [[
            'label' => $action['label'],
            'url' => $action['url'],
            'class' => 'button button-small',
        ]] : [];
        if (function_exists('apply_filters')) {
            $filtered = apply_filters('shieldx_incident_event_actions', $actions, $event, 'incident_timeline');
            $actions = is_array($filtered) ? $filtered : $actions;
        }
        ?>
        <article class="<?php echo esc_attr('shieldx-incident shieldx-incident--' . $severity . ' shieldx-incident--' . $category); ?>">
            <div class="shieldx-incident-time">
                <time><?php echo esc_html($this->formatTime((string) ($event['time'] ?? ''))); ?></time>
            </div>
            <div class="shieldx-incident-kind">
                <span class="<?php echo esc_attr('shieldx-severity shieldx-severity--' . $severity); ?>"><?php echo esc_html($this->formatLabel($severity)); ?></span>
                <span class="shieldx-incident-category"><?php echo esc_html($this->formatLabel($category)); ?></span>
                <?php if (!empty($event['state_event'])) : ?>
                    <span class="shieldx-incident-state"><?php echo esc_html__('Current', 'xerotact-shield-x-security'); ?></span>
                <?php endif; ?>
            </div>
            <div class="shieldx-incident-main">
                <strong><?php echo esc_html((string) ($event['title'] ?? __('Security event', 'xerotact-shield-x-security'))); ?></strong>
                <?php if (!empty($event['summary'])) : ?>
                    <span><?php echo esc_html((string) $event['summary']); ?></span>
                <?php endif; ?>
                <?php if (!empty($meta)) : ?>
                    <div class="shieldx-incident-meta">
                        <?php foreach (array_slice($meta, 0, $metaLimit, true) as $key => $value) : ?>
                            <span>
                                <?php if ($showMetaLabels) : ?>
                                    <strong><?php echo esc_html($this->formatLabel((string) $key)); ?></strong>
                                <?php endif; ?>
                                <?php echo esc_html((string) $value); ?>
                            </span>
                        <?php endforeach; ?>
                    </div>
                <?php endif; ?>
            </div>
            <div class="shieldx-incident-subject">
                <?php if (!empty($event['subject'])) : ?>
                    <code><?php echo esc_html((string) $event['subject']); ?></code>
                <?php else : ?>
                    <span class="shieldx-muted"><?php echo esc_html__('None', 'xerotact-shield-x-security'); ?></span>
                <?php endif; ?>
            </div>
            <div class="shieldx-incident-action">
                <?php if (!empty($actions)) : ?>
                    <?php foreach ($actions as $incidentAction) : ?>
                        <?php
                        $incidentAction = is_array($incidentAction) ? $incidentAction : [];
                        $incidentActionLabel = isset($incidentAction['label']) && is_scalar($incidentAction['label']) ? trim((string) $incidentAction['label']) : '';
                        $incidentActionUrl = isset($incidentAction['url']) && is_scalar($incidentAction['url']) ? (string) $incidentAction['url'] : '';
                        $incidentActionClass = isset($incidentAction['class']) && is_scalar($incidentAction['class']) ? (string) $incidentAction['class'] : 'button button-small';
                        $incidentActionClass = preg_replace('/[^A-Za-z0-9_\-\s]/', '', $incidentActionClass) ?: 'button button-small';
                        ?>
                        <?php if ($incidentActionLabel !== '' && $incidentActionUrl !== '') : ?>
                            <a class="<?php echo esc_attr($incidentActionClass); ?>" href="<?php echo esc_url($incidentActionUrl); ?>"><?php echo esc_html($incidentActionLabel); ?></a>
                        <?php endif; ?>
                    <?php endforeach; ?>
                <?php else : ?>
                    <span class="shieldx-muted"><?php echo esc_html__('Review', 'xerotact-shield-x-security'); ?></span>
                <?php endif; ?>
            </div>
        </article>
        <?php
    }

    /**
     * @param array<string,mixed> $event
     * @return array{url:string,label:string}
     */
    private function incidentTimelineAction(array $event): array
    {
        $category = (string) ($event['category'] ?? '');
        $meta = isset($event['meta']) && is_array($event['meta']) ? $event['meta'] : [];
        $threatId = (string) ($meta['threat_id'] ?? '');
        $subject = (string) ($event['subject'] ?? '');

        if ($category === 'database' && $threatId !== '') {
            return [
                'url' => add_query_arg(['page' => 'shield-x-scan', 'shieldx_db_review' => $threatId], admin_url('admin.php')) . '#shieldx-db-review',
                'label' => __('Review DB', 'xerotact-shield-x-security'),
            ];
        }

        if ($category === 'scanner' && $subject !== '' && strpos($subject, 'db:') !== 0) {
            return [
                'url' => add_query_arg(['page' => 'shield-x-scan', 'shieldx_review_file' => rawurlencode($subject)], admin_url('admin.php')) . '#shieldx-file-review',
                'label' => __('Review file', 'xerotact-shield-x-security'),
            ];
        }

        if ($category === 'waf' || $category === 'blocking') {
            return [
                'url' => add_query_arg(['page' => 'shield-x-waf'], admin_url('admin.php')),
                'label' => __('WAF settings', 'xerotact-shield-x-security'),
            ];
        }

        if ($category === 'activity') {
            return $this->activityLogAction($event);
        }

        if ($category === 'login' || $category === 'recovery') {
            return [
                'url' => add_query_arg(['page' => 'shield-x-login-security'], admin_url('admin.php')),
                'label' => __('Login security', 'xerotact-shield-x-security'),
            ];
        }

        if ($category === 'lockdown') {
            return [
                'url' => add_query_arg(['page' => 'shield-x-lockdown'], admin_url('admin.php')),
                'label' => __('Lockdown', 'xerotact-shield-x-security'),
            ];
        }

        return ['url' => '', 'label' => ''];
    }

    /**
     * @param array<string,mixed> $visit
     */
    private function renderUserProfilerVisitRow(array $visit, array $blockedIps = []): void
    {
        $visitorId = (string) ($visit['visitor_id'] ?? '');
        $methodPath = trim((string) ($visit['method'] ?? '') . ' ' . (string) ($visit['path'] ?? ''));
        $key = $this->profileKey($visit);
        $country = $this->countryBadgeForProfile($visit);
        $ip = (string) ($visit['ip'] ?? '');
        $isBlocked = $ip !== '' && in_array($ip, array_map('strval', $blockedIps), true);
        ?>
        <tr data-shieldx-profiler-visit="<?php echo esc_attr($key); ?>">
            <td><?php echo esc_html($this->formatTime((string) ($visit['visited_at'] ?? ''))); ?></td>
            <td>
                <div class="shieldx-user-identity shieldx-user-identity--compact">
                    <span class="shieldx-country-flag" title="<?php echo esc_attr($country['label']); ?>" aria-label="<?php echo esc_attr($country['label']); ?>"><?php echo esc_html($country['flag']); ?></span>
                    <div>
                        <strong><?php echo esc_html((string) ($visit['ip'] ?? 'unknown')); ?></strong>
                        <span><?php echo esc_html((string) ($visit['browser'] ?? 'Unknown')); ?></span>
                    </div>
                </div>
            </td>
            <td><code><?php echo esc_html($this->shortVisitorId($visitorId)); ?></code></td>
            <td>
                <code><?php echo esc_html($methodPath !== '' ? $methodPath : (string) ($visit['uri'] ?? '')); ?></code>
                <?php if (!empty($visit['is_admin'])) : ?>
                    <small><?php echo esc_html__('Admin surface', 'xerotact-shield-x-security'); ?></small>
                <?php endif; ?>
            </td>
            <td><code><?php echo esc_html((string) ($visit['query_string'] ?? '')); ?></code></td>
            <td><code><?php echo esc_html((string) ($visit['referer'] ?? '')); ?></code></td>
            <td>
                <?php if ($isBlocked) : ?>
                    <span class="shieldx-user-status shieldx-user-status--unknown"><?php echo esc_html__('Blocked', 'xerotact-shield-x-security'); ?></span>
                <?php elseif (filter_var($ip, FILTER_VALIDATE_IP)) : ?>
                    <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="shieldx-row-action-form">
                        <?php wp_nonce_field('shieldx_activity_quick_block'); ?>
                        <input type="hidden" name="action" value="shieldx_activity_quick_block">
                        <input type="hidden" name="block_type" value="ip">
                        <input type="hidden" name="block_value" value="<?php echo esc_attr($ip); ?>">
                        <button type="submit" class="button shieldx-row-action-button shieldx-button-danger"><?php echo esc_html__('Block IP', 'xerotact-shield-x-security'); ?></button>
                    </form>
                <?php else : ?>
                    <span class="shieldx-muted"><?php echo esc_html__('No IP', 'xerotact-shield-x-security'); ?></span>
                <?php endif; ?>
            </td>
        </tr>
        <?php
    }

    /**
     * @param array<int,mixed> $events
     * @return array<int,array{value:string,label:string,count:int,checked:bool}>
     */
    private function wafHitFilterOptions(array $events): array
    {
        $options = [
            'all' => [
                'label' => __('All events', 'xerotact-shield-x-security'),
                'count' => 0,
                'checked' => true,
            ],
            'critical' => [
                'label' => __('Critical / blocking', 'xerotact-shield-x-security'),
                'count' => 0,
                'checked' => false,
            ],
            'blocked' => [
                'label' => __('Blocked', 'xerotact-shield-x-security'),
                'count' => 0,
                'checked' => false,
            ],
            'would_block' => [
                'label' => __('Would block', 'xerotact-shield-x-security'),
                'count' => 0,
                'checked' => false,
            ],
            'high_score' => [
                'label' => __('High score (60+)', 'xerotact-shield-x-security'),
                'count' => 0,
                'checked' => false,
            ],
            'logged' => [
                'label' => __('Logged / monitor', 'xerotact-shield-x-security'),
                'count' => 0,
                'checked' => false,
            ],
        ];

        foreach ($events as $event) {
            if (!is_array($event)) {
                continue;
            }

            $options['all']['count']++;
            foreach ($this->wafHitFilterCategories($event) as $category) {
                if (isset($options[$category])) {
                    $options[$category]['count']++;
                }
            }
        }

        $result = [];
        foreach ($options as $value => $option) {
            $result[] = [
                'value' => $value,
                'label' => (string) $option['label'],
                'count' => (int) $option['count'],
                'checked' => (bool) $option['checked'],
            ];
        }

        return $result;
    }

    /**
     * @param array<string,mixed> $event
     * @return array<int,string>
     */
    private function wafHitFilterCategories(array $event): array
    {
        $categories = [];
        $action = (string) ($event['action'] ?? 'log');
        if ($action !== 'block' && !empty($event['would_block'])) {
            $action = 'would_block';
        }

        $score = (int) ($event['score'] ?? 0);
        if ($action === 'block') {
            $categories[] = 'blocked';
        } elseif ($action === 'would_block') {
            $categories[] = 'would_block';
        } else {
            $categories[] = 'logged';
        }

        if ($score >= 60) {
            $categories[] = 'high_score';
        }

        if ($action === 'block' || $action === 'would_block' || $score >= 60) {
            array_unshift($categories, 'critical');
        }

        return array_values(array_unique($categories));
    }

    /**
     * @param array<string,mixed>  $event
     * @param array<string,string> $ruleLabels
     */
    private function renderEventRow(array $event, array $ruleLabels = []): void
    {
        $rules = isset($event['rules']) && is_array($event['rules']) ? array_values(array_filter(array_map('strval', $event['rules']))) : [];
        $userAgent = (string) ($event['user_agent'] ?? '');
        $ip = (string) ($event['ip'] ?? 'unknown');
        $action = (string) ($event['action'] ?? 'log');
        if ($action !== 'block' && !empty($event['would_block'])) {
            $action = 'would_block';
        }
        $reason = (string) ($event['reason'] ?? '');
        $matchedValue = (string) ($event['matched_value'] ?? '');
        $method = strtoupper((string) ($event['method'] ?? ''));
        $score = (int) ($event['score'] ?? 0);
        $actionTone = $action === 'block' ? 'danger' : ($action === 'would_block' ? 'warn' : 'info');
        $scoreTone = $score >= 60 ? 'danger' : ($score >= 30 ? 'warn' : 'info');
        $eventTime = $this->formatEventTimeParts((string) ($event['time'] ?? ''));
        $reasonLabel = $reason !== '' ? $this->formatLabel($reason) : '';
        $scoreLabel = sprintf(
            /* translators: %s is the numeric WAF score for a request. */
            __('Matched score %s', 'xerotact-shield-x-security'),
            (string) $score
        );
        $accessStatus = $action === 'would_block' ? 'would_block' : ($action === 'block' ? 'blocked' : 'recent');
        $accessUrl = add_query_arg('status', $accessStatus, admin_url('admin.php?page=shield-x-waf-access'));
        $requestLabel = $method !== '' ? $method : __('Request', 'xerotact-shield-x-security');
        $titleLabel = $reasonLabel !== '' ? $reasonLabel : $requestLabel;
        $requestText = $userAgent !== ''
            ? $userAgent
            : ($matchedValue !== '' ? $matchedValue : (string) ($event['uri'] ?? ''));
        ?>
        <article class="<?php echo esc_attr('shieldx-event shieldx-event--' . $actionTone); ?>">
            <div class="shieldx-event-topline">
                <div class="shieldx-event-identity">
                    <span class="<?php echo esc_attr('shieldx-event-pill shieldx-event-pill--' . $actionTone); ?>"><?php echo esc_html($this->formatLabel($action)); ?></span>
                    <strong><?php echo esc_html($ip); ?></strong>
                </div>
                <time class="shieldx-event-client-time" datetime="<?php echo esc_attr($eventTime['datetime']); ?>">
                    <span><?php echo esc_html($eventTime['date']); ?></span>
                    <span><?php echo esc_html($eventTime['time']); ?></span>
                </time>
            </div>
            <div class="shieldx-event-main">
                <div class="shieldx-event-card-head">
                    <div class="shieldx-event-card-title">
                        <div class="shieldx-event-title-row">
                            <strong><?php echo esc_html($titleLabel); ?></strong>
                            <span class="<?php echo esc_attr('shieldx-event-score-badge shieldx-event-score-badge--' . $scoreTone); ?>" aria-label="<?php echo esc_attr($scoreLabel); ?>"><?php echo esc_html((string) $score); ?></span>
                        </div>
                    </div>
                </div>
                <?php if ($requestText !== '') : ?>
                    <div class="shieldx-event-request-strip">
                        <span class="shieldx-event-method"><?php echo esc_html($requestLabel); ?></span>
                        <span><?php echo esc_html($requestText); ?></span>
                    </div>
                <?php endif; ?>
                <?php if (!empty($event['score_threshold_action']) || !empty($event['body_truncated']) || !empty($event['budget_exhausted'])) : ?>
                <div class="shieldx-event-rule-score">
                    <?php if (!empty($event['score_threshold_action']) && (string) $event['score_threshold_action'] !== 'none') : ?>
                        <span class="shieldx-event-pill shieldx-event-pill--warn"><?php echo esc_html(sprintf(
                            /* translators: %s is the WAF threshold action. */
                            __('Threshold %s', 'xerotact-shield-x-security'),
                            $this->formatLabel((string) $event['score_threshold_action'])
                        )); ?></span>
                    <?php endif; ?>
                    <?php if (!empty($event['body_truncated'])) : ?>
                        <span class="shieldx-event-pill shieldx-event-pill--info"><?php echo esc_html__('Body truncated', 'xerotact-shield-x-security'); ?></span>
                    <?php endif; ?>
                    <?php if (!empty($event['budget_exhausted'])) : ?>
                        <span class="shieldx-event-pill shieldx-event-pill--warn"><?php echo esc_html__('Budget exhausted', 'xerotact-shield-x-security'); ?></span>
                    <?php endif; ?>
                </div>
                <?php endif; ?>
            </div>
            <div class="shieldx-event-footer">
                <?php if ($rules !== []) : ?>
                    <div class="shieldx-event-footer-rules">
                    <?php foreach ($rules as $ruleId) : ?>
                        <?php
                        $ruleName = (string) ($ruleLabels[$ruleId] ?? '');
                        $ruleTitleTarget = $ruleName !== '' ? $ruleName : $ruleId;
                        /* translators: %s is a WAF rule name or ID. */
                        $ruleTitle = sprintf(__('View %s rule', 'xerotact-shield-x-security'), $ruleTitleTarget);
                        ?>
                        <a class="shieldx-event-pill shieldx-event-pill--rule" href="#<?php echo esc_attr($this->wafRuleAnchor($ruleId)); ?>" title="<?php echo esc_attr($ruleTitle); ?>"><?php echo esc_html($ruleId); ?></a>
                    <?php endforeach; ?>
                    </div>
                <?php endif; ?>
                <a class="shieldx-card-link shieldx-event-access-link" href="<?php echo esc_url($accessUrl); ?>">
                    <span><?php echo esc_html__('Review in WAF Access', 'xerotact-shield-x-security'); ?></span>
                    <?php echo wp_kses(Iconoir::svg('nav-arrow-right', 'shieldx-icon shieldx-card-link-icon'), $this->allowedIconSvg()); ?>
                </a>
            </div>
        </article>
        <?php
    }

    /**
     * @param array<string,mixed> $profile
     */
    private function profileKey(array $profile): string
    {
        return md5((string) ($profile['visitor_id'] ?? '') . '|' . (string) ($profile['ip'] ?? '') . '|' . strtoupper((string) ($profile['country_code'] ?? '')) . '|' . (string) ($profile['browser'] ?? ''));
    }

    /**
     * @param array<string,mixed> $profile
     * @return array{flag:string,label:string}
     */
    private function countryBadgeForProfile(array $profile): array
    {
        $code = strtoupper(trim((string) ($profile['country_code'] ?? '')));

        if (preg_match('/^[A-Z]{2}$/', $code) === 1) {
            return [
                'flag' => $this->countryFlagEmoji($code),
                /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */
                'label' => sprintf(__('Country: %s', 'xerotact-shield-x-security'), $code),
            ];
        }

        if ($this->isPrivateIp((string) ($profile['ip'] ?? ''))) {
            return [
                'flag' => 'LAN',
                'label' => __('Local or private network', 'xerotact-shield-x-security'),
            ];
        }

        return [
            'flag' => '--',
            'label' => __('Country not detected', 'xerotact-shield-x-security'),
        ];
    }

    /**
     * @return array{label:string,type:string}
     */
    private function profileStatus(string $lastSeen): array
    {
        $timestamp = strtotime($lastSeen);

        if (!$timestamp) {
            return [
                'label' => __('Unknown', 'xerotact-shield-x-security'),
                'type' => 'unknown',
            ];
        }

        if ((time() - $timestamp) <= 900) {
            return [
                'label' => __('Active', 'xerotact-shield-x-security'),
                'type' => 'active',
            ];
        }

        return [
            'label' => __('Seen', 'xerotact-shield-x-security'),
            'type' => 'seen',
        ];
    }

    private function countryFlagEmoji(string $countryCode): string
    {
        $flag = '';

        for ($index = 0; $index < 2; $index += 1) {
            $flag .= html_entity_decode('&#' . (127397 + ord($countryCode[$index])) . ';', ENT_NOQUOTES, 'UTF-8');
        }

        return $flag;
    }

    private function isPrivateIp(string $ip): bool
    {
        if (filter_var($ip, FILTER_VALIDATE_IP) === false) {
            return false;
        }

        return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false;
    }

    private function shortVisitorId(string $visitorId): string
    {
        if (strlen($visitorId) <= 15) {
            return $visitorId !== '' ? $visitorId : __('unknown', 'xerotact-shield-x-security');
        }

        return substr($visitorId, 0, 6) . '...' . substr($visitorId, -6);
    }

    /**
     * @param array<string,mixed> $rule
     * @param array<string,int>   $ruleHits
     */
    private function renderRuleRow(array $rule, array $ruleHits): void
    {
        $id = (string) ($rule['id'] ?? '');
        $isEnabled = ($rule['enabled'] ?? true) !== false;
        $action = (string) ($rule['action'] ?? 'default');
        $fields = isset($rule['fields']) && is_array($rule['fields'])
            ? implode(', ', array_map('strval', $rule['fields']))
            : (string) ($rule['field'] ?? '');
        $references = array_filter([
            (string) ($rule['cwe'] ?? ''),
            (string) ($rule['owasp'] ?? ''),
        ]);
        ?>
        <tr id="<?php echo esc_attr($this->wafRuleAnchor($id)); ?>" class="<?php echo esc_attr($isEnabled ? '' : 'shieldx-rule-disabled'); ?>">
            <td class="shieldx-rule-toggle">
                <input type="hidden" name="all_rules[]" value="<?php echo esc_attr($id); ?>">
                <label>
                    <input type="checkbox" name="enabled_rules[]" value="<?php echo esc_attr($id); ?>" <?php checked($isEnabled); ?>>
                    <span><?php echo esc_html($isEnabled ? __('Enabled', 'xerotact-shield-x-security') : __('Disabled', 'xerotact-shield-x-security')); ?></span>
                </label>
            </td>
            <td>
                <strong><?php echo esc_html($id); ?></strong>
                <span><?php echo esc_html((string) ($rule['name'] ?? '')); ?></span>
                <small><?php echo esc_html((string) ($rule['description'] ?? '')); ?></small>
            </td>
            <td><?php echo esc_html((string) ($rule['category'] ?? '')); ?></td>
            <td>
                <code><?php echo esc_html($fields); ?></code>
                <?php /* translators: Placeholder values are generated from SHIELD-X status data, file paths, counts, or action names. */ ?>
                <small><?php echo esc_html(sprintf(__('%s patterns', 'xerotact-shield-x-security'), (string) ($rule['pattern_count'] ?? count((array) ($rule['patterns'] ?? []))))); ?></small>
            </td>
            <td><span class="<?php echo esc_attr('shieldx-severity shieldx-severity--' . strtolower((string) ($rule['severity'] ?? 'medium'))); ?>"><?php echo esc_html($this->formatLabel((string) ($rule['severity'] ?? 'medium'))); ?></span></td>
            <td>
                <?php echo esc_html((string) ($rule['source'] ?? '')); ?>
                <small><?php echo esc_html((string) ($rule['rule_family'] ?? '')); ?></small>
                <?php if (!empty($references)) : ?>
                    <small><?php echo esc_html(implode(' / ', $references)); ?></small>
                <?php endif; ?>
            </td>
            <td class="shieldx-rule-action">
                <select name="<?php echo esc_attr('rule_actions[' . $id . ']'); ?>">
                    <option value="default" <?php selected($action, 'default'); ?>><?php echo esc_html__('Default', 'xerotact-shield-x-security'); ?></option>
                    <option value="log" <?php selected($action, 'log'); ?>><?php echo esc_html__('Log only', 'xerotact-shield-x-security'); ?></option>
                    <option value="block" <?php selected($action, 'block'); ?>><?php echo esc_html__('Block', 'xerotact-shield-x-security'); ?></option>
                    <option value="challenge" <?php selected($action, 'challenge'); ?>><?php echo esc_html__('Challenge', 'xerotact-shield-x-security'); ?></option>
                </select>
            </td>
            <td><?php echo esc_html((string) ($ruleHits[$id] ?? 0)); ?></td>
        </tr>
        <?php
    }

    /**
     * @param array<string,mixed> $plugin
     * @param array<int,string>   $pluginWhitelist
     */
    private function renderPluginWhitelistRow(array $plugin, array $pluginWhitelist): void
    {
        $slug = (string) ($plugin['slug'] ?? '');

        if ($slug === '') {
            return;
        }

        $name = (string) ($plugin['name'] ?? $slug);
        $file = (string) ($plugin['file'] ?? '');
        $active = !empty($plugin['active']);
        ?>
        <label class="shieldx-plugin-item">
            <input type="checkbox" name="plugin_whitelist[]" value="<?php echo esc_attr($slug); ?>" <?php checked(in_array($slug, $pluginWhitelist, true)); ?>>
            <span>
                <strong><?php echo esc_html($name); ?></strong>
                <code><?php echo esc_html($slug); ?></code>
                <?php if ($file !== '') : ?>
                    <small><?php echo esc_html($file); ?></small>
                <?php endif; ?>
            </span>
            <em class="<?php echo esc_attr($active ? 'shieldx-plugin-state shieldx-plugin-state--active' : 'shieldx-plugin-state'); ?>">
                <?php echo esc_html($active ? __('Active', 'xerotact-shield-x-security') : __('Installed', 'xerotact-shield-x-security')); ?>
            </em>
        </label>
        <?php
    }

    private function renderNumberField(string $name, string $label, int $value, int $min, int $max): void
    {
        ?>
        <label class="shieldx-field">
            <span><?php echo esc_html($label); ?></span>
            <input type="number" name="<?php echo esc_attr($name); ?>" value="<?php echo esc_attr((string) $value); ?>" min="<?php echo esc_attr((string) $min); ?>" max="<?php echo esc_attr((string) $max); ?>" step="1">
        </label>
        <?php
    }

    private static function manageCapability(): string
    {
        return defined('SHIELDX_MANAGE_CAPABILITY') ? (string) SHIELDX_MANAGE_CAPABILITY : 'manage_options';
    }

    private static function currentUserCanManageShieldX(): bool
    {
        static $canManage = null;

        if ($canManage !== null) {
            return $canManage;
        }

        $canManage = function_exists('current_user_can') && current_user_can(self::manageCapability());

        return $canManage;
    }

    private function assertCanSave(string $nonceAction): void
    {
        if (!self::currentUserCanManageShieldX()) {
            $this->denySavePermission();
        }

        check_admin_referer($nonceAction);
    }

    private function canSaveWithNonce(string $nonceAction, string $nonce): bool
    {
        return self::currentUserCanManageShieldX()
            && $nonce !== ''
            && wp_verify_nonce($nonce, $nonceAction) !== false;
    }

    private function assertCanSaveWithNonce(string $nonceAction, string $nonce): void
    {
        if (!self::currentUserCanManageShieldX()) {
            $this->denySavePermission();
        }

        if ($nonce === '' || wp_verify_nonce($nonce, $nonceAction) === false) {
            wp_die(esc_html__('The SHIELD-X security check failed. Please reload the page and try again.', 'xerotact-shield-x-security'));
        }
    }

    private function denySavePermission(): void
    {
        $message = __('You do not have permission to change SHIELD-X settings.', 'xerotact-shield-x-security');

        if (function_exists('wp_doing_ajax') && wp_doing_ajax() && function_exists('wp_send_json_error')) {
            wp_send_json_error(['message' => $message], 403);
        }

        wp_die(esc_html($message), '', ['response' => 403]);
    }

    private function targetUserCanManageOptions(int $userId): bool
    {
        if ($userId <= 0 || !function_exists('get_userdata')) {
            return false;
        }

        $user = get_userdata($userId);
        if (!is_object($user) || !isset($user->ID)) {
            return false;
        }

        if (method_exists($user, 'has_cap')) {
            return (bool) $user->has_cap('manage_options');
        }

        $caps = isset($user->allcaps) && is_array($user->allcaps) ? $user->allcaps : [];

        return !empty($caps['manage_options']);
    }

    private function currentUserCanEditTargetUser(int $userId): bool
    {
        if ($userId <= 0 || !function_exists('current_user_can')) {
            return false;
        }

        return current_user_can('edit_user', $userId);
    }

    private function prepareLongAdminAction(): void
    {
        if (function_exists('wp_raise_memory_limit')) {
            wp_raise_memory_limit('admin');
        }

        if (function_exists('set_time_limit')) {
            // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Explicit admin scan/backup actions can exceed default PHP time limits on shared hosting.
            set_time_limit(300);
        }
    }

    private function isShieldXAdminScreen(): bool
    {
        $screen = function_exists('get_current_screen') ? get_current_screen() : null;
        if ($screen && isset($screen->id) && $this->isShieldXAdminScreenId((string) $screen->id)) {
            return true;
        }

        $page = $this->queryText('page');

        return $page === 'shield-x' || strpos($page, 'shield-x-') === 0;
    }

    private function isShieldXAdminScreenId(string $screenId): bool
    {
        return $screenId === 'toplevel_page_shield-x'
            || preg_match('/(?:^|_)page_shield-x(?:-.+)?$/', $screenId) === 1
            || preg_match('/^shield-x(?:-.+)?$/', $screenId) === 1;
    }

    // phpcs:disable WordPress.Security.NonceVerification.Recommended,WordPress.Security.NonceVerification.Missing -- These helpers only read request values after each admin-post handler has called assertCanSave(), or render read-only admin panels from GET state.
    /**
     * Build a safe SHIELD-X admin URL from the real admin page context.
     *
     * Async admin sections are rendered through admin-ajax.php, so WordPress'
     * default remove_query_arg() base URL can accidentally produce close links
     * pointing at admin-ajax.php. Use the copied render context instead.
     *
     * @param array<int,string> $removeKeys
     */
    private function adminPageUrlWithout(array $removeKeys, string $fallbackPage): string
    {
        $args = $this->adminPageQueryArgs($fallbackPage);

        foreach ($removeKeys as $key) {
            unset($args[sanitize_key($key)]);
        }

        if (empty($args['page'])) {
            $args['page'] = sanitize_key($fallbackPage);
        }

        return add_query_arg($args, admin_url('admin.php'));
    }

    /**
     * @return array<string,string>
     */
    private function adminPageQueryArgs(string $fallbackPage): array
    {
        $source = [];

        if (!empty($this->ajaxQueryContext)) {
            $source = $this->ajaxQueryContext;
        } else {
            foreach ($_GET as $key => $value) {
                if (!is_scalar($value)) {
                    continue;
                }

                $source[(string) $key] = (string) wp_unslash($value);
            }
        }

        $args = [];
        foreach ($source as $key => $value) {
            $key = sanitize_key((string) $key);
            if ($key === '' || in_array($key, ['action', '_ajax_nonce', 'context'], true) || !is_scalar($value)) {
                continue;
            }

            $args[$key] = sanitize_text_field(rawurldecode((string) $value));
        }

        $page = sanitize_key((string) ($args['page'] ?? $fallbackPage));
        $args['page'] = $page !== '' ? $page : sanitize_key($fallbackPage);

        return $args;
    }

    private function queryFlag(string $key): bool
    {
        return $this->queryText($key) === '1';
    }

    private function queryText(string $key, string $default = ''): string
    {
        $value = $this->queryRaw($key);
        if ($value === null) {
            return $default;
        }

        if (in_array($key, self::FLASH_QUERY_KEYS, true)) {
            return $value === '1' ? AdminFlashNotice::consume($key) : $default;
        }

        return sanitize_text_field(rawurldecode($value));
    }

    private function queryKey(string $key, string $default = ''): string
    {
        $value = $this->queryText($key, $default);

        return $value === '' ? $default : sanitize_key($value);
    }

    private function queryInt(string $key, int $default = 0): int
    {
        $value = $this->queryText($key, '');

        return $value === '' ? $default : max(0, (int) $value);
    }

    private function queryRaw(string $key): ?string
    {
        if (array_key_exists($key, $this->ajaxQueryContext)) {
            return $this->ajaxQueryContext[$key];
        }

        if (!isset($_GET[$key]) || !is_scalar($_GET[$key])) {
            return null;
        }

        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Dynamic query value is unslashed here and sanitized by queryText().
        $value = wp_unslash($_GET[$key]);

        return is_scalar($value) ? (string) $value : null;
    }

    /**
     * @return array<int,string>
     */
    private function toastQueryKeys(): array
    {
        return self::TOAST_QUERY_KEYS;
    }

    private function postFlag(string $key): bool
    {
        return isset($_POST[$key]);
    }

    /**
     * @return array<int,string>
     */
    private function postedArray(string $key): array
    {
        if (!isset($_POST[$key]) || !is_array($_POST[$key])) {
            return [];
        }

        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Dynamic posted array is unslashed here and every scalar value is sanitized below.
        $values = wp_unslash($_POST[$key]);
        $posted = [];

        foreach ((array) $values as $value) {
            if (is_scalar($value)) {
                $posted[] = sanitize_text_field((string) $value);
            }
        }

        return $posted;
    }

    /**
     * @param array<string,mixed> $current
     * @return array<string,array<string,mixed>>
     */
    private function postedEndpointProfiles(array $current): array
    {
        // phpcs:disable WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- The hardening save handler verifies its nonce before this helper; every supported scalar is sanitized and normalized below.
        $posted = isset($_POST['endpoint_profiles']) && is_array($_POST['endpoint_profiles'])
            ? wp_unslash($_POST['endpoint_profiles'])
            : [];
        // phpcs:enable WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
        $profiles = [];

        foreach (RateLimitSettings::ENDPOINT_PROFILES as $profile) {
            $values = isset($posted[$profile]) && is_array($posted[$profile]) ? $posted[$profile] : [];
            $existing = isset($current[$profile]) && is_array($current[$profile]) ? $current[$profile] : [];
            $profiles[$profile] = [
                'enabled' => isset($values['enabled']),
                'mode' => isset($values['mode']) && is_scalar($values['mode']) ? sanitize_key((string) $values['mode']) : (string) ($existing['mode'] ?? 'log_only'),
                'limit' => isset($values['limit']) && is_numeric($values['limit']) ? (int) $values['limit'] : (int) ($existing['limit'] ?? 30),
                'window_seconds' => isset($values['window_seconds']) && is_numeric($values['window_seconds']) ? (int) $values['window_seconds'] : (int) ($existing['window_seconds'] ?? 60),
                'burst_limit' => isset($values['burst_limit']) && is_numeric($values['burst_limit']) ? (int) $values['burst_limit'] : (int) ($existing['burst_limit'] ?? 10),
                'burst_window_seconds' => isset($values['burst_window_seconds']) && is_numeric($values['burst_window_seconds']) ? (int) $values['burst_window_seconds'] : (int) ($existing['burst_window_seconds'] ?? 10),
            ];
        }

        return $profiles;
    }

    /**
     * @return array<string,string>
     */
    private function postedKeyedArray(string $key): array
    {
        // phpcs:disable WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Every caller verifies its action-specific nonce; this helper sanitizes every key and value below.
        if (!isset($_POST[$key]) || !is_array($_POST[$key])) {
            return [];
        }

        $values = wp_unslash($_POST[$key]);
        // phpcs:enable WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
        $posted = [];

        foreach ((array) $values as $postedKey => $value) {
            if (is_scalar($postedKey) && is_scalar($value)) {
                $cleanKey = sanitize_text_field((string) $postedKey);

                if ($cleanKey !== '') {
                    $posted[$cleanKey] = sanitize_text_field((string) $value);
                }
            }
        }

        return $posted;
    }

    /**
     * Keeps permission repair details out of the redirect URL while still
     * letting admins see exactly which host-owned paths need manual action.
     *
     * @param array<string,mixed> $report
     */
    private function savePermissionFixReport(array $report): string
    {
        if (function_exists('wp_generate_uuid4')) {
            $id = (string) wp_generate_uuid4();
        } elseif (function_exists('wp_rand')) {
            $id = md5((string) wp_rand() . '|' . (string) microtime(true));
        } else {
            $id = md5((string) microtime(true));
        }

        $id = sanitize_key($id);

        if ($id === '') {
            return '';
        }

        $ttl = defined('DAY_IN_SECONDS') ? (int) DAY_IN_SECONDS : 86400;
        set_transient('shieldx_permission_fix_' . $id, $this->normalizePermissionFixReport($report), $ttl);

        return $id;
    }

    /**
     * @return array<string,mixed>
     */
    private function loadPermissionFixReport(string $id): array
    {
        $id = sanitize_key($id);
        if ($id === '') {
            return [];
        }

        $report = get_transient('shieldx_permission_fix_' . $id);

        return is_array($report) ? $this->normalizePermissionFixReport($report) : [];
    }

    /**
     * @param array<string,mixed> $report
     * @return array<string,mixed>
     */
    private function normalizePermissionFixReport(array $report): array
    {
        $failed = [];
        $scopes = [];

        foreach ((array) ($report['failed'] ?? []) as $failure) {
            if (!is_array($failure)) {
                continue;
            }

            $path = isset($failure['path']) && is_scalar($failure['path']) ? sanitize_text_field((string) $failure['path']) : '';
            if ($path === '') {
                continue;
            }

            $type = isset($failure['type']) && is_scalar($failure['type']) ? sanitize_key((string) $failure['type']) : 'path';
            $mode = isset($failure['target_mode']) && is_scalar($failure['target_mode']) ? sanitize_text_field((string) $failure['target_mode']) : '';
            $reason = isset($failure['reason']) && is_scalar($failure['reason']) ? sanitize_text_field((string) $failure['reason']) : '';

            $failed[] = [
                'path' => $path,
                'type' => in_array($type, ['file', 'folder', 'path'], true) ? $type : 'path',
                'target_mode' => preg_match('/^0?[0-7]{3,4}$/', $mode) ? sprintf('%04o', octdec($mode)) : '',
                'reason' => $reason !== '' ? $reason : __('Permission change failed.', 'xerotact-shield-x-security'),
            ];

            if (count($failed) >= 100) {
                break;
            }
        }

        foreach ((array) ($report['scopes'] ?? []) as $scope) {
            if (!is_scalar($scope)) {
                continue;
            }

            $scope = sanitize_key((string) $scope);
            if ($scope !== '') {
                $scopes[] = $scope;
            }
        }

        return [
            'created_at' => isset($report['created_at']) && is_scalar($report['created_at']) ? sanitize_text_field((string) $report['created_at']) : '',
            'files' => max(0, (int) ($report['files'] ?? 0)),
            'directories' => max(0, (int) ($report['directories'] ?? 0)),
            'unchanged' => max(0, (int) ($report['unchanged'] ?? 0)),
            'skipped' => max(0, (int) ($report['skipped'] ?? 0)),
            'failed_total' => max(count($failed), (int) ($report['failed_total'] ?? count($failed))),
            'failed' => $failed,
            'scopes' => array_values(array_unique($scopes)),
        ];
    }

    /**
     * @return array<string,mixed>
     */
    private function postedLockdownPolicy(): array
    {
        return LockdownSettings::normalizePolicy([
            'read_only_enabled' => $this->postFlag('lockdown_read_only_enabled'),
            'no_exec_enabled' => $this->postFlag('lockdown_no_exec_enabled'),
            'disable_file_mods' => $this->postFlag('lockdown_disable_file_mods'),
            'directory_index_off' => $this->postFlag('lockdown_directory_index_off'),
            'allowlist' => $this->postedText('lockdown_allowlist', ''),
        ]);
    }

    private function postedText(string $key, string $default): string
    {
        // phpcs:disable WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Admin form handlers verify their nonce first; this helper sanitizes the unslashed scalar before return.
        if (!isset($_POST[$key]) || !is_scalar($_POST[$key])) {
            return $default;
        }

        $value = wp_unslash($_POST[$key]);
        // phpcs:enable WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

        return is_scalar($value) ? sanitize_textarea_field((string) $value) : $default;
    }

    private function postedRawText(string $key): string
    {
        // phpcs:disable WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- The only caller verifies its admin nonce before preserving complete editor contents byte-for-byte.
        if (!isset($_POST[$key]) || !is_scalar($_POST[$key])) {
            return '';
        }

        $value = wp_unslash($_POST[$key]);
        // phpcs:enable WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

        return is_scalar($value) ? (string) $value : '';
    }

    private function postedRelativeFilePath(string $key): string
    {
        return self::sanitizeRelativeFilePath($this->postedText($key, ''));
    }

    private function postedFileRoutePath(string $legacyKey): string
    {
        $path = $this->fileRoutePathFromToken($this->postedText('shieldx_file_route_token', ''));

        return $path !== '' ? $path : $this->postedRelativeFilePath($legacyKey);
    }

    private static function sanitizeRelativeFilePath(string $path): string
    {
        $path = rawurldecode($path);
        $path = ScanRuleEngine::normalizePath($path);

        if ($path === '' || strpos($path, "\0") !== false || preg_match('~^[A-Za-z]:~', $path)) {
            return '';
        }

        foreach (explode('/', $path) as $segment) {
            if ($segment === '' || $segment === '.' || $segment === '..') {
                return '';
            }
        }

        return $path;
    }

    private function postedInt(string $key, int $default): int
    {
        // phpcs:disable WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Admin form handlers verify their nonce first; this helper accepts only an unslashed numeric scalar.
        if (!isset($_POST[$key]) || !is_scalar($_POST[$key])) {
            return $default;
        }

        $value = wp_unslash($_POST[$key]);
        // phpcs:enable WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

        return is_numeric($value) ? (int) $value : $default;
    }

    /**
     * @param array<string,string> $query
     */
    private function redirectLockdown(array $query): void
    {
        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-lockdown')));
        exit;
    }

    /**
     * @param array<string,mixed> $query
     * @return array<string,mixed>
     */
    private function protectRedirectQuery(array $query): array
    {
        return AdminFlashNotice::protectQuery($query, self::FLASH_QUERY_KEYS);
    }

    private function requestedFileRoutePath(string $tokenKey, string $legacyKey): string
    {
        $path = $this->fileRoutePathFromToken($this->queryText($tokenKey));

        return $path !== '' ? $path : $this->queryText($legacyKey);
    }

    private function fileRouteToken(string $path): string
    {
        $path = $this->normalizeReviewPath($path);
        $key = $this->fileRouteTokenKey();
        if ($path === '' || $key === '') {
            return '';
        }

        $encrypted = CryptoBox::protect($path, $key);
        if ($encrypted === '') {
            return '';
        }

        return rtrim(strtr(base64_encode($encrypted), '+/', '-_'), '=');
    }

    private function fileRoutePathFromToken(string $token): string
    {
        $token = trim($token);
        $key = $this->fileRouteTokenKey();
        if ($token === '' || $key === '' || strlen($token) > 2048 || preg_match('/^[A-Za-z0-9_-]+$/', $token) !== 1) {
            return '';
        }

        $padding = strlen($token) % 4;
        $encrypted = base64_decode(strtr($token, '-_', '+/') . ($padding === 0 ? '' : str_repeat('=', 4 - $padding)), true);
        if (!is_string($encrypted)) {
            return '';
        }

        $path = CryptoBox::reveal($encrypted, $key);
        $normalized = $this->normalizeReviewPath($path);

        return $path !== '' && hash_equals($path, $normalized) ? $normalized : '';
    }

    private function fileRouteTokenKey(): string
    {
        $salt = function_exists('wp_salt') ? (string) wp_salt('secure_auth') : '';

        return $salt !== '' ? hash('sha256', $salt . '|shieldx-file-route-token', true) : '';
    }

    /**
     * @param array<string,mixed> $result
     * @return array<string,string>
     */
    private function lockdownResultRedirectQuery(array $result, string $successKey, string $failureMessage): array
    {
        if (empty($result['ok'])) {
            return [
                'shieldx_lockdown_error' => (string) ($result['message'] ?? $failureMessage),
            ];
        }

        $query = [$successKey => '1'];
        $warning = trim((string) ($result['restore_point_warning'] ?? ''));
        if ($warning !== '') {
            $query['shieldx_lockdown_warning'] = $warning;
        }

        return $query;
    }

    /**
     * @param array<string,mixed> $policy
     */
    private function storeLockdownPreviewPolicy(array $policy): void
    {
        if (!function_exists('set_transient')) {
            return;
        }

        $ttl = defined('MINUTE_IN_SECONDS') ? 10 * MINUTE_IN_SECONDS : 600;
        set_transient($this->lockdownPreviewTransientKey(), LockdownSettings::normalizePolicy($policy), $ttl);
    }

    /**
     * @return array<string,mixed>
     */
    private function loadLockdownPreviewPolicy(): array
    {
        if (!function_exists('get_transient')) {
            return [];
        }

        $policy = get_transient($this->lockdownPreviewTransientKey());

        return is_array($policy) ? LockdownSettings::normalizePolicy($policy) : [];
    }

    private function clearLockdownPreviewPolicy(): void
    {
        if (function_exists('delete_transient')) {
            delete_transient($this->lockdownPreviewTransientKey());
        }
    }

    private function lockdownPreviewTransientKey(): string
    {
        $userId = function_exists('get_current_user_id') ? max(0, (int) get_current_user_id()) : 0;
        $blogId = function_exists('get_current_blog_id') ? max(0, (int) get_current_blog_id()) : 0;
        $sessionToken = function_exists('wp_get_session_token') ? (string) wp_get_session_token() : '';
        $sessionSuffix = $sessionToken !== '' ? '_' . hash('sha256', $sessionToken) : '';

        return 'shieldx_lockdown_preview_' . (string) $blogId . '_' . (string) $userId . $sessionSuffix;
    }

    /**
     * @param array<string,string> $query
     */
    private function redirectSystemImport(array $query): void
    {
        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-system')));
        exit;
    }

    /**
     * @param array<string,string> $query
     */
    private function redirectLogRotation(array $query): void
    {
        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-log-rotation')));
        exit;
    }

    /**
     * @param array<string,mixed> $result
     */
    private function logRotationScanCompletionUrl(array $result): string
    {
        return add_query_arg([
            'shieldx_log_scan' => '1',
            'shieldx_log_scan_marked' => (string) (int) ($result['marked_files'] ?? 0),
            'shieldx_log_scan_total' => (string) (int) ($result['total_files'] ?? 0),
        ], admin_url('admin.php?page=shield-x-log-rotation'));
    }

    private function logRotationScanContinueUrl(string $scanId, bool $ajax = false): string
    {
        if (preg_match('/^[a-f0-9]{32}$/i', $scanId) !== 1) {
            return admin_url('admin.php?page=shield-x-log-rotation');
        }

        $query = [
            'action' => 'shieldx_scan_log_rotation',
            'scan_id' => strtolower($scanId),
            '_wpnonce' => wp_create_nonce('shieldx_scan_log_rotation'),
        ];

        if ($ajax) {
            $query['shieldx_ajax_progress'] = '1';
        }

        return add_query_arg($query, admin_url('admin-post.php'));
    }

    /**
     * @param array<string,string> $query
     */
    private function redirectActivityBlock(array $query): void
    {
        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-activity')));
        exit;
    }

    /**
     * @param array{ok:bool,message:string,path:string} $result
     */
    private function redirectScanRemediation(array $result, string $successKey): void
    {
        $query = !empty($result['ok'])
            ? [$successKey => '1']
            : ['shieldx_scan_action_error' => AdminFlashNotice::flag('shieldx_scan_action_error', (string) ($result['message'] ?? __('Scanner action failed.', 'xerotact-shield-x-security')))];

        if (!empty($result['path'])) {
            $query['shieldx_action_path'] = (string) $result['path'];
        }

        if (!empty($result['review_threat_id'])) {
            $query['shieldx_db_review'] = rawurlencode((string) $result['review_threat_id']);
        }

        if (isset($result['recheck']) && is_array($result['recheck'])) {
            $query['shieldx_recheck_status'] = (string) ($result['recheck']['status'] ?? 'clean');
            $query['shieldx_recheck_count'] = (string) ((int) ($result['recheck']['remaining_count'] ?? 0));
        }

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-scan')));
        exit;
    }

    /**
     * Keep an official plugin comparison visible after an explicit trust or
     * edit decision, including a targeted recheck result or safe error notice.
     *
     * @param array{ok:bool,message:string,path:string} $result
     */
    private function redirectPluginCompareRemediation(array $result, string $successKey): void
    {
        $path = self::sanitizeRelativeFilePath((string) ($result['path'] ?? ''));
        $query = !empty($result['ok'])
            ? [$successKey => '1']
            : ['shieldx_scan_action_error' => AdminFlashNotice::flag('shieldx_scan_action_error', (string) ($result['message'] ?? __('Plugin comparison action failed.', 'xerotact-shield-x-security')))];

        if ($path !== '') {
            $query['shieldx_plugin_compare'] = $path;
            $query['shieldx_action_path'] = $path;
        }

        if (isset($result['recheck']) && is_array($result['recheck'])) {
            $query['shieldx_recheck_status'] = (string) ($result['recheck']['status'] ?? 'clean');
            $query['shieldx_recheck_count'] = (string) ((int) ($result['recheck']['remaining_count'] ?? 0));
        }

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-scan')) . '#shieldx-plugin-compare-result');
        exit;
    }

    /**
     * @param array{ok:bool,message:string,path:string} $result
     */
    private function redirectProtectedFileAction(array $result, string $successKey): void
    {
        $path = (string) ($result['path'] ?? '');
        $query = !empty($result['ok'])
            ? [$successKey => '1']
            : ['shieldx_scan_action_error' => AdminFlashNotice::flag('shieldx_scan_action_error', (string) ($result['message'] ?? __('Protected file action failed.', 'xerotact-shield-x-security')))];

        if ($path !== '') {
            $query['shieldx_protected_file_token'] = $this->fileRouteToken($path);
        }

        if (isset($result['recheck']) && is_array($result['recheck'])) {
            $query['shieldx_recheck_status'] = (string) ($result['recheck']['status'] ?? 'clean');
            $query['shieldx_recheck_count'] = (string) ((int) ($result['recheck']['remaining_count'] ?? 0));
        }

        if (isset($result['removed_count'])) {
            $query['shieldx_removed_lines'] = (string) max(0, (int) $result['removed_count']);
        }

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-scan')) . ($path !== '' ? '#shieldx-protected-file' : ''));
        exit;
    }

    /**
     * Route ordinary scanner actions back to the scanner results. The
     * protected-file panel only understands the small protected-file list, so
     * sending a plugin or theme file there produces a misleading repair error.
     *
     * @param array{ok:bool,message:string,path:string} $result
     */
    private function redirectFileRemediation(array $result, string $successKey): void
    {
        $path = (string) ($result['path'] ?? '');
        if (ScannerRemediation::isProtectedQuarantinePath($path)) {
            $this->redirectProtectedFileAction($result, $successKey);

            return;
        }

        $this->redirectScanRemediation($result, $successKey);
    }

    /**
     * @return array{ok:bool,message:string,path:string}
     */
    private function missingScannerFileResult(string $path): array
    {
        return [
            'ok' => false,
            'message' => __('The selected file no longer exists.', 'xerotact-shield-x-security'),
            'path' => $path,
        ];
    }

    private function redirectAfterSave(string $page): void
    {
        $allowed = ['shield-x', 'shield-x-setup', 'shield-x-addons', 'shield-x-waf', 'shield-x-waf-access', 'shield-x-login-security', 'shield-x-scan', 'shield-x-backup', 'shield-x-hardening', 'shield-x-lockdown', 'shield-x-activity', 'shield-x-log-rotation', 'shield-x-system'];

        if (!in_array($page, $allowed, true)) {
            $page = 'shield-x';
        }

        wp_safe_redirect(add_query_arg('shieldx_saved', '1', admin_url('admin.php?page=' . $page)));
        exit;
    }

    /**
     * @param array<string,string> $query
     */
    private function redirectWafHitAction(array $query): void
    {
        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-waf')) . '#shieldx-waf-latest-hits');
        exit;
    }

    private function redirectWafSettingsError(string $message): void
    {
        AdminFlashNotice::store('shieldx_waf_error', $message);
        wp_safe_redirect(add_query_arg('shieldx_waf_error', '1', admin_url('admin.php?page=shield-x-waf')) . '#shieldx-waf-reputation-feed');
        exit;
    }

    /**
     * @param array<string,mixed> $result
     */
    private function redirectModuleResult(array $result): void
    {
        $query = !empty($result['ok'])
            ? ['shieldx_module_notice' => AdminFlashNotice::flag('shieldx_module_notice', (string) ($result['message'] ?? __('Module action completed.', 'xerotact-shield-x-security')))]
            : ['shieldx_module_error' => AdminFlashNotice::flag('shieldx_module_error', (string) ($result['message'] ?? __('Module action failed.', 'xerotact-shield-x-security')))];

        if (!empty($result['id'])) {
            $query['shieldx_module_id'] = rawurlencode((string) $result['id']);
        }

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=shield-x-addons')));
        exit;
    }

    /**
     * @param array{ok:bool,status:string,message:string} $result
     */
    private function redirectLicenseResult(array $result, string $successKey, string $page): void
    {
        $page = $this->allowedReturnPage($page);
        $query = !empty($result['ok'])
            ? [$successKey => '1']
            : ['shieldx_license_error' => AdminFlashNotice::flag('shieldx_license_error', (string) ($result['message'] ?? __('License action failed.', 'xerotact-shield-x-security')))];

        wp_safe_redirect(add_query_arg($this->protectRedirectQuery($query), admin_url('admin.php?page=' . $page)));
        exit;
    }

    private function allowedReturnPage(string $page): string
    {
        $allowed = [
            'shield-x',
            'shield-x-setup',
            'shield-x-addons',
            'shield-x-waf',
            'shield-x-waf-access',
            'shield-x-login-security',
            'shield-x-scan',
            'shield-x-backup',
            'shield-x-hardening',
            'shield-x-lockdown',
            'shield-x-activity',
            'shield-x-log-rotation',
            'shield-x-system',
        ];

        if (!in_array($page, $allowed, true)) {
            $page = 'shield-x';
        }

        return $page;
    }

    /**
     * @param array<int,string> $paths
     */
    private function createPreChangeRestorePoint(string $reason, array $paths = []): void
    {
        try {
            RestorePointManager::fromWordPress()->create(
                $reason,
                array_values(array_filter(array_map('strval', $paths))),
                function_exists('get_current_user_id') ? (int) get_current_user_id() : 0,
                true
            );
        } catch (\Throwable $error) {
            // Restore points are a safety layer; the requested action still reports its own result.
        }
    }

    /**
     * Direct editor writes are blocked unless a complete, non-truncated restore
     * point was created first. This is stricter than convenience remediation
     * actions because the editor accepts arbitrary complete file contents.
     *
     * @param array<int,string> $paths
     * @return array<string,mixed>
     */
    private function createRequiredPreChangeRestorePoint(string $reason, array $paths = []): array
    {
        try {
            $result = RestorePointManager::fromWordPress()->create(
                $reason,
                array_values(array_filter(array_map('strval', $paths))),
                function_exists('get_current_user_id') ? (int) get_current_user_id() : 0,
                true
            );
        } catch (\Throwable $error) {
            return [
                'ok' => false,
                'message' => __('SHIELD-X could not create a restore point before saving this file.', 'xerotact-shield-x-security'),
            ];
        }

        if (empty($result['ok']) || !empty($result['truncated'])) {
            return [
                'ok' => false,
                'message' => !empty($result['truncated'])
                    ? __('SHIELD-X blocked this save because the required restore point was incomplete.', 'xerotact-shield-x-security')
                    : (string) ($result['message'] ?? __('SHIELD-X could not create a restore point before saving this file.', 'xerotact-shield-x-security')),
            ];
        }

        return $result;
    }

    private function issuePath(string $threatId): string
    {
        if ($threatId === '') {
            return '';
        }

        $issue = ScanRepository::fromWordPress()->issueByThreatId($threatId);

        return is_array($issue) ? (string) ($issue['affected_path'] ?? '') : '';
    }

    /**
     * When an extension supplies an analyzed hash, make the administrator repeat
     * the decision only against the exact active file that was analyzed. Legacy
     * scanner forms do not send this optional field and retain their behavior.
     *
     * @return array{ok:bool,message:string,path:string}
     */
    private function verifyAnalyzedScannerFileHash(string $path, string $threatId = ''): array
    {
        $expected = strtolower($this->postedText('expected_file_hash', ''));
        if ($expected === '') {
            return ['ok' => true, 'message' => '', 'path' => $path];
        }
        if (preg_match('/^[a-f0-9]{64}$/', $expected) !== 1) {
            return ['ok' => false, 'message' => __('SHIELD-X blocked this action because the analyzed file hash was invalid.', 'xerotact-shield-x-security'), 'path' => $path];
        }
        if ($threatId === '') {
            return ['ok' => false, 'message' => __('SHIELD-X blocked this action because the AI review was not bound to an active scanner finding.', 'xerotact-shield-x-security'), 'path' => $path];
        }
        $issue = ScanRepository::fromWordPress()->issueByThreatId($threatId);
        if (!is_array($issue) || (string) ($issue['source'] ?? '') !== 'scanner' || (string) ($issue['status'] ?? '') !== 'active' || (string) ($issue['affected_path'] ?? '') !== $path) {
            return ['ok' => false, 'message' => __('SHIELD-X blocked this action because the scanner finding is no longer active.', 'xerotact-shield-x-security'), 'path' => $path];
        }
        $path = self::sanitizeRelativeFilePath($path);
        $root = realpath(WordPressPaths::root());
        $file = is_string($root) && $root !== '' && $path !== '' ? realpath(rtrim($root, '/\\') . '/' . $path) : false;
        $normalizedRoot = is_string($root) ? rtrim(str_replace('\\', '/', $root), '/') . '/' : '';
        $normalizedFile = is_string($file) ? str_replace('\\', '/', $file) : '';
        if (!is_string($file) || $normalizedRoot === '' || strpos($normalizedFile, $normalizedRoot) !== 0 || !is_file($file) || !is_readable($file)) {
            return ['ok' => false, 'message' => __('SHIELD-X blocked this action because the analyzed file is no longer available inside the WordPress root.', 'xerotact-shield-x-security'), 'path' => $path];
        }
        $current = hash_file('sha256', $file);
        if (!is_string($current) || !hash_equals($expected, $current)) {
            return ['ok' => false, 'message' => __('SHIELD-X blocked this action because the file changed after the AI review. Review the current file again first.', 'xerotact-shield-x-security'), 'path' => $path];
        }

        return ['ok' => true, 'message' => '', 'path' => $path];
    }

    /**
     * The compare editor and trust action must operate on the exact local hash
     * that was presented to the administrator. Unlike an AI review, this local
     * comparison does not require an active scanner finding to remain open.
     *
     * @return array{ok:bool,message:string,path:string}
     */
    private function verifyComparedPluginFileHash(string $path): array
    {
        $expected = strtolower($this->postedText('expected_file_hash', ''));
        $path = self::sanitizeRelativeFilePath($path);
        $verifier = PluginFileVerifier::fromWordPress();
        if ($path === '' || !$verifier->isPluginPath($path)) {
            return ['ok' => false, 'message' => __('SHIELD-X blocked this action because the selected file is not an eligible plugin comparison.', 'xerotact-shield-x-security'), 'path' => $path];
        }
        if (preg_match('/^[a-f0-9]{64}$/', $expected) !== 1) {
            return ['ok' => false, 'message' => __('SHIELD-X blocked this action because the compared file hash was invalid.', 'xerotact-shield-x-security'), 'path' => $path];
        }

        try {
            $comparison = $verifier->compareFile($path);
        } catch (\Throwable $error) {
            return ['ok' => false, 'message' => __('SHIELD-X could not revalidate the official plugin comparison. Refresh the comparison and try again.', 'xerotact-shield-x-security'), 'path' => $path];
        }
        if (empty($comparison['eligible']) || empty($comparison['clean_exists']) || empty($comparison['local_exists'])) {
            return ['ok' => false, 'message' => __('SHIELD-X blocked this action because the official plugin comparison is no longer available for this file.', 'xerotact-shield-x-security'), 'path' => $path];
        }

        $root = realpath(WordPressPaths::root());
        $file = is_string($root) && $root !== '' ? realpath(rtrim($root, '/\\') . '/' . $path) : false;
        $normalizedRoot = is_string($root) ? rtrim(str_replace('\\', '/', $root), '/') . '/' : '';
        $normalizedFile = is_string($file) ? str_replace('\\', '/', $file) : '';
        if (!is_string($file) || $normalizedRoot === '' || strpos($normalizedFile, $normalizedRoot) !== 0 || !is_file($file) || !is_readable($file)) {
            return ['ok' => false, 'message' => __('SHIELD-X blocked this action because the compared file is no longer available inside the WordPress root.', 'xerotact-shield-x-security'), 'path' => $path];
        }

        $current = hash_file('sha256', $file);
        $comparedHash = strtolower((string) ($comparison['local_hash'] ?? ''));
        if (!is_string($current) || !hash_equals($expected, $current) || !hash_equals($expected, $comparedHash)) {
            return ['ok' => false, 'message' => __('SHIELD-X blocked this action because the file changed since it was compared. Review the current file again first.', 'xerotact-shield-x-security'), 'path' => $path];
        }

        return ['ok' => true, 'message' => '', 'path' => $path];
    }

    private function uploadedImportJson(): string
    {
        // phpcs:disable WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Caller verifies shieldx_import_settings; upload metadata and the temporary path are validated below.
        if (empty($_FILES['settings_import_file']) || !is_array($_FILES['settings_import_file'])) {
            return '';
        }

        $file = $_FILES['settings_import_file'];
        // phpcs:enable WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
        $error = isset($file['error']) ? (int) $file['error'] : UPLOAD_ERR_NO_FILE;
        if ($error === UPLOAD_ERR_NO_FILE) {
            return '';
        }

        if ($error !== UPLOAD_ERR_OK) {
            return '';
        }

        $tmpName = isset($file['tmp_name']) && is_scalar($file['tmp_name']) ? (string) $file['tmp_name'] : '';
        $size = isset($file['size']) ? (int) $file['size'] : 0;
        if ($tmpName === '' || $size <= 0 || $size > 1048576 || !is_uploaded_file($tmpName)) {
            return '';
        }

        $contents = file_get_contents($tmpName);

        return is_string($contents) ? $contents : '';
    }
    // phpcs:enable WordPress.Security.NonceVerification.Recommended,WordPress.Security.NonceVerification.Missing

    private function formatTime(string $time): string
    {
        $timestamp = strtotime($time);

        if (!$timestamp) {
            return __('Unknown time', 'xerotact-shield-x-security');
        }

        return function_exists('wp_date') ? wp_date('Y-m-d H:i:s', $timestamp) : gmdate('Y-m-d H:i:s', $timestamp);
    }

    /**
     * @return array{datetime:string,date:string,time:string}
     */
    private function formatEventTimeParts(string $time): array
    {
        $timestamp = strtotime($time);

        if (!$timestamp) {
            return [
                'datetime' => '',
                'date' => __('UNKNOWN DATE', 'xerotact-shield-x-security'),
                'time' => __('Unknown time', 'xerotact-shield-x-security'),
            ];
        }

        $date = function_exists('wp_date') ? wp_date('d M Y', $timestamp) : gmdate('d M Y', $timestamp);
        $clock = function_exists('wp_date') ? wp_date('H:i:s', $timestamp) : gmdate('H:i:s', $timestamp);

        return [
            'datetime' => gmdate('c', $timestamp),
            'date' => strtoupper($date),
            'time' => $clock,
        ];
    }

    private function formatBytes(int $bytes): string
    {
        if ($bytes < 1024) {
            return (string) max(0, $bytes) . ' B';
        }

        $units = ['KB', 'MB', 'GB'];
        $value = (float) $bytes;
        $unit = 'KB';

        foreach ($units as $candidate) {
            $value = $value / 1024;
            $unit = $candidate;
            if ($value < 1024 || $candidate === 'GB') {
                break;
            }
        }

        return rtrim(rtrim(number_format($value, 1), '0'), '.') . ' ' . $unit;
    }

    private function formatInteger(int $value): string
    {
        if (function_exists('number_format_i18n')) {
            return number_format_i18n($value);
        }

        return number_format($value);
    }

    private function formatTimestamp(int $timestamp): string
    {
        if ($timestamp <= 0) {
            return __('Unknown time', 'xerotact-shield-x-security');
        }

        return function_exists('wp_date') ? wp_date('Y-m-d H:i:s', $timestamp) : gmdate('Y-m-d H:i:s', $timestamp);
    }
}
