<?php

/**
 * Main Plugin Class
 *
 * @package ProRank\SEO
 */

declare(strict_types=1);

namespace ProRank\SEO;

use ProRank\SEO\Core\ServiceContainer;
use ProRank\SEO\Core\Config\DomainConfig;
use ProRank\SEO\Core\SettingsManager;
use ProRank\SEO\Core\LicenseManager;
use ProRank\SEO\Core\NotificationManager;
use ProRank\SEO\Core\ModuleManager;
use ProRank\SEO\Core\ModuleLoader;
use ProRank\SEO\Core\AdminMenus;
use ProRank\SEO\Core\PresetManager;
use ProRank\SEO\Core\SettingsSnapshot;

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

// Import REST API Controllers
use ProRank\SEO\Core\RestApi\DashboardController;
use ProRank\SEO\Core\RestApi\ModulesController;
use ProRank\SEO\Core\RestApi\UserPreferencesController;
use ProRank\SEO\Core\RestApi\NotificationsFeedController;
use ProRank\SEO\Core\Notifications\NotificationsFeed;
use ProRank\SEO\Core\RestApi\SettingsController;
use ProRank\SEO\Core\RestApi\AdvancedSettingsController;
use ProRank\SEO\Core\RestApi\DataController;
use ProRank\SEO\Core\RestApi\FeedbackController;
use ProRank\SEO\Core\RestApi\MetaController;
use ProRank\SEO\Core\RestApi\OnboardingController;
use ProRank\SEO\Core\RestApi\SitemapsController;
use ProRank\SEO\Core\RestApi\HtmlSitemapController;
use ProRank\SEO\Core\RestApi\ReadabilityController;
use ProRank\SEO\Core\RestApi\HeadlineAnalyzerController;
use ProRank\SEO\Core\RestApi\LinkingController;
use ProRank\SEO\Core\RestApi\RedirectsController;
use ProRank\SEO\Core\RestApi\RedirectSettingsController;
use ProRank\SEO\Core\RestApi\RobotsIndexingController;
use ProRank\SEO\Core\RestApi\HeadCleanupController;
use ProRank\SEO\Core\RestApi\UrlCleanupController;
use ProRank\SEO\Core\RestApi\ImportExportController;
use ProRank\SEO\Core\RestApi\EnhancedAuditController;
use ProRank\SEO\Core\RestApi\CommandPaletteController;
use ProRank\SEO\Core\RestApi\PresetsController;
use ProRank\SEO\Core\RestApi\CacheController;
use ProRank\SEO\Core\RestApi\SettingsSnapshotController;
use ProRank\SEO\Core\RestApi\ImageOptimizationController;
use ProRank\SEO\Core\RestApi\ImageOptimizationSettingsController;
use ProRank\SEO\Core\RestApi\ImagesController;
use ProRank\SEO\Core\RestApi\FontOptimizationController;
use ProRank\SEO\Core\RestApi\DatabaseOptimizationController;
use ProRank\SEO\Core\RestApi\SystemController;
use ProRank\SEO\Core\RestApi\CssOptimizationController;
use ProRank\SEO\Core\RestApi\CwvDataController;
use ProRank\SEO\Core\RestApi\PerformanceSettingsController;
use ProRank\SEO\Core\RestApi\WizardStateController;
use ProRank\SEO\Core\RestApi\CdnController;
use ProRank\SEO\Core\RestApi\CdnSetupController;
use ProRank\SEO\Core\RestApi\VersionController;
use ProRank\SEO\Frontend\HeadOutput;
use ProRank\SEO\Frontend\SiteBasicsOutput;
use ProRank\SEO\Frontend\IndexNowVirtualFile;
use ProRank\SEO\Frontend\SpeculationRules;
use ProRank\SEO\Admin\BulkEditIntegration;
use ProRank\SEO\Services\Cdn\CdnManager;
use ProRank\SEO\Frontend\ThirdPartyDefer;
use ProRank\SEO\Frontend\LazyMedia;
use ProRank\SEO\Frontend\RumBeacon;

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


/**
 * Main Plugin Class
 */


class Plugin {
    
    /**
     * Plugin version
     *
     * @var string
     */
    const VERSION = '1.1.1';
    
    /**
     * Plugin instance
     *
     * @var Plugin|null
     */
    private static ?Plugin $instance = null;
    
    /**
     * Service container
     *
     * @var ServiceContainer
     */
    private ServiceContainer $services;
    
    /**
     * Get plugin instance
     *
     * @return Plugin
     */
    public static function get_instance(): Plugin {
        static $creating = false;
        
        if ( null === self::$instance ) {
            if ($creating) {
                // Prevent recursive instantiation
                prorank_log('[ProRank SEO] ERROR: Recursive Plugin instantiation detected!');
                throw new \RuntimeException('Recursive Plugin instantiation detected');
            }
            
            $creating = true;
            self::$instance = new self();
            $creating = false;
        }
        
        return self::$instance;
    }

    /**
     * Handle content changes to trigger cache/CDN purge
     */
    public function handle_content_change( $post_id, $post, $update ): void {
        if ( self::is_elementor_builder_save_request() ) {
            return;
        }

        if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
            return;
        }
        $this->purge_urls(
            array_merge(
                $this->collect_urls_for_post( $post_id ),
                $this->collect_global_urls()
            )
        );
    }

    /**
     * Handle post deletion
     */
    public function handle_content_delete( $post_id ): void {
        $post_id = (int) $post_id;
        if ( $post_id <= 0 ) {
            return;
        }
        $this->purge_urls(
            array_merge(
                $this->collect_urls_for_post( $post_id ),
                $this->collect_global_urls()
            )
        );
    }

    /**
     * Handle status transitions (e.g., draft -> publish)
     */
    public function handle_post_status_transition( $new_status, $old_status, $post ): void {
        if ( self::is_elementor_builder_save_request() ) {
            return;
        }

        if ( $new_status === $old_status ) {
            return;
        }
        $this->purge_urls(
            array_merge(
                $this->collect_urls_for_post( $post->ID ),
                $this->collect_global_urls()
            )
        );
    }

    /**
     * Handle menu updates
     */
    public function handle_menu_change( $nav_menu_id ): void {
        $this->purge_urls( $this->collect_global_urls() );
    }

    /**
     * Detect Elementor builder save AJAX requests.
     *
     * Elementor persists page changes through admin-ajax `save_builder`. ProRank
     * should not run heavy save_post maintenance hooks inside that request.
     *
     * @return bool
     */
    public static function is_elementor_builder_save_request(): bool {
        if ( ! wp_doing_ajax() ) {
            return false;
        }

        $ajax_action = isset( $_REQUEST['action'] ) ? sanitize_key( wp_unslash( (string) $_REQUEST['action'] ) ) : '';
        if ( $ajax_action !== 'elementor_ajax' ) {
            return false;
        }

        $actions = $_REQUEST['actions'] ?? '';
        if ( is_array( $actions ) ) {
            $actions = wp_json_encode( $actions );
        }

        if ( ! is_string( $actions ) || $actions === '' ) {
            return false;
        }

        return str_contains( wp_unslash( $actions ), 'save_builder' );
    }

    /**
     * Collect URLs related to a post for purging
     */
    private function collect_urls_for_post( int $post_id ): array {
        $urls = [];
        $permalink = get_permalink( $post_id );
        if ( $permalink ) {
            $urls[] = $permalink;
        }
        return array_filter( array_unique( $urls ) );
    }

    /**
     * Collect sitewide URLs that should be purged on structural changes
     */
    private function collect_global_urls(): array {
        $urls = [];
        if ( function_exists( 'home_url' ) ) {
            $urls[] = home_url( '/' );
            $urls[] = home_url( '/feed/' );
            $urls[] = home_url( '/sitemap.xml' );
            $urls[] = home_url( '/sitemap_index.xml' );
            $urls[] = home_url( '/robots.txt' );
        }
        $feed_link = function_exists( 'get_feed_link' ) ? get_feed_link() : '';
        if ( $feed_link ) {
            $urls[] = $feed_link;
        }
        return array_filter( array_unique( $urls ) );
    }

    /**
     * Purge URLs from CDN if configured
     */
    private function purge_urls( array $urls ): void {
        if ( empty( $urls ) ) {
            return;
        }
        $urls = array_unique( array_filter( $urls ) );
        $this->purge_litespeed_cache( $urls );
        try {
            $cdn = new CdnManager();
            $result = $cdn->purge_urls( $urls );
            if ( is_wp_error( $result ) ) {
                prorank_log( 'ProRank CDN purge failed: ' . $result->get_error_message() );
            } else {
                do_action( 'prorank_cdn_cache_cleared', get_option( 'prorank_cdn_provider', 'none' ), $urls );
                do_action( 'prorank_audit_log_event', 'cdn_purge', [ 'urls' => $urls, 'provider' => get_option( 'prorank_cdn_provider', 'none' ) ] );
            }
        } catch ( \Throwable $e ) {
            prorank_log( 'ProRank CDN purge exception: ' . $e->getMessage() );
        }
    }

    /**
     * Best-effort LiteSpeed Cache purge when LSCache is active.
     *
     * @param array $urls URLs to purge.
     */
    private function purge_litespeed_cache( array $urls ): void {
        if ( empty( $urls ) ) {
            return;
        }
        if ( ! defined( 'LSCWP_V' ) && ! class_exists( 'LiteSpeed_Cache_API' ) ) {
            return;
        }

        if ( class_exists( 'LiteSpeed_Cache_API' ) ) {
            if ( method_exists( 'LiteSpeed_Cache_API', 'purge_url' ) ) {
                foreach ( $urls as $url ) {
                    \LiteSpeed_Cache_API::purge_url( $url );
                }
                return;
            }
            if ( method_exists( 'LiteSpeed_Cache_API', 'purge' ) ) {
                foreach ( $urls as $url ) {
                    \LiteSpeed_Cache_API::purge( $url );
                }
                return;
            }
            if ( method_exists( 'LiteSpeed_Cache_API', 'purge_all' ) ) {
                \LiteSpeed_Cache_API::purge_all();
                return;
            }
        }

        if ( has_action( 'litespeed_purge_all' ) ) {
            do_action( 'litespeed_purge_all' );
            return;
        }
        if ( has_action( 'litespeed_purge_url' ) ) {
            foreach ( $urls as $url ) {
                do_action( 'litespeed_purge_url', $url );
            }
            return;
        }
        if ( has_action( 'litespeed_purge' ) ) {
            foreach ( $urls as $url ) {
                do_action( 'litespeed_purge', $url );
            }
        }
    }
    
    /**
     * Constructor
     */
    private function __construct() {
        // Initialize domain configuration first
        DomainConfig::init();
        
        $this->services = new ServiceContainer();
        
        $this->init_services();
        $this->init_hooks();
        
        // Security validation will be done after plugins_loaded to prevent circular dependency
    }
    
    /**
     * Initialize services
     */
    private function init_services(): void {
        // Initialize core services in dependency order
        $this->services->register('settings', fn() => new SettingsManager());
        $this->services->register('notifications', fn() => new NotificationManager());
        $this->services->register('license', fn() => new LicenseManager());
        $this->services->register('modules', fn() => new ModuleManager());
        $this->services->register('menus', fn() => new \ProRank\SEO\Core\MenuRegistry());
        $this->services->register('presets', function() {
            return new PresetManager($this->settings());
        });
        $this->services->register('snapshots', function() {
            return new SettingsSnapshot($this->settings());
        });
        
        // Module loading will be done after plugin is fully initialized
    }
    
    /**
     * Initialize Action Scheduler
     */
    private function init_action_scheduler(): void {
        // Load Action Scheduler if not already loaded
        $action_scheduler_path = PRORANK_PLUGIN_DIR . 'vendor/woocommerce/action-scheduler/action-scheduler.php';
        if ( file_exists( $action_scheduler_path ) && ! class_exists( 'ActionScheduler' ) ) {
            require_once $action_scheduler_path;
        }
    }
    
    /**
     * Initialize hooks
     */
    private function init_hooks(): void {
        // Initialize Action Scheduler
        $this->init_action_scheduler();

        // Apply performance conflict safe mode before module bootstrap.
        add_action( 'plugins_loaded', [ $this, 'maybe_apply_performance_conflict_safe_mode' ], 4 );
        add_action( 'plugins_loaded', [ $this, 'apply_performance_engine_safeguards' ], 4 );
        
        // Load modules very early
        add_action( 'plugins_loaded', [ $this, 'load_modules' ], 5 );
        // Ensure background image optimization actions always have a callback
        add_action( 'plugins_loaded', [ $this, 'register_image_optimization_actions' ], 20 );
        // Ensure local Site Audit cron actions always have callbacks.
        add_action( 'plugins_loaded', [ $this, 'register_site_audit_actions' ], 20 );
        
        // Core initialization
        add_action( 'init', [ $this, 'init' ] );
        add_action( 'admin_init', [ $this, 'admin_init' ] );
        add_action( 'admin_post_prorank_download_backup', [ $this, 'handle_backup_download' ] );
        
        // Load text domain
        
        // Admin menu - using MenuHandler
        $menu_handler = new \ProRank\SEO\Admin\MenuHandler($this);
        add_action( 'admin_menu', [ $menu_handler, 'init' ] );
        
        // Initialize Meta Box
        $meta_box = new \ProRank\SEO\Admin\MetaBox($this);
        $meta_box->init();
        
        // Initialize Editor Sidebar
        $editor_sidebar = new \ProRank\SEO\Admin\EditorSidebar($this);
        $editor_sidebar->init();
        
        // Initialize Admin Bar Menu
        $admin_bar_menu = new \ProRank\SEO\Admin\AdminBarMenu();
        $admin_bar_menu->init();
        
        // Initialize Menu Styles
        $menu_styles = new \ProRank\SEO\Admin\MenuStyles();
        $menu_styles->init();

        // Admin scripts (enqueue main React app + import map)
        new \ProRank\SEO\Admin\AdminScripts($this);
        
        // Admin assets
        add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_assets' ] );
        
        // Frontend assets (if needed)
        add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_frontend_assets' ] );

        // REST API initialization
        add_action( 'rest_api_init', [ $this, 'init_rest_api' ] );
        add_filter( 'auto_update_plugin', [ $this, 'filter_plugin_auto_update' ], 10, 2 );

        // Initialize Frontend Head Output
        // Safety guard: when performance group is OFF, ensure no stale runtime
        // mutations remain active from prior settings/cache artifacts.
        add_action( 'init', [ $this, 'reconcile_performance_active_modules_from_settings' ], 0 );
        add_action( 'init', [ $this, 'enforce_performance_runtime_safety' ], 1 );
        add_action( 'init', [ $this, 'init_frontend_head_output' ] );
        add_action( 'init', [ $this, 'init_indexnow_virtual_file' ] );
        add_action( 'init', [ $this, 'init_resource_hints_fallback' ] );
        add_action( 'init', [ $this, 'init_speculation_rules' ] );
        add_action( 'init', [ $this, 'init_third_party_defer' ] );
        add_action( 'init', [ $this, 'init_lazy_media' ] );
        add_action( 'init', [ $this, 'init_css_async_fallback' ] );
        add_action( 'init', [ $this, 'init_js_defer_fallback' ] );
        add_action( 'init', [ $this, 'init_rum_beacon' ] );
        add_action( 'prorank_seo_module_toggled', [ $this, 'handle_module_toggle_sync' ], 10, 2 );
        add_action( 'prorank_seo_module_group_toggled', [ $this, 'handle_module_group_toggle_sync' ], 10, 2 );
        add_action( 'prorank_performance_cache_clear', [ $this, 'clear_performance_runtime_cache' ] );
        
        // Cache/CDN purges on content changes
        add_action( 'save_post', [ $this, 'handle_content_change' ], 10, 3 );
        add_action( 'deleted_post', [ $this, 'handle_content_delete' ], 10, 1 );
        add_action( 'transition_post_status', [ $this, 'handle_post_status_transition' ], 10, 3 );
        add_action( 'wp_update_nav_menu', [ $this, 'handle_menu_change' ], 10, 1 );

    }
    
    /**
     * Load modules early
     */
    public function load_modules(): void {
        static $modules_loaded = false;
        if (!$modules_loaded) {
            // Security validation checkpoint - module loading
            if (class_exists('\ProRank\SEO\Core\SecurityValidator')) {
                \ProRank\SEO\Core\SecurityValidator::validate('cp_module');
            }
            
            $module_loader = new ModuleLoader($this->modules());
            $module_loader->load();
            $modules_loaded = true;
            
            // Hook for Premium plugin to register external modules
            do_action( 'prorank_seo_modules_loaded', $this->modules() );
            
            // Initialize modules immediately
            $this->modules()->init_active_modules();
        }
    }

    /**
     * Register image optimization background actions even when modules are not initialized.
     *
     * @return void
     */
    public function register_image_optimization_actions(): void {
        if ( ! has_action( 'prorank_bulk_optimization_continue' ) ) {
            add_action( 'prorank_bulk_optimization_continue', [ $this, 'handle_bulk_optimization_continue' ], 10, 2 );
        }

        if ( ! has_action( 'prorank_bulk_optimization_batch' ) ) {
            add_action( 'prorank_bulk_optimization_batch', [ $this, 'handle_bulk_optimization_batch' ], 10, 2 );
        }

        if ( ! has_action( 'prorank_optimize_image' ) ) {
            add_action( 'prorank_optimize_image', [ $this, 'handle_optimize_image_action' ], 10, 1 );
        }
    }

    /**
     * Register Site Audit cron action callbacks, even when module bootstrap is delayed.
     *
     * @return void
     */
    public function register_site_audit_actions(): void {
        if ( ! has_action( 'prorank_run_site_crawler' ) ) {
            add_action( 'prorank_run_site_crawler', [ $this, 'handle_site_audit_run_crawler' ], 10, 1 );
        }

        if ( ! has_action( 'prorank_run_immediate_crawler' ) ) {
            add_action( 'prorank_run_immediate_crawler', [ $this, 'handle_site_audit_run_crawler' ], 10, 1 );
        }

        if ( ! has_action( 'prorank_schedule_url_checks' ) ) {
            add_action( 'prorank_schedule_url_checks', [ $this, 'handle_site_audit_schedule_checks' ], 10, 1 );
        }

        if ( ! has_action( 'prorank_check_audit_url' ) ) {
            add_action( 'prorank_check_audit_url', [ $this, 'handle_site_audit_check_url' ], 10, 3 );
        }
    }

    /**
     * Handle scheduled site crawl execution.
     *
     * @param string $audit_id Audit identifier.
     * @return void
     */
    public function handle_site_audit_run_crawler( $audit_id = '' ): void {
        if ( ! class_exists( \ProRank\SEO\Core\Audits\SiteAuditEngine::class ) ) {
            return;
        }

        $audit_id = (string) $audit_id;
        if ( $audit_id === '' ) {
            return;
        }

        $engine = new \ProRank\SEO\Core\Audits\SiteAuditEngine();
        $engine->run_crawler( $audit_id );
    }

    /**
     * Handle scheduled URL batch scheduling for Site Audit.
     *
     * @param string $audit_id Audit identifier.
     * @return void
     */
    public function handle_site_audit_schedule_checks( $audit_id = '' ): void {
        if ( ! class_exists( \ProRank\SEO\Core\Audits\SiteAuditEngine::class ) ) {
            return;
        }

        $audit_id = (string) $audit_id;
        if ( $audit_id === '' ) {
            return;
        }

        $engine = new \ProRank\SEO\Core\Audits\SiteAuditEngine();
        $engine->schedule_url_checks( $audit_id );
    }

    /**
     * Handle scheduled single URL check for Site Audit.
     *
     * @param string $audit_id Audit identifier.
     * @param int    $url_id   URL row ID.
     * @param string $url      URL to check.
     * @return void
     */
    public function handle_site_audit_check_url( $audit_id = '', $url_id = 0, $url = '' ): void {
        if ( ! class_exists( \ProRank\SEO\Core\Audits\SiteAuditEngine::class ) ) {
            return;
        }

        $audit_id = (string) $audit_id;
        $url_id   = (int) $url_id;
        $url      = (string) $url;

        if ( $audit_id === '' || $url_id <= 0 || $url === '' ) {
            return;
        }

        $engine = new \ProRank\SEO\Core\Audits\SiteAuditEngine();
        $engine->check_url( $audit_id, $url_id, $url );
    }

    /**
     * Handle bulk optimization continuation when Action Scheduler fires.
     *
     * @param string $job_id Job ID.
     * @param int    $next_offset Next offset.
     * @return void
     */
    public function handle_bulk_optimization_continue( $job_id = '', $next_offset = 0 ): void {
        if ( ! class_exists( ImageOptimizationController::class ) ) {
            return;
        }

        $controller = new ImageOptimizationController();
        $controller->handle_bulk_optimization_continue( (string) $job_id, (int) $next_offset );
    }

    /**
     * Handle bulk optimization batch when WP-Cron fallback fires.
     *
     * @param string $job_id Job ID.
     * @param int    $offset Offset for the next batch.
     * @return void
     */
    public function handle_bulk_optimization_batch( $job_id = '', $offset = 0 ): void {
        if ( ! class_exists( ImageOptimizationController::class ) ) {
            return;
        }

        $controller = new ImageOptimizationController();
        $controller->handle_bulk_optimization_continue( (string) $job_id, (int) $offset );
    }

    /**
     * Handle single image optimization when Action Scheduler fires.
     *
     * @param mixed $attachment_id Attachment ID or args array.
     * @return void
     */
    public function handle_optimize_image_action( $attachment_id = 0 ): void {
        if ( is_array( $attachment_id ) ) {
            $attachment_id = $attachment_id['attachment_id'] ?? 0;
        }

        $attachment_id = (int) $attachment_id;
        if ( $attachment_id <= 0 ) {
            return;
        }

        $module = $this->modules()->get_module( 'image_optimization' );
        if ( is_object( $module ) && method_exists( $module, 'process_single_image_optimization' ) ) {
            $module->process_single_image_optimization( $attachment_id );
            return;
        }

        if ( class_exists( \ProRank\SEO\Modules\Performance\ImageOptimizationModule::class ) ) {
            $fallback = new \ProRank\SEO\Modules\Performance\ImageOptimizationModule();
            $fallback->process_single_image_optimization( $attachment_id );
        }
    }
    
    /**
     * General initialization
     */
    public function init(): void {
        // Security validation checkpoint - initialization
        // Do this first now that the plugin is fully initialized
        if (class_exists('\ProRank\SEO\Core\SecurityValidator')) {
            \ProRank\SEO\Core\SecurityValidator::validate('cp_init');
        }
        
        // Ensure modules are loaded (in case plugins_loaded didn't fire)
        $this->load_modules();

        // Hook for Premium plugin to initialize
        do_action( 'prorank_seo_init', $this );

        // Load Elementor integration if Elementor is active
        $this->load_elementor_integration();

        // Load Divi Builder integration if Divi is active
        $this->load_divi_integration();

        // Load Beaver Builder integration if BB is active
        $this->load_beaver_builder_integration();

        // Check for competitor plugins
        $this->check_competitors();

        // Audit system now handled by external prorank.io service
        // No local tables needed

    }

    /**
     * Respect the saved auto-update setting during WordPress update checks.
     */
    public function filter_plugin_auto_update( $update, $item ): bool {
        $plugin_basename = defined( 'PRORANK_PLUGIN_BASENAME' ) ? PRORANK_PLUGIN_BASENAME : plugin_basename( PRORANK_PLUGIN_FILE );
        $plugin_slug     = dirname( $plugin_basename );
        $item_plugin     = is_object( $item ) ? (string) ( $item->plugin ?? '' ) : '';
        $item_slug       = is_object( $item ) ? (string) ( $item->slug ?? '' ) : '';

        if ( $item_plugin === $plugin_basename || $item_slug === $plugin_slug || $item_slug === 'prorank-seo' ) {
            return (bool) get_option( 'prorank_auto_update', false );
        }

        return (bool) $update;
    }
    
    /**
     * Admin initialization
     */
    public function admin_init(): void {
        // Security validation checkpoint - admin access
        if (class_exists('\ProRank\SEO\Core\SecurityValidator')) {
            \ProRank\SEO\Core\SecurityValidator::validate('cp_admin', ['user_id' => get_current_user_id()]);
        }
        
        
        // Register settings
        // $this->settings()->register_settings();
        
        // Initialize media enhancements
        new Admin\MediaEnhancements($this);

        // Initialize media library optimization features
        new Admin\MediaLibraryOptimization($this);

        // Initialize bulk edit integration
        new BulkEditIntegration();

        // Initialize user profile E-E-A-T fields
        $user_profile_fields = new Admin\UserProfileFields();
        $user_profile_fields->init();

        // Initialize dashboard widget
        $dashboard_widget = new Admin\DashboardWidget();
        $dashboard_widget->init();

        // Check for plugin updates
        $this->check_updates();
        
        // Initialize onboarding
        $this->init_onboarding();
        
        // Initialize audit settings page
        if (class_exists('\ProRank\SEO\Admin\Pages\AuditSettings')) {
            $audit_settings = new \ProRank\SEO\Admin\Pages\AuditSettings();
            $audit_settings->init();
        }
        
    }
    
    /**
     * Get menu icon
     *
     * Returns the ProRank favicon as a data URI for WordPress admin menu.
     *
     * @return string
     */
    private function get_menu_icon(): string {
        // ProRank favicon - orange P on dark background
        $svg = '<svg version="1.1" viewBox="0 0 1909 2048" width="2106" height="2259" xmlns="http://www.w3.org/2000/svg"><path transform="translate(959,1)" d="m0 0h10l6 3 5 8 12 32 8 19 9 24 7 20 16 40 10 26 3 10 13 34 4 9 12 31 7 20 10 25 9 23 7 18 6 18 8 21 7 16 11 29 3 10 14 36 5 12 11 28 4 13 12 36 3 16 1 13v13l-2 16-2 4-2 1-14 1-424 11h-14l-6 4-6 7-2 6-1 30-1 149v195l-3 8-7 6-35 12-28 11-27 10-37 13-16 6-12 5-30 11-11-2-11-9-16-13-11-8-9-8-11-9-5-4-5-5-14-12-10-8-12-9-13-11-11-9-5-4-5-5-13-11-14-11-9-7-13-11-14-11-8-8-14-11-9-8-6-5-5-3-13-11-11-9-10-9-13-11-11-9-8-6-10-8-13-11-11-9-12-11-10-8-11-9-11-8-15-13-10-8-12-11-22-18-11-8-15-13-10-8-12-11-11-9-4-5-1-2v-12l4-6 8-4 27-1 36-1 11-1 24-1 47-1 42-2 47-1 35-2 47-1 26-1 9-1 73-2 42-2h19l44-2 19-1 47-1 25-1 10-1 35-1h12l12-1 10-5 4-7 7-20 9-24 5-11 10-27 6-18 11-29 4-8 13-35 7-20 11-28 10-26 4-10 4-13 12-31 8-20 8-21 6-18 10-26 4-9 11-29 5-15 13-34 5-12 11-29 7-20 9-24 4-9 12-31 4-13 4-8z" fill="#0B121F"/><path transform="translate(728,646)" d="m0 0h324l24 2 24 4 20 5 20 7 16 7 19 10 15 10 14 11 13 12 11 12 10 13 10 15 11 21 7 18 7 25 4 25 1 11v62l-3 24-4 19-9 27-10 21-11 18-10 13-11 13-14 14-11 9-14 10-15 9-17 9-23 9-26 7-23 4-22 2h-185v275h-142z" fill="#FE6900"/><path transform="translate(1156,637)" d="m0 0h26l567 19 143 5 9 3 5 5 3 9-3 8-8 8-11 9-8 6-11 9-13 11-9 7-5 5-11 10-11 9-8 6-11 9-13 11-10 8-5 5-10 9-30 24-13 11-11 9-7 7-11 9-13 11-13 10-26 22-11 10-11 9-12 10-8 6-13 11-11 9-4 2-2 4-13 11-11 9-8 6-11 9-13 11-11 9-12 11-10 8-13 10-14 11-14 12-10 9-16 13-14 11-8 6-11 9-16 13-10 9-14 11-11 9-5 3-16 13-14 11-10 9-14 11-24 18-25 20-13 11-16 12-11 8-13 10-17 13-5 5-16 12-14 11-7 4-15 12-15 11-5 5-19 14-11 8-10 6-7 1-4-2h-12l-12 1-25 1-50 1-16 1h-17l-1-4v-31l1-24 1-59v-142l-3-1v-3h3l1 3 22 1 9 1 29 1h15l60 3h29l28-3 23-4 29-9 25-12 19-10 12-7 15-11 12-10 16-16 9-11 11-13 10-18 6-13 4-13 8-28 3-13 4-30 1-25v-39l-3-29-7-28-7-19-4-10-8-15-8-12-7-8-8-10-11-12-7-7v-2l-4-2-13-11-15-10-24-13-9-5-5-6-1-3v-11l3-6z" fill="#0B121F"/><path transform="translate(1369,1159)" d="m0 0 9 1 6 4 4 6 4 15 10 40 5 22 6 24 5 17 6 25 3 14 8 33 3 9 8 32 4 20 7 27 5 18 6 25 4 18 7 27 9 35 4 18 8 33 4 13 7 29 6 26 5 20 5 18 6 24 6 27 7 25 9 36v10l-3 6-6 5-9 2-8-3-16-8-19-10-18-11-29-16-12-6-21-12-21-13-24-14-12-6-22-13-15-10-27-16-16-9-21-13-13-9-24-15-14-8-23-15-16-11-17-11-11-7-9-5-20-13-10-7-23-15-14-9-9-5-17-11-12-8-10-7-25-16-14-8-18-12-5-6-3-9v-8l4-9 4-5 13-10 8-7 17-13 9-7 5-3 26-20 13-11 17-13 11-8 16-12 13-10 7-5 5-5 14-11 13-10 13-9 14-11 13-10 9-8 12-9 13-10 9-6 14-11 13-10 9-8 28-21 11-8 18-14 5-4h2l2-4 12-9 18-14 6-3z" fill="#0B121F"/><path transform="translate(714,1135)" d="m0 0h3l1 41v73l1 138-1 24h142l55 1h71l21 1-5 5-11 8-11 9-14 10-8 6-7 4-13 10-15 11h-2l-1 3-19 14-14 10-17 12-18 13-5 5-18 13-11 8-9 6-19 14-9 7-13 10-14 10-9 6-17 12-16 12-10 8-17 12-15 10-28 20h-2l-1 3-19 13-14 10-7 4-17 12-14 10-15 11-18 12-7 4-24 16-15 11-20 13-15 9-17 11-12 8-10 7-18 11-8 5-8 4-11 7-5-1-4-7-1-5v-13l7-30 4-13 7-29 3-15 7-28 4-14 6-25 3-15 7-28 4-15 6-24 3-15 7-28 4-15 6-24 3-15 7-28 4-15 6-24 5-23 5-20 4-15 6-24 3-15 7-28 4-15 6-24 3-15 7-28 5-18 2-5 25-10 48-17 10-3 37-13 15-6 37-13z" fill="#0B121F"/><path transform="translate(915,789)" d="m0 0h44l53 1 22 2 12 3 19 9 10 8 12 12 8 14 5 14 2 14v28l-3 19-7 16-7 12-4 2-2 4-10 9-13 8-11 5-12 3-8 1-31 1-36 1h-25l-29-1-12-1-1-23v-45l1-115z" fill="#0B121F"/><path transform="translate(870,776)" d="m0 0h170l22 4 15 5 15 8 13 10 11 11 10 16 6 15 4 20v41l-4 19-6 15-7 12-9 10-5 5-8 7-15 9-19 7-20 4-19 1h-154zm45 13-23 1-1 115v45l1 23 12 1 29 1h25l47-1 20-1 16-3 13-5 12-7 11-9 4-4v-2l4-2 8-14 6-14 3-19v-28l-2-14-5-14-8-14-15-15-11-7-18-8-16-3-24-1-44-1z" fill="#fff"/></svg>';
        return 'data:image/svg+xml;base64,' . base64_encode($svg);
    }
    
    /**
     * Render admin page
     */
    public function render_admin_page(): void {
        echo '<div class="wrap"><div id="prorank-admin-root" class="prorank-admin-container"></div></div>';
    }
    
    /**
     * Render settings page
     */
    public function render_settings_page(): void {
        echo '<div class="wrap"><div id="prorank-admin-root" class="prorank-admin-container"></div></div>';
    }
    
    /**
     * Enqueue admin assets
     *
     * @param string $hook_suffix Current admin page hook suffix
     */
    public function enqueue_admin_assets( string $hook_suffix ): void {
        // Only load main admin assets on our admin pages
        if ( ! $this->is_prorank_admin_page( $hook_suffix ) ) {
            return;
        }

        if ( $this->current_prorank_page_uses_media_library() ) {
            wp_enqueue_media();
        }
        
        // Get asset file for admin bundle (fallback if asset file doesn't exist - Vite builds)
        $asset_file_path = PRORANK_PLUGIN_DIR . 'build/admin.asset.php';
        if (file_exists($asset_file_path)) {
            $asset_file = include $asset_file_path;
            $dependencies = $asset_file['dependencies'] ?? [];
            $version = $asset_file['version'] ?? self::VERSION;
        } else {
            // Fallback for Vite builds - no asset.php file
            $dependencies = ['wp-element', 'wp-components', 'wp-api-fetch', 'wp-i18n'];
            $version = self::VERSION;
        }

        // Admin script loading is handled by AdminScripts.php which properly
        // sets up ES modules with import maps. This prevents conflicts.
        // PropTypes and Recharts are bundled by Vite, no separate vendor files needed.

        // Only add localized data if AdminScripts has enqueued the main script
        if (wp_script_is('prorank-admin-js', 'enqueued')) {
            wp_localize_script(
                'prorank-admin-js',
                'prorankSeoAdmin',
                $this->get_admin_localized_data()
            );
        }
        
        // Enqueue admin styles only for legacy builds. The Vite admin bundle
        // imports its own shared CSS, so loading build/main.css here would
        // duplicate the same stylesheet on every routed admin page.
        $main_css = PRORANK_PLUGIN_DIR . 'build/main.css';
        $admin_css = PRORANK_PLUGIN_DIR . 'build/admin.css';
        $enqueued_main = false;

        if (!file_exists($main_css) && file_exists($admin_css)) {
            wp_enqueue_style(
                'prorank-seo-admin-main',
                PRORANK_PLUGIN_URL . 'build/admin.css',
                ['wp-components'],
                filemtime($admin_css)
            );
            $enqueued_main = true;
        } elseif (!file_exists($main_css)) {
            // Fallback for older builds.
            $legacy_css = PRORANK_PLUGIN_DIR . 'admin/assets/js/App.css';
            if (file_exists($legacy_css)) {
                wp_enqueue_style(
                    'prorank-seo-admin-main',
                    PRORANK_PLUGIN_URL . 'admin/assets/js/App.css',
                    ['wp-components'],
                    filemtime($legacy_css)
                );
                $enqueued_main = true;
            }
        }

        $site_audit_style_dependency = $enqueued_main ? 'prorank-seo-admin-main' : 'wp-components';

        $this->maybe_enqueue_site_audit_route_css($site_audit_style_dependency);
        
        // Enqueue admin menu fix styles
        $menu_fix_css = PRORANK_PLUGIN_DIR . 'admin/assets/css/admin-menu-fix.css';
        if (file_exists($menu_fix_css)) {
            wp_enqueue_style(
                'prorank-admin-menu-fix',
                PRORANK_PLUGIN_URL . 'admin/assets/css/admin-menu-fix.css',
                ['prorank-seo-admin'],
                PRORANK_VERSION
            );
        }
    }

    /**
     * Enqueue the lazy Site Audit route CSS chunk with explicit cache-busting.
     *
     * Vite emits the Site Audit view styles into a lazy CSS chunk (for example
     * `index3.css`). Enqueuing it through WordPress gives the file a real
     * `?ver=` value, which prevents stale admin CSS from surviving deploys.
     *
     * @param string $dependency_handle Base admin stylesheet handle.
     * @return void
     */
    private function maybe_enqueue_site_audit_route_css(string $dependency_handle): void {
        $current_page = isset($_GET['page']) ? sanitize_key(wp_unslash($_GET['page'])) : '';
        $current_tab  = isset($_GET['tab']) ? sanitize_key(wp_unslash($_GET['tab'])) : '';

        if ('prorank-site-audit' !== $current_page && 'site-audit' !== $current_tab) {
            return;
        }

        $this->enqueue_vite_manifest_css_entry(
            'src/admin/pages/SiteAudit/index.jsx',
            'prorank-seo-site-audit',
            [$dependency_handle]
        );
    }

    /**
     * Enqueue CSS files listed for a Vite manifest entry.
     *
     * @param string $entry_source   Manifest entry source path.
     * @param string $handle_prefix  Prefix for generated style handles.
     * @param array  $dependencies   Base dependencies for the first chunk.
     * @return void
     */
    private function enqueue_vite_manifest_css_entry(string $entry_source, string $handle_prefix, array $dependencies = []): void {
        $manifest = $this->get_vite_manifest();

        if (!isset($manifest[$entry_source]['css']) || !is_array($manifest[$entry_source]['css'])) {
            return;
        }

        $chunk_dependencies = $dependencies;

        foreach ($manifest[$entry_source]['css'] as $css_file) {
            $css_file = ltrim((string) $css_file, '/');
            $css_path = PRORANK_PLUGIN_DIR . 'build/' . $css_file;

            if (!file_exists($css_path)) {
                continue;
            }

            $handle = sanitize_key($handle_prefix . '-' . pathinfo($css_file, PATHINFO_FILENAME));

            wp_enqueue_style(
                $handle,
                PRORANK_PLUGIN_URL . 'build/' . $css_file,
                $chunk_dependencies,
                filemtime($css_path)
            );

            $chunk_dependencies[] = $handle;
        }
    }

    /**
     * Get the Vite manifest as an array.
     *
     * @return array<string, mixed>
     */
    private function get_vite_manifest(): array {
        static $manifest = null;

        if (null !== $manifest) {
            return $manifest;
        }

        $manifest_path = PRORANK_PLUGIN_DIR . 'build/manifest.json';

        if (!file_exists($manifest_path)) {
            $manifest = [];
            return $manifest;
        }

        $raw = file_get_contents($manifest_path);
        $decoded = is_string($raw) ? json_decode($raw, true) : null;
        $manifest = is_array($decoded) ? $decoded : [];

        return $manifest;
    }

    /**
     * Determine whether the current ProRank admin page needs WordPress media assets.
     *
     * @return bool
     */
    private function current_prorank_page_uses_media_library(): bool {
        $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
        $tab  = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : '';

        if ( 'prorank-on-page-seo' === $page ) {
            return true;
        }

        return 'prorank-seo' === $page && 'quick-setup' === $tab;
    }
    
    /**
     * Get build information
     *
     * @return array
     */
    private function get_build_info(): array {
        $build_file = PRORANK_PLUGIN_DIR . 'build-info.json';
        $default_info = [
            'fingerprint' => '',
            'timestamp' => 0,
            'commit' => '',
        ];
        
        if (file_exists($build_file)) {
            $build_data = json_decode(file_get_contents($build_file), true);
            if ($build_data) {
                return array_merge($default_info, $build_data);
            }
        }
        
        return $default_info;
    }
    
    /**
     * Get admin localized data
     *
     * @return array
     */
    /**
     * Check if premium plugin is active
     *
     * @return bool
     */
    public function is_premium_active(): bool {
        if (defined('PRORANK_PREMIUM_VERSION')) {
            return true;
        }

        if (!function_exists('is_plugin_active')) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        if (!function_exists('is_plugin_active')) {
            return false;
        }

        return is_plugin_active('prorank-seo-premium/prorank-seo.php')
            || is_plugin_active('prorank-seo-premium/prorank-seo-premium.php');
    }

    /**
     * Get modules with availability status
     *
     * Returns all free-tier modules with their availability status.
     *
     * @return array
     */
    /**
     * Load the static module catalog shipped with the plugin.
     *
     * Free build uses this catalog for free-module metadata only.
     *
     * @return array<string, array<string, mixed>>
     */
    private function load_module_catalog(): array {
        $catalog_file = PRORANK_PLUGIN_DIR . 'config/module-catalog.json';
        if (!file_exists($catalog_file)) {
            return [];
        }

        $raw = file_get_contents($catalog_file);
        if ($raw === false) {
            return [];
        }

        $decoded = json_decode($raw, true);
        if (!is_array($decoded) || !isset($decoded['modules']) || !is_array($decoded['modules'])) {
            return [];
        }

        return $decoded['modules'];
    }

    /**
     * Get modules with availability status.
     *
     * Returns only free-tier modules for the free plugin UI.
     *
     * @return array<string, array<string, mixed>>
     */
    private function get_filtered_modules(): array {
        $loaded_modules = $this->modules()->get_modules();
        $catalog_modules = $this->load_module_catalog();

        $result = [];

        $add_module = function (string $slug, array $meta) use (&$result, $loaded_modules): void {
            $tier = (string) ($meta['tier'] ?? 'free');
            if ($tier === 'core') {
                $tier = 'pro';
            }
            if ($tier !== 'free') {
                return;
            }

            $name = (string) ($meta['name'] ?? $slug);
            $description = (string) ($meta['description'] ?? '');
            $parent_slug = $meta['parent_slug'] ?? null;

            $enabled = (bool) ($meta['enabled'] ?? false);

            if (isset($loaded_modules[$slug]) && is_object($loaded_modules[$slug])) {
                $module = $loaded_modules[$slug];

                if (method_exists($module, 'get_feature_tier')) {
                    $tier = (string) $module->get_feature_tier();
                    if ($tier === 'core') {
                        $tier = 'pro';
                    }
                    if ($tier !== 'free') {
                        return;
                    }
                }

                if (method_exists($module, 'get_name')) {
                    $name = (string) $module->get_name();
                }

                if (method_exists($module, 'get_description')) {
                    $description = (string) $module->get_description();
                }

                if (method_exists($module, 'get_parent_slug')) {
                    $parent_slug = $module->get_parent_slug();
                }

                if (method_exists($module, 'is_enabled')) {
                    $enabled = (bool) $module->is_enabled();
                } else {
                    $enabled = true;
                }
            }

            $result[$slug] = [
                'id' => $slug,
                'name' => $name,
                'description' => $description,
                'tier' => $tier,
                'parent_slug' => $parent_slug,
                'enabled' => $enabled,
                'available' => true,
                'loaded' => isset($loaded_modules[$slug]),
            ];
        };

        // Prefer static catalog metadata for free modules.
        if (!empty($catalog_modules)) {
            foreach ($catalog_modules as $slug => $meta) {
                if (!is_array($meta)) {
                    $meta = [];
                }
                $add_module((string) $slug, $meta);
            }
        }

        // Ensure any runtime-registered modules not present in the catalog still show.
        foreach ($loaded_modules as $slug => $module) {
            if (!isset($result[$slug])) {
                $add_module((string) $slug, []);
            }
        }

        return $result;
    }



    private function get_admin_localized_data(): array {
        $current_user = wp_get_current_user();
        $server_software = '';
        if ( isset( $_SERVER['SERVER_SOFTWARE'] ) ) {
            $server_software = sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) );
        }
        $nginx_server = $server_software !== '' && stripos( $server_software, 'nginx' ) !== false;
        $litespeed_server = $server_software !== '' && stripos( $server_software, 'litespeed' ) !== false;
        $litespeed_cache = defined( 'LSCWP_V' ) || class_exists( 'LiteSpeed_Cache_API' );
        
        // Get build information
        $build_info = $this->get_build_info();
        
        return [
            'apiUrl' => home_url( '/wp-json/prorank-seo/v1' ),
            'nonce' => wp_create_nonce( 'wp_rest' ),
            'adminUrl' => admin_url(),
            'siteUrl' => home_url(),
            'pluginUrl' => PRORANK_PLUGIN_URL,
            'version' => self::VERSION,
            'buildFingerprint' => $build_info['fingerprint'],
            'buildTimestamp' => $build_info['timestamp'],
            'buildCommit' => $build_info['commit'],
            'isMultisite' => is_multisite(),
            'currentUser' => [
                'id' => $current_user->ID,
                'name' => $current_user->display_name,
                'email' => $current_user->user_email,
                'avatar' => get_avatar_url( $current_user->ID ),
                'capabilities' => $current_user->allcaps,
            ],
            'settings' => $this->settings()->get_all(),
            'license' => [
                'status' => 'inactive',
                'tier' => 'free',
            ],
            'isPremiumActive' => false,
            'upgrade_url' => 'https://prorank.io/pricing',
            'modules' => $this->get_filtered_modules(),
            'notifications' => $this->notifications()->get_notifications(),
            'competitors' => $this->get_competitor_plugins(),
            'urls' => DomainConfig::getAllUrls(),
            'server' => [
                'nginx' => [
                    'server' => $nginx_server,
                ],
                'litespeed' => [
                    'server' => $litespeed_server,
                    'cache_plugin' => $litespeed_cache,
                    'supported' => $litespeed_server || $litespeed_cache,
                ],
            ],
        ];
    }
    
    /**
     * Check if current page is a ProRank admin page
     *
     * @param string $hook_suffix Hook suffix
     * @return bool
     */
    private function is_prorank_admin_page( string $hook_suffix ): bool {
        // Check if it's the top-level page or any submenu page
        if ( strpos( $hook_suffix, 'prorank-seo' ) !== false ) {
            return true;
        }
        
        // Also check the specific hook formats WordPress uses
        $prorank_pages = [
            'toplevel_page_prorank-seo',
            'prorank-seo_page_prorank-on-page-seo',
            'prorank-seo_page_prorank-performance',
            'prorank-seo_page_prorank-technical-seo',
            'prorank-seo_page_prorank-site-audit',
            'prorank-seo_page_prorank-support',
        ];
        
        if ( in_array( $hook_suffix, $prorank_pages, true ) ) {
            return true;
        }
        
        return false;
    }
    
    /**
     * Enqueue frontend assets
     */
    public function enqueue_frontend_assets(): void {
        // Frontend assets will be enqueued by individual modules as needed
    }
    
    /**
     * Check for plugin updates
     */
    private function check_updates(): void {
        // Update check logic here
    }
    
    /**
     * Initialize onboarding
     */
    private function init_onboarding(): void {
        // Check if onboarding is needed
        $onboarding_completed = get_option( 'prorank_onboarding_completed', false );
        
        if ( ! $onboarding_completed && current_user_can( 'manage_options' ) ) {
            // Show onboarding notice
            // $this->admin_notices->add(
            //     'onboarding',
            //     sprintf(
/* translators: %1$: value */
            //         __( 'Welcome to ProRank SEO! <a href="%1$s">Click here to complete the setup wizard</a>.', 'prorank-seo' ),
            //         admin_url( 'admin.php?page=prorank-seo&onboarding=1' )
            //     ),
            //     'info',
            //     false
            // );
        }
    }
    
    /**
     * Get service by name
     *
     * @param string $name Service name
     * @return mixed
     */
    public function get( string $name ) {
        return $this->services->get( $name );
    }
    
    /**
     * Get settings manager
     *
     * @return SettingsManager
     */
    public function settings(): SettingsManager {
        return $this->services->get('settings');
    }
    
    /**
     * Get license manager
     *
     * @return LicenseManager
     */
    public function license(): LicenseManager {
        return $this->services->get('license');
    }
    
    /**
     * Get notification manager
     *
     * @return NotificationManager
     */
    public function notifications(): NotificationManager {
        return $this->services->get('notifications');
    }
    
    /**
     * Get module manager
     *
     * @return ModuleManager
     */
    public function modules(): ModuleManager {
        return $this->services->get('modules');
    }
    
    /**
     * Get menu registry
     *
     * @return \ProRank\SEO\Core\MenuRegistry
     */
    public function menus(): \ProRank\SEO\Core\MenuRegistry {
        return $this->services->get('menus');
    }
    
    /**
     * Check if plugin is initialized
     *
     * @return bool
     */
    public function is_initialized(): bool {
        return !empty($this->services);
    }

    /**
     * Load Elementor integration if Elementor is active
     */
    private function load_elementor_integration(): void {
        // Only load if Elementor is active
        if (!defined('ELEMENTOR_VERSION')) {
            return;
        }

        // Load the integration class
        $integration_file = PRORANK_PLUGIN_DIR . 'includes/Integrations/Elementor/ElementorIntegration.php';
        if (file_exists($integration_file)) {
            require_once $integration_file;
            \ProRank\SEO\Integrations\Elementor\ElementorIntegration::get_instance();
        }
    }

    private function load_divi_integration(): void {
        // Only load if Divi Builder is active (theme or plugin)
        if (!defined('ET_BUILDER_VERSION')) {
            return;
        }

        $integration_file = PRORANK_PLUGIN_DIR . 'includes/Integrations/Divi/DiviIntegration.php';
        if (file_exists($integration_file)) {
            require_once $integration_file;
            \ProRank\SEO\Integrations\Divi\DiviIntegration::get_instance();
        }
    }

    private function load_beaver_builder_integration(): void {
        if (!defined('FL_BUILDER_VERSION') && !class_exists('FLBuilderModel')) {
            return;
        }

        $integration_file = PRORANK_PLUGIN_DIR . 'includes/Integrations/BeaverBuilder/BeaverBuilderIntegration.php';
        if (file_exists($integration_file)) {
            require_once $integration_file;
            \ProRank\SEO\Integrations\BeaverBuilder\BeaverBuilderIntegration::get_instance();
        }
    }

    private function check_competitors(): void {
        // Only check in admin
        if (!is_admin()) {
            return;
        }

        $detector = new \ProRank\SEO\Core\Compatibility\CompetitorDetector();
        $competitors = $detector->get_active_seo_competitors_details();
        $seo_competitors = $detector->get_active_seo_competitors();
        $seo_safe_mode_active = $detector->should_enable_seo_safe_mode();

        // Allow users who intentionally run both plugins to suppress the
        // conflict banner via prorank_conflict_settings['suppress_seo_notice'].
        // This keeps the warning visible for accidental conflicts on other sites.
        $conflict_settings = get_option('prorank_conflict_settings', []);
        if ($seo_safe_mode_active && !empty($conflict_settings['suppress_seo_notice'])) {
            return;
        }

        if (!empty($competitors)) {
            // Add a dismissible admin notice with deactivation links
            add_action('admin_notices', function() use ($competitors, $seo_competitors, $seo_safe_mode_active) {
                ?>
                <div class="notice notice-warning is-dismissible prorank-competitor-notice">
                    <p><strong><?php echo esc_html__('ProRank SEO Conflict Detection:', 'prorank-seo'); ?></strong></p>
                    <p><?php echo esc_html__('We\'ve detected other SEO plugins that may conflict with ProRank SEO. Running multiple SEO plugins can cause issues with your site\'s performance and search rankings.', 'prorank-seo'); ?></p>
                    <?php if ($seo_safe_mode_active && !empty($seo_competitors)) : ?>
                        <p>
                            <strong><?php echo esc_html__('SEO Safe Mode Active:', 'prorank-seo'); ?></strong>
                            <?php
                            echo esc_html(
                                sprintf(
/* translators: %s: list of active SEO plugins */
                                    __('ProRank SEO output is temporarily reduced to prevent duplicate tags while these SEO plugins are active: %s.', 'prorank-seo'),
                                    implode(', ', $seo_competitors)
                                )
                            );
                            ?>
                        </p>
                    <?php endif; ?>
                    
                    <div style="margin: 15px 0;">
                        <?php foreach ($competitors as $competitor): ?>
                            <div style="margin: 10px 0; padding: 10px; background: #fff; border-left: 4px solid #ff9800; display: flex; align-items: center; justify-content: space-between;">
                                <div>
                                    <strong><?php echo esc_html($competitor['name']); ?></strong>
                                    <span style="color: #666; margin-left: 10px;"><?php echo esc_html__('is currently active', 'prorank-seo'); ?></span>
                                </div>
                                <div>
                                    <a href="<?php echo esc_url($competitor['deactivate_url']); ?>" 
                                       class="button button-primary"
                                       onclick="return confirm('<?php
echo esc_js(
    sprintf(
        /* translators: %s: plugin name */
        __( 'Are you sure you want to deactivate %s?', 'prorank-seo' ),
        $competitor['name']
    )
);
?>');">
                                        <?php echo esc_html__('Deactivate', 'prorank-seo'); ?>
                                    </a>
                                    <a href="<?php echo esc_url(admin_url('plugins.php')); ?>" 
                                       class="button" 
                                       style="margin-left: 5px;">
                                        <?php echo esc_html__('Manage Plugins', 'prorank-seo'); ?>
                                    </a>
                                </div>
                            </div>
                        <?php endforeach; ?>
                    </div>
                    
                    <p style="margin-top: 15px;">
                        <strong><?php echo esc_html__('Recommendation:', 'prorank-seo'); ?></strong> 
                        <?php echo esc_html__('We recommend deactivating other SEO plugins before using ProRank SEO to avoid conflicts and ensure optimal performance.', 'prorank-seo'); ?>
                    </p>
                    
                    <p>
                        <a href="<?php echo esc_url(admin_url('admin.php?page=prorank-site-audit#?tab=import-export')); ?>" class="button button-secondary">
                            <?php echo esc_html__('Import Settings from Other SEO Plugins', 'prorank-seo'); ?>
                        </a>
                    </p>
                </div>
                <?php
            });
        }
    }

    /**
     * Enforce performance conflict safe mode before modules initialize.
     *
     * This prevents cache/minify/lazy-load stacks from being enabled in
     * parallel with competing performance plugins.
     *
     * @return void
     */
    public function maybe_apply_performance_conflict_safe_mode(): void {
        try {
            $detector = new \ProRank\SEO\Core\Compatibility\CompetitorDetector();
            if ( ! $detector->should_enable_performance_safe_mode() ) {
                return;
            }

            $active_conflicts = $detector->get_active_performance_competitors();
            if ( empty( $active_conflicts ) ) {
                return;
            }

            $suppressed_modules = $detector->get_performance_safe_mode_suppressions();
            if ( empty( $suppressed_modules ) ) {
                return;
            }

            $settings = get_option( 'prorank_settings', [] );
            if ( ! is_array( $settings ) ) {
                $decoded = json_decode( (string) $settings, true );
                $settings = is_array( $decoded ) ? $decoded : [];
            }

            if ( ! isset( $settings['active_modules'] ) || ! is_array( $settings['active_modules'] ) ) {
                $settings['active_modules'] = [];
            }

            $changed = false;
            foreach ( $suppressed_modules as $module_slug ) {
                if ( $this->is_truthy_setting( $settings['active_modules'][ $module_slug ] ?? null ) ) {
                    $settings['active_modules'][ $module_slug ] = false;
                    $changed = true;
                }
            }

            $groups = get_option( 'prorank_seo_module_groups', [] );
            if ( ! is_array( $groups ) ) {
                $groups = [];
            }

            $performance_enabled = false;
            foreach ( $this->get_performance_child_module_slugs( array_keys( $settings['active_modules'] ) ) as $module_slug ) {
                if ( in_array( $module_slug, $suppressed_modules, true ) ) {
                    continue;
                }

                if ( $this->is_truthy_setting( $settings['active_modules'][ $module_slug ] ?? null ) ) {
                    $performance_enabled = true;
                    break;
                }
            }

            if ( $this->is_truthy_setting( $settings['active_modules']['performance'] ?? false ) !== $performance_enabled ) {
                $settings['active_modules']['performance'] = $performance_enabled;
                $changed = true;
            }

            if ( $this->is_truthy_setting( $groups['performance'] ?? false ) !== $performance_enabled ) {
                $groups['performance'] = $performance_enabled;
                update_option( 'prorank_seo_module_groups', $groups );
                $changed = true;
            }

            if ( $changed ) {
                update_option( 'prorank_settings', $settings );
            }

            if ( ! $performance_enabled ) {
                $this->hard_disable_performance_runtime();
                return;
            }

            foreach ( $suppressed_modules as $module_slug ) {
                $this->disable_performance_runtime_for_module( $module_slug );
            }
            $this->clear_performance_runtime_cache();
        } catch ( \Throwable $e ) {
            prorank_log( '[ProRank SEO] Performance safe mode bootstrap failed: ' . $e->getMessage() );
        }
    }
    
    /**
     * Get competitor plugins
     *
     * @return array
     */
    private function get_competitor_plugins(): array {
        $detector = new \ProRank\SEO\Core\Compatibility\CompetitorDetector();
        return $detector->get_active_seo_competitors();
    }
    
    /**
     * Get service container
     *
     * @return ServiceContainer
     */
    public function get_container(): ServiceContainer {
        return $this->services;
    }
    
    /**
     * Initialize frontend head output
     */
    public function init_frontend_head_output(): void {
        // Only initialize on frontend requests
        if (!is_admin() && !wp_doing_ajax() && !wp_doing_cron()) {
            try {
                $head_output = new HeadOutput($this);
                $head_output->init();
            } catch (\Throwable $e) {
                prorank_log('[ProRank SEO] Failed to initialize HeadOutput: ' . $e->getMessage());
            }

            // Initialize Site Basics Output (verification tags, social meta, org schema)
            try {
                $site_basics_output = new SiteBasicsOutput();
            } catch (\Throwable $e) {
                prorank_log('[ProRank SEO] Failed to initialize SiteBasicsOutput: ' . $e->getMessage());
            }
        }
    }

    /**
     * Initialize virtual IndexNow key responder.
     */
    public function init_indexnow_virtual_file(): void {
        if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
            return;
        }
        try {
            $virtual_file = new IndexNowVirtualFile();
            $virtual_file->init();
        } catch (\Throwable $e) {
            prorank_log('[ProRank SEO] Failed to initialize IndexNowVirtualFile: ' . $e->getMessage());
        }
    }

    /**
     * Initialize speculation rules (safe prefetch/prerender)
     */
    public function init_speculation_rules(): void {
        if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
            return;
        }
        if (!$this->is_performance_module_active('speculation_rules')) {
            return;
        }
        // Respect module/group state and explicit cache settings. Never run by default.
        $cache_settings = get_option('prorank_cache_settings', []);
        $prefetch_enabled = is_array($cache_settings) && (
            $this->is_truthy_setting($cache_settings['prefetch_enabled'] ?? null)
            || $this->is_truthy_setting($cache_settings['prerender_enabled'] ?? null)
        );
        if (!$prefetch_enabled) {
            return;
        }
        // Suppress WordPress core's speculation rules since we provide our own
        add_filter('wp_speculation_rules_configuration', '__return_empty_array', 999);
        remove_action('wp_footer', 'wp_print_speculation_rules');
        // Skip if SpeculationRulesModule is handling this (avoid duplicates)
        $mm = $this->modules();
        if ($mm && isset($mm->get_active_modules()["speculation_rules"])) {
            return;
        }
        try {
            $speculation = new SpeculationRules();
            $speculation->init();
        } catch (\Throwable $e) {
            prorank_log('[ProRank SEO] Failed to initialize SpeculationRules: ' . $e->getMessage());
        }
    }

    /**
     * Initialize resource hints fallback.
     *
     * Ensures settings-driven DNS/font/link hints still work when the
     * `resource_hints` module is disabled in Module Manager.
     */
    public function init_resource_hints_fallback(): void {
        if ( is_admin() || wp_doing_ajax() || wp_doing_cron() ) {
            return;
        }

        // Module handles this when enabled.
        if ( $this->is_performance_module_active( 'resource_hints' ) ) {
            return;
        }

        $cache_settings = get_option( 'prorank_cache_settings', [] );
        if ( ! is_array( $cache_settings ) ) {
            return;
        }

        $dns_enabled   = $this->is_truthy_setting( $cache_settings['dns_prefetch_enabled'] ?? null );
        $font_enabled  = $this->is_truthy_setting( $cache_settings['font_preload_enabled'] ?? null );
        $link_enabled  = $this->is_truthy_setting( $cache_settings['link_preload_enabled'] ?? null );
        $any_enabled   = $dns_enabled || $font_enabled || $link_enabled;

        if ( ! $any_enabled ) {
            return;
        }

        try {
            $resource_hints = new \ProRank\SEO\Modules\Performance\ResourceHintsModule();

            if ( $dns_enabled ) {
                add_filter( 'wp_resource_hints', [ $resource_hints, 'add_dns_prefetch_hints' ], 10, 2 );
            }

            if ( $font_enabled ) {
                add_action( 'wp_head', [ $resource_hints, 'add_font_preload_links' ], 2 );
            }

            if ( $link_enabled ) {
                add_action( 'wp_enqueue_scripts', [ $resource_hints, 'enqueue_link_preload_script' ] );
            }
        } catch ( \Throwable $e ) {
            prorank_log( '[ProRank SEO] Failed to initialize resource hints fallback: ' . $e->getMessage() );
        }
    }

    /**
     * Initialize third-party defer for INP improvements
     */
    public function init_third_party_defer(): void {
        if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
            return;
        }

        $asset_settings = get_option('prorank_asset_optimization_settings', []);
        $cache_settings = get_option('prorank_cache_settings', []);

        $inp_enabled = $this->is_performance_module_active('inp_optimizer')
            && is_array($asset_settings)
            && $this->is_truthy_setting($asset_settings['inp_optimizer_enabled'] ?? null);

        $tracker_delay_enabled = $this->is_performance_module_active('js_opt')
            && is_array($cache_settings)
            && (
                $this->is_truthy_setting($cache_settings['tracker_delay_enabled'] ?? null)
                || $this->is_truthy_setting($cache_settings['js_delay'] ?? null)
            );

        if (!$inp_enabled && !$tracker_delay_enabled) {
            return;
        }

        try {
            $defer = new ThirdPartyDefer();
            $defer->init();
        } catch (\Throwable $e) {
            prorank_log('[ProRank SEO] Failed to initialize ThirdPartyDefer: ' . $e->getMessage());
        }
    }

    /**
     * Initialize lazy media enhancer
     */
    public function init_lazy_media(): void {
        if (is_admin()) {
            return;
        }
        if (
            !$this->is_performance_module_active('lazyload')
            && !$this->is_performance_module_active('image_optimization')
        ) {
            return;
        }
        $image_settings = get_option('prorank_image_optimization_settings', []);
        $lazy_enabled = $this->is_lazyload_enabled_in_image_settings($image_settings);
        if (!$lazy_enabled) {
            return;
        }
        try {
            $lazy = new LazyMedia();
            $lazy->init();
        } catch (\Throwable $e) {
            prorank_log('[ProRank SEO] Failed to initialize LazyMedia: ' . $e->getMessage());
        }
    }

    /**
     * Whether lazyload is enabled in the image settings payload.
     *
     * Accept the legacy umbrella flag as a valid source of truth so older or
     * mixed payloads still boot lazy media features consistently.
     *
     * @param mixed $image_settings
     * @return bool
     */
    private function is_lazyload_enabled_in_image_settings($image_settings): bool {
        return is_array($image_settings) && (
            $this->is_truthy_setting($image_settings['lazyload_images'] ?? null) ||
            $this->is_truthy_setting($image_settings['lazyload_iframes'] ?? null) ||
            $this->is_truthy_setting($image_settings['lazyload_videos'] ?? null) ||
            $this->is_truthy_setting($image_settings['lazyload_enabled'] ?? null)
        );
    }

    /**
     * Initialize a safe CSS-async fallback for free/performance baseline.
     *
     * This only runs when css_async is enabled and the dedicated css_opt module
     * is not active. It defers non-critical third-party styles while preserving
     * core/theme/page-builder critical CSS on the blocking path.
     *
     * @return void
     */
    public function init_css_async_fallback(): void {
        if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
            return;
        }

        // Avoid conflicts when dedicated CSS optimization module is available.
        if ($this->is_performance_module_active('css_opt')) {
            return;
        }

        $cache_settings = get_option('prorank_cache_settings', []);
        if (!is_array($cache_settings) || !$this->is_truthy_setting($cache_settings['css_async'] ?? null)) {
            return;
        }

        add_filter('style_loader_tag', [$this, 'apply_css_async_fallback'], 20, 4);
    }

    /**
     * Convert non-critical stylesheet tags to preload+swap.
     *
     * @param string $html   Original stylesheet tag.
     * @param string $handle Style handle.
     * @param string $href   Style URL.
     * @param string $media  Media attribute.
     * @return string
     */
    public function apply_css_async_fallback(string $html, string $handle, string $href, string $media): string {
        if ($href === '' || stripos($html, 'rel=\'preload\'') !== false || stripos($html, 'rel="preload"') !== false) {
            return $html;
        }

        if (!preg_match('/\.css(?:$|\?)/i', $href)) {
            return $html;
        }

        if ($this->should_keep_blocking_stylesheet($handle, $href, $media)) {
            return $html;
        }

        $href_esc = esc_url($href);
        $handle_attr = sanitize_key($handle);
        $id_attr = $handle_attr !== '' ? " id='{$handle_attr}-css-async'" : '';

        // Keep the final stylesheet media as all to ensure styles apply after swap.
        $preload = "<link rel='preload' as='style'{$id_attr} href='{$href_esc}' onload=\"this.onload=null;this.rel='stylesheet'\" />";
        $noscript = "<noscript><link rel='stylesheet' href='{$href_esc}' media='all' /></noscript>"; // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet

        return $preload . $noscript;
    }

    /**
     * Decide if a stylesheet should remain render-blocking.
     *
     * @param string $handle Style handle.
     * @param string $href   Style URL.
     * @param string $media  Media attribute.
     * @return bool
     */
    private function should_keep_blocking_stylesheet(string $handle, string $href, string $media): bool {
        // Keep admin-bar and logged-in UX stable.
        if (is_user_logged_in()) {
            return true;
        }

        $media_normalized = strtolower(trim((string) $media));
        if ($media_normalized !== '' && $media_normalized !== 'all' && $media_normalized !== 'screen') {
            return true;
        }

        $handle_lc = strtolower($handle);
        $href_lc = strtolower($href);

        // Keep core/theme/page-builder critical path blocking.
        $blocking_handle_patterns = [
            'astra-theme-css',
            'elementor-frontend',
            'elementor-post-',
            'hfe-style',
            'tve_frontend',
            'thrive-',
            'wp-block-library',
            'global-styles',
            'classic-theme-styles',
            'dashicons',
            'admin-bar',
            'wp-emoji',
        ];

        foreach ($blocking_handle_patterns as $pattern) {
            if ($pattern !== '' && strpos($handle_lc, $pattern) !== false) {
                return true;
            }
        }

        // Keep critical core/theme assets blocking even when handle names vary.
        $blocking_href_patterns = [
            '/wp-includes/css/',
            '/wp-content/themes/',
            '/wp-content/uploads/elementor/css/post-',
            '/wp-content/plugins/elementor/assets/css/frontend',
            '/thrive-visual-editor/',
        ];

        foreach ($blocking_href_patterns as $pattern) {
            if ($pattern !== '' && strpos($href_lc, $pattern) !== false) {
                return true;
            }
        }

        return false;
    }

    /**
     * Initialize lightweight JS defer fallback.
     *
     * This only runs when js_defer is enabled and the dedicated js_opt module
     * is NOT active (e.g. suppressed on builder sites by the compatibility
     * engine). Unlike js_opt, this path does NOT activate the output buffer,
     * does NOT touch non-enqueued inline scripts, and does NOT delay anything.
     * It simply adds the `defer` attribute to eligible enqueued scripts via
     * the `script_loader_tag` filter — the lowest-risk JS optimization.
     *
     * @return void
     */
    public function init_js_defer_fallback(): void {
        if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
            return;
        }

        // When js_opt is active it handles defer itself; avoid double-hooking.
        if ($this->is_performance_module_active('js_opt')) {
            return;
        }

        $cache_settings = get_option('prorank_cache_settings', []);
        if (!is_array($cache_settings) || !$this->is_truthy_setting($cache_settings['js_defer'] ?? null)) {
            return;
        }

        add_filter('script_loader_tag', [$this, 'apply_js_defer_fallback'], 10, 3);
    }

    /**
     * Add `defer` attribute to eligible enqueued script tags.
     *
     * @param string $tag    Full `<script>` tag.
     * @param string $handle Script handle.
     * @param string $src    Script source URL.
     * @return string
     */
    public function apply_js_defer_fallback(string $tag, string $handle, string $src): string {
        // Only touch external scripts (those with a src attribute).
        if ($src === '' || strpos($tag, ' src=') === false) {
            return $tag;
        }

        // Already has defer or async — nothing to do.
        if (preg_match('/\s(?:defer|async)[\s>\/=]/i', $tag)) {
            return $tag;
        }

        // Respect opt-out attributes from other plugins/themes.
        if (stripos($tag, 'data-no-defer') !== false || stripos($tag, 'data-cfasync="false"') !== false) {
            return $tag;
        }

        if ($this->should_keep_blocking_script($handle, $src)) {
            return $tag;
        }

        // Scripts with inline 'after' code rely on the external file having
        // finished execution before the inline <script> runs. Deferring the
        // external file breaks that ordering because the inline sibling stays
        // synchronous while the external load is postponed.
        if ($this->script_has_inline_after($handle)) {
            return $tag;
        }

        // Insert defer before the closing > of the <script tag.
        return preg_replace('/(<script\b[^>]*)(>)/i', '$1 defer$2', $tag, 1);
    }

    /**
     * Whether a registered script has inline 'after' code attached.
     *
     * wp_add_inline_script( $handle, $code, 'after' ) emits a synchronous
     * <script> block immediately after the external tag. If the external tag
     * is deferred, the inline code runs first and references undefined vars.
     *
     * @param string $handle Script handle.
     * @return bool
     */
    private function script_has_inline_after(string $handle): bool {
        if ($handle === '') {
            return false;
        }

        $wp_scripts = wp_scripts();
        if (!is_object($wp_scripts) || !isset($wp_scripts->registered[$handle])) {
            return false;
        }

        $extra = $wp_scripts->registered[$handle]->extra ?? [];
        if (!is_array($extra)) {
            return false;
        }

        // 'after' is the dangerous one — it expects the external script to
        // have already executed. 'before' and 'data' (wp_localize_script)
        // run before the external tag, so they are safe with defer.
        return !empty($extra['after']);
    }

    /**
     * Decide if a script must remain render-blocking.
     *
     * Conservative for the safe profile: blocks ProRank loaders, polyfills,
     * jQuery, payment/booking/map scripts, page-builder runtime, and captcha.
     *
     * @param string $handle Script handle.
     * @param string $src    Script source URL.
     * @return bool
     */
    private function should_keep_blocking_script(string $handle, string $src): bool {
        // Keep admin-bar and logged-in UX stable.
        if (is_user_logged_in()) {
            return true;
        }

        // ProRank's own loader/facade scripts must remain synchronous.
        $prorank_sync_handles = [
            'prorank-delay-loader',
            'prorank-jquery-facade',
            'prorank-wp-shim',
            'prorank-cls-fix',
            'prorank-async-css-loader',
            'prorank-rum-beacon',
            'prorank-lcp-beacon',
            'prorank-inp-monitor',
        ];

        if (in_array($handle, $prorank_sync_handles, true)) {
            return true;
        }

        // WordPress core script packages rely on the global `wp` namespace and
        // on each other in tightly-coupled ways. Keep them synchronous in the
        // lightweight defer fallback to avoid frontend ordering regressions.
        if ($handle === 'wp-polyfill'
            || $handle === 'regenerator-runtime'
            || strpos($handle, 'wp-') === 0
            || strpos($src_lc = strtolower($src), '/wp-includes/js/') !== false) {
            return true;
        }

        // jQuery — many inline scripts assume synchronous jQuery. Deferring
        // it without a facade (which only js_opt provides) will break them.
        if (in_array($handle, ['jquery', 'jquery-core', 'jquery-migrate'], true)) {
            return true;
        }

        $src_lc = isset($src_lc) ? $src_lc : strtolower($src);
        $handle_lc = strtolower($handle);

        // Payment processors: must stay sync on checkout/booking flows.
        $payment_patterns = [
            'stripe', 'paypal', 'braintree', 'klarna', 'squareup', 'square.',
            'wc-checkout', 'woocommerce-checkout',
            '/woocommerce/assets/js/frontend/checkout',
        ];

        foreach ($payment_patterns as $p) {
            if (strpos($handle_lc, $p) !== false || strpos($src_lc, $p) !== false) {
                return true;
            }
        }

        // Booking systems: calendar/date-picker logic breaks if deferred.
        $booking_patterns = [
            'bookly', 'amelia', 'latepoint', 'booknetic',
            'simply-schedule-appointments',
        ];

        foreach ($booking_patterns as $p) {
            if (strpos($handle_lc, $p) !== false || strpos($src_lc, $p) !== false) {
                return true;
            }
        }

        // Google Maps: synchronous callback expected by many themes/plugins.
        if (strpos($src_lc, 'maps.googleapis.com/maps/api/js') !== false
            || strpos($src_lc, 'maps.gstatic.com') !== false) {
            return true;
        }

        // reCAPTCHA / hCaptcha / Turnstile: render callbacks expect sync load.
        $captcha_patterns = [
            'recaptcha', 'hcaptcha', 'challenges.cloudflare.com/turnstile',
            'google.com/recaptcha', 'gstatic.com/recaptcha',
        ];

        foreach ($captcha_patterns as $p) {
            if (strpos($handle_lc, $p) !== false || strpos($src_lc, $p) !== false) {
                return true;
            }
        }

        // Page builder runtime scripts: keep the full Thrive/Elementor/Divi
        // frontend bootstrap synchronous in the lightweight defer fallback.
        // Thrive's mobile navigation and interactive modules can fail to
        // initialize when these scripts are deferred on builder-heavy pages.
        $builder_exact_handles = [
            'tve_frontend',
            'theme-frontend-js',
            'tve-dash-frontend-js',
        ];

        if (in_array($handle, $builder_exact_handles, true)) {
            return true;
        }

        $builder_patterns = [
            'elementor-frontend', 'elementor-pro-frontend',
            'divi-custom-script', 'et-builder-modules-script',
            'tve_frontend',
            'thrive-',
            '/wp-content/themes/thrive-theme/inc/assets/dist/frontend',
            '/wp-content/themes/thrive-theme/thrive-dashboard/js/dist/frontend',
            '/wp-content/plugins/thrive-visual-editor/editor/js/dist/modules/',
        ];

        foreach ($builder_patterns as $p) {
            if (strpos($handle_lc, $p) !== false) {
                return true;
            }
        }

        return false;
    }

    /**
     * Initialize RUM beacon (INP/CLS/LCP)
     */
    public function init_rum_beacon(): void {
        if (is_admin()) {
            return;
        }
        if (
            !$this->is_performance_module_active('performance_monitor')
            && !$this->is_performance_module_active('inp_optimizer')
            && !$this->is_performance_module_active('lcp_beacon')
        ) {
            return;
        }
        try {
            $rum = new RumBeacon();
            $rum->init();
        } catch (\Throwable $e) {
            prorank_log('[ProRank SEO] Failed to initialize RumBeacon: ' . $e->getMessage());
        }
    }

    /**
     * Synchronize runtime performance settings when an individual module is toggled.
     *
     * @param string $module_slug Module slug.
     * @param bool   $enabled     Enabled state.
     * @return void
     */
    public function handle_module_toggle_sync(string $module_slug, bool $enabled): void {
        // If the performance group itself was disabled, hard-disable runtime options.
        if ($module_slug === 'performance' && !$enabled) {
            $this->hard_disable_performance_runtime();
            return;
        }

        // For any individual performance module toggle, purge runtime cache so
        // stale HTML/CSS/JS artifacts don't continue to affect rendering.
        if ($this->is_performance_child_module($module_slug)) {
            if ($enabled) {
                $this->apply_performance_defaults_for_module($module_slug);
            } else {
                $this->disable_performance_runtime_for_module($module_slug);
            }
            $this->clear_performance_runtime_cache();
        }
    }

    /**
     * Synchronize runtime performance settings when a module group is toggled.
     *
     * @param string $group_slug Group slug.
     * @param bool   $enabled    Enabled state.
     * @return void
     */
    public function handle_module_group_toggle_sync(string $group_slug, bool $enabled): void {
        if ($group_slug !== 'performance') {
            return;
        }

        if (!$enabled) {
            $this->hard_disable_performance_runtime();
            return;
        }

        $this->apply_recommended_performance_defaults();
        $this->clear_performance_runtime_cache();
    }

    /**
     * Apply a conservative performance baseline when the performance group
     * is enabled. Existing explicit user values are preserved.
     *
     * @return void
     */
    private function apply_recommended_performance_defaults(): void {
        // Force a safe high-impact baseline when users explicitly enable the
        // performance group. They can still change any setting afterwards.
        $this->force_performance_option_values(
            'prorank_cache_settings',
            [
                'enabled' => true,
                'cache_enabled' => true,
                'browser_cache_enabled' => true,
                'dns_prefetch_enabled' => true,
                'dns_prefetch_hosts' => "fonts.googleapis.com\nfonts.gstatic.com\nwww.googletagmanager.com",
                'js_defer' => true,
                'css_minify' => true,
                'css_async' => true,
            ]
        );

        $this->force_performance_option_values(
            'prorank_asset_optimization_settings',
            [
                'js_minify_enabled' => true,
                'minify_html' => true,
                'remove_html_comments' => true,
                'font_display_swap' => true,
            ]
        );

        // Keep lazy-loading conservative by default to avoid CLS regressions
        // on some themes; still enable modern image delivery/compression.
        $this->force_performance_option_values(
            'prorank_image_optimization_settings',
            [
                'webp_enabled' => true,
                'smart_compression' => true,
                'lazyload_images' => false,
                'lazyload_iframes' => false,
                'lazyload_videos' => false,
            ]
        );
    }

    /**
     * Ensure a toggled performance child module has the minimal runtime keys
     * enabled so the module takes effect immediately.
     *
     * @param string $module_slug Module slug.
     * @return void
     */
    private function apply_performance_defaults_for_module(string $module_slug): void {
        switch ($module_slug) {
            case 'cache':
            case 'modern_cache':
                $this->force_performance_option_values('prorank_cache_settings', [
                    'enabled' => true,
                    'cache_enabled' => true,
                ]);
                break;

            case 'browser_cache':
                $this->force_performance_option_values('prorank_cache_settings', [
                    'browser_cache_enabled' => true,
                ]);
                break;

            case 'resource_hints':
                $this->force_performance_option_values('prorank_cache_settings', [
                    'dns_prefetch_enabled' => true,
                ]);
                $cache_settings = get_option('prorank_cache_settings', []);
                if (is_array($cache_settings) && empty(trim((string) ($cache_settings['dns_prefetch_hosts'] ?? '')))) {
                    $cache_settings['dns_prefetch_hosts'] = "fonts.googleapis.com\nfonts.gstatic.com\nwww.googletagmanager.com";
                    update_option('prorank_cache_settings', $cache_settings);
                }
                break;

            case 'js_opt':
                $this->force_performance_option_values('prorank_cache_settings', [
                    'tracker_delay_enabled' => true,
                ]);
                $this->force_performance_option_values('prorank_asset_optimization_settings', [
                    'js_minify_enabled' => true,
                ]);
                break;

            case 'css_opt':
                $this->force_performance_option_values('prorank_cache_settings', [
                    'css_minify' => true,
                    'css_async' => true,
                ]);
                break;

            case 'font_optimization':
                $this->force_performance_option_values('prorank_asset_optimization_settings', [
                    'host_google_fonts_locally' => true,
                    'font_display_swap' => true,
                ]);
                break;

            case 'lazyload':
                $this->force_performance_option_values('prorank_image_optimization_settings', [
                    'lazyload_images' => true,
                    'lazyload_iframes' => true,
                    'lazyload_videos' => true,
                    'lazyload_enabled' => true,
                ]);
                break;

            case 'image_optimization':
                $this->force_performance_option_values('prorank_image_optimization_settings', [
                    'webp_enabled' => true,
                    'smart_compression' => true,
                    'automatic_optimization' => true,
                ]);
                break;

            case 'js_minify':
                $this->force_performance_option_values('prorank_asset_optimization_settings', [
                    'js_minify_enabled' => true,
                ]);
                break;

            case 'html_minify':
                $this->force_performance_option_values('prorank_asset_optimization_settings', [
                    'minify_html' => true,
                    'remove_html_comments' => true,
                ]);
                break;

            case 'inp_optimizer':
                $this->force_performance_option_values('prorank_asset_optimization_settings', [
                    'inp_optimizer_enabled' => true,
                ]);
                break;

            case 'lcp_optimizer':
            case 'lcp_beacon':
                $this->force_performance_option_values('prorank_asset_optimization_settings', [
                    'lcp_optimizer_enabled' => true,
                    'lcp_fetchpriority_enabled' => true,
                    'lcp_preload_lcp' => true,
                    'lcp_remove_above_fold_lazy' => true,
                ]);
                break;
        }
    }

    /**
     * Merge defaults into a settings option without overriding explicit values.
     *
     * @param string               $option_name Option name.
     * @param array<string, mixed> $defaults    Defaults to apply only when missing.
     * @return void
     */
    private function merge_performance_option_defaults(string $option_name, array $defaults): void {
        $settings = get_option($option_name, []);
        if (!is_array($settings)) {
            $settings = [];
        }

        $changed = false;
        foreach ($defaults as $key => $value) {
            if (!array_key_exists($key, $settings)) {
                $settings[$key] = $value;
                $changed = true;
            }
        }

        if ($changed) {
            update_option($option_name, $settings);
        }
    }

    /**
     * Force explicit settings values for a given option.
     *
     * @param string               $option_name Option name.
     * @param array<string, mixed> $values      Values to force.
     * @return void
     */
    private function force_performance_option_values(string $option_name, array $values): void {
        $settings = get_option($option_name, []);
        if (!is_array($settings)) {
            $settings = [];
        }

        $changed = false;
        foreach ($values as $key => $value) {
            if (!array_key_exists($key, $settings) || $settings[$key] !== $value) {
                $settings[$key] = $value;
                $changed = true;
            }
        }

        if ($changed) {
            update_option($option_name, $settings);
        }
    }

    /**
     * Disable runtime settings for a specific performance child module.
     *
     * Keeps Module Manager and the settings pages aligned so turning a module
     * off does not leave the underlying runtime flags enabled.
     *
     * @param string $module_slug Module slug.
     * @return void
     */
    private function disable_performance_runtime_for_module(string $module_slug): void {
        switch ($module_slug) {
            case 'cache':
            case 'modern_cache':
                $this->force_performance_option_values('prorank_cache_settings', [
                    'enabled' => false,
                    'cache_enabled' => false,
                ]);
                break;

            case 'browser_cache':
                $this->force_performance_option_values('prorank_cache_settings', [
                    'browser_cache_enabled' => false,
                ]);
                break;

            case 'cache_preload':
                $this->force_performance_option_values('prorank_cache_settings', [
                    'preload_enabled' => false,
                ]);
                break;

            case 'resource_hints':
                $this->force_performance_option_values('prorank_cache_settings', [
                    'dns_prefetch_enabled' => false,
                    'font_preload_enabled' => false,
                    'link_preload_enabled' => false,
                    'dns_prefetch_hosts' => '',
                ]);
                break;

            case 'speculation_rules':
                $this->force_performance_option_values('prorank_cache_settings', [
                    'prefetch_enabled' => false,
                    'prerender_enabled' => false,
                ]);
                break;

            case 'js_opt':
                $this->force_performance_option_values('prorank_cache_settings', [
                    'js_defer' => false,
                    'js_delay' => false,
                    'js_delay_jquery' => false,
                    'js_delay_elementor_modules' => false,
                    'tracker_delay_enabled' => false,
                ]);
                $this->force_performance_option_values('prorank_asset_optimization_settings', [
                    'js_advanced_enabled' => false,
                ]);
                break;

            case 'css_opt':
                $this->force_performance_option_values('prorank_cache_settings', [
                    'css_minify' => false,
                    'css_combine' => false,
                    'css_async' => false,
                ]);
                break;

            case 'font_optimization':
                $this->force_performance_option_values('prorank_asset_optimization_settings', [
                    'host_google_fonts_locally' => false,
                    'font_display_swap' => false,
                ]);
                break;

            case 'lazyload':
                $this->force_performance_option_values('prorank_image_optimization_settings', [
                    'lazyload_images' => false,
                    'lazyload_iframes' => false,
                    'lazyload_videos' => false,
                    'lazyload_enabled' => false,
                ]);
                break;

            case 'image_optimization':
                $this->force_performance_option_values('prorank_image_optimization_settings', [
                    'webp_enabled' => false,
                    'avif_enabled' => false,
                    'jxl_enabled' => false,
                    'jpegli_enabled' => false,
                    'optimize_on_upload' => false,
                    'smart_compression' => false,
                    'background_mode' => false,
                    'png_to_jpeg' => false,
                    'cmyk_to_rgb' => false,
                    'remove_exif' => false,
                    'optimize_pdf' => false,
                    'generate_retina' => false,
                    'automatic_optimization' => false,
                ]);
                break;

            case 'js_minify':
                $this->force_performance_option_values('prorank_asset_optimization_settings', [
                    'js_minify_enabled' => false,
                    'js_minify_inline' => false,
                ]);
                break;

            case 'html_minify':
                $this->force_performance_option_values('prorank_asset_optimization_settings', [
                    'minify_html' => false,
                    'remove_html_comments' => false,
                ]);
                break;

            case 'inp_optimizer':
                $this->force_performance_option_values('prorank_asset_optimization_settings', [
                    'inp_optimizer_enabled' => false,
                ]);
                break;

            case 'lcp_optimizer':
            case 'lcp_beacon':
                $this->force_performance_option_values('prorank_asset_optimization_settings', [
                    'lcp_optimizer_enabled' => false,
                    'lcp_fetchpriority_enabled' => false,
                    'lcp_preload_lcp' => false,
                    'lcp_optimize_first_image' => false,
                    'lcp_remove_above_fold_lazy' => false,
                ]);
                break;
        }
    }

    /**
     * Check whether the module belongs to the performance group.
     *
     * @param string $module_slug Module slug.
     * @return bool
     */
    private function is_performance_child_module(string $module_slug): bool {
        try {
            $mm = $this->modules();
            if (!$mm || !$mm->has_module($module_slug)) {
                return false;
            }

            $module = $mm->get_module($module_slug);
            return is_object($module)
                && method_exists($module, 'get_parent_slug')
                && $module->get_parent_slug() === 'performance';
        } catch (\Throwable $e) {
            return false;
        }
    }

    /**
     * Clear runtime performance caches after settings/rule/module changes.
     *
     * @return void
     */
    public function clear_performance_runtime_cache(): void {
        try {
            $mm = $this->modules();
            if ($mm) {
                $module_methods = [
                    'modern_cache' => ['clear_all_cache'],
                    'cache' => ['clear_all_cache'],
                    'css_optimize' => ['clear_css_cache'],
                    'js_minify' => ['clear_cache'],
                    'js_optimize' => ['clear_combine_cache'],
                ];

                foreach ($module_methods as $slug => $methods) {
                    $module = $mm->get_module($slug);
                    if (!is_object($module)) {
                        continue;
                    }
                    foreach ($methods as $method) {
                        if (method_exists($module, $method)) {
                            $module->{$method}();
                        }
                    }
                }
            }

            if (function_exists('wp_cache_flush')) {
                wp_cache_flush();
            }
        } catch (\Throwable $e) {
            prorank_log('[ProRank SEO] Failed to clear performance runtime cache: ' . $e->getMessage());
        }
    }

    /**
     * Whether the performance group is currently enabled.
     *
     * @return bool
     */
    private function is_performance_group_enabled(): bool {
        $settings = get_option('prorank_settings', []);
        if (!is_array($settings)) {
            $decoded = json_decode((string) $settings, true);
            $settings = is_array($decoded) ? $decoded : [];
        }
        $active_modules = isset($settings['active_modules']) && is_array($settings['active_modules'])
            ? $settings['active_modules']
            : [];
        if (array_key_exists('performance', $active_modules) && !$this->is_truthy_setting($active_modules['performance'])) {
            return false;
        }

        $group_states = get_option('prorank_seo_module_groups', []);
        if (is_array($group_states) && array_key_exists('performance', $group_states)) {
            return $this->is_truthy_setting($group_states['performance']);
        }

        // If no explicit group state exists yet, treat as enabled only when a
        // performance child module is active.
        $mm = $this->modules();
        if (!$mm) {
            return false;
        }
        foreach ($mm->get_modules() as $slug => $module) {
            if (is_object($module) && method_exists($module, 'get_parent_slug') && $module->get_parent_slug() === 'performance') {
                if ($mm->is_active((string) $slug)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Whether a specific performance module is active.
     *
     * @param string $slug Module slug.
     * @return bool
     */
    private function is_performance_module_active(string $slug): bool {
        if (in_array($slug, $this->get_suppressed_performance_modules(), true)) {
            return false;
        }

        try {
            $mm = $this->modules();
            if (!$mm || !$mm->has_module($slug)) {
                return false;
            }
            return $mm->is_active($slug);
        } catch (\Throwable $e) {
            return false;
        }
    }

    /**
     * Runtime safety guard for performance features.
     *
     * If the performance group is disabled, force-disable stale runtime toggles
     * and cache artifacts that may still affect frontend rendering.
     *
     * @return void
     */
    public function enforce_performance_runtime_safety(): void {
        if ($this->is_performance_group_enabled()) {
            return;
        }

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

        $this->hard_disable_performance_runtime();
    }

    /**
     * Check whether any performance runtime flags are still enabled.
     *
     * @return bool
     */
    private function has_enabled_performance_runtime_flags(): bool {
        $cache_settings = get_option('prorank_cache_settings', []);
        if (is_array($cache_settings)) {
            foreach ([
                'enabled',
                'cache_enabled',
                'preload_enabled',
                'link_preload_enabled',
                'prefetch_enabled',
                'prerender_enabled',
                'browser_cache_enabled',
                'js_defer',
                'js_delay',
                'js_delay_jquery',
                'js_delay_elementor_modules',
                'css_minify',
                'css_combine',
                'css_async',
                'edge_cache_enabled',
                'preload_ai_enabled',
                'early_hints_enabled',
                'critical_css_enabled',
                'override_core',
                'link_fallback',
                'dns_prefetch_enabled',
                'font_preload_enabled',
                'htaccess_serving',
            ] as $key) {
                if ($this->is_truthy_setting($cache_settings[$key] ?? null)) {
                    return true;
                }
            }
        }

        $asset_settings = get_option('prorank_asset_optimization_settings', []);
        if (is_array($asset_settings)) {
            foreach ([
                'minify_html',
                'remove_html_comments',
                'css_minify',
                'css_combine',
                'js_minify_enabled',
                'js_minify_inline',
                'host_google_fonts_locally',
                'font_display_swap',
            ] as $key) {
                if ($this->is_truthy_setting($asset_settings[$key] ?? null)) {
                    return true;
                }
            }
        }

        $image_settings = get_option('prorank_image_optimization_settings', []);
        if (is_array($image_settings)) {
            foreach ([
                'optimize_on_upload',
                'lazyload_images',
                'lazyload_iframes',
                'lazyload_videos',
                'webp_enabled',
                'avif_enabled',
                'jxl_enabled',
                'smart_compression',
            ] as $key) {
                if ($this->is_truthy_setting($image_settings[$key] ?? null)) {
                    return true;
                }
            }
        }

        if ($this->is_truthy_setting(get_option('prorank_page_cache_enabled', false))) {
            return true;
        }

        if (class_exists('\ProRank\SEO\Cache\AdvancedCache') && \ProRank\SEO\Cache\AdvancedCache::is_installed()) {
            return true;
        }

        return false;
    }

    /**
     * Normalize bool-like option values.
     *
     * Strings such as "false" should not be treated as enabled.
     *
     * @param mixed $value
     * @return bool
     */
    private function is_truthy_setting($value): bool {
        if (is_bool($value)) {
            return $value;
        }

        if (is_int($value) || is_float($value)) {
            return ((int) $value) !== 0;
        }

        if (is_string($value)) {
            $normalized = strtolower(trim($value));
            if ($normalized === '' || in_array($normalized, ['0', 'false', 'no', 'off', 'disabled'], true)) {
                return false;
            }
            if (in_array($normalized, ['1', 'true', 'yes', 'on', 'enabled'], true)) {
                return true;
            }
        }

        return !empty($value);
    }

    /**
     * Reconcile performance child module flags from persisted option settings.
     *
     * This self-heals older installs where settings were enabled before module
     * flags were synchronized, ensuring runtime features actually boot.
     *
     * @return void
     */
    public function reconcile_performance_active_modules_from_settings(): void {
        if ($this->is_performance_group_explicitly_disabled()) {
            $this->normalize_explicitly_disabled_performance_group();
            return;
        }

        $this->maybe_backfill_safe_performance_defaults();

        $flags = $this->apply_performance_module_suppression($this->get_expected_performance_module_flags_from_settings());
        if (empty($flags)) {
            return;
        }

        $settings = get_option('prorank_settings', []);
        if (!is_array($settings)) {
            $decoded = json_decode((string) $settings, true);
            $settings = is_array($decoded) ? $decoded : [];
        }

        $active_modules = isset($settings['active_modules']) && is_array($settings['active_modules'])
            ? $settings['active_modules']
            : [];

        $changed = false;
        foreach ($flags as $module_slug => $enabled) {
            $normalized_enabled = (bool) $enabled;
            if (!array_key_exists($module_slug, $active_modules) || $this->is_truthy_setting($active_modules[$module_slug]) !== $normalized_enabled) {
                $active_modules[$module_slug] = $normalized_enabled;
                $changed = true;
            }
        }

        $performance_child_slugs = $this->get_performance_child_module_slugs(array_keys($active_modules));
        $performance_group_enabled = false;
        foreach ($performance_child_slugs as $module_slug) {
            if ($this->is_truthy_setting($active_modules[$module_slug] ?? false)) {
                $performance_group_enabled = true;
                break;
            }
        }

        if ($this->is_truthy_setting($active_modules['performance'] ?? false) !== $performance_group_enabled) {
            $active_modules['performance'] = $performance_group_enabled;
            $changed = true;
        }

        $group_states = get_option('prorank_seo_module_groups', []);
        if (!is_array($group_states)) {
            $group_states = [];
        }

        $group_changed = !array_key_exists('performance', $group_states)
            || $this->is_truthy_setting($group_states['performance']) !== $performance_group_enabled;

        if ($changed) {
            $settings['active_modules'] = $active_modules;
            update_option('prorank_settings', $settings);
        }

        if ($group_changed) {
            $group_states['performance'] = $performance_group_enabled;
            update_option('prorank_seo_module_groups', $group_states);
        }
    }

    /**
     * Backfill safe performance defaults for older installs that already have
     * the performance group enabled but predate the current baseline.
     *
     * This only fills missing keys. It does not override explicit user choices.
     *
     * @return void
     */
    private function maybe_backfill_safe_performance_defaults(): void {
        if (!$this->is_performance_group_enabled()) {
            return;
        }

        $this->merge_performance_option_defaults(
            'prorank_cache_settings',
            [
                'browser_cache_enabled' => true,
                'preload_enabled' => true,
                'dns_prefetch_enabled' => true,
                'dns_prefetch_hosts' => "fonts.googleapis.com\nfonts.gstatic.com\nwww.googletagmanager.com",
            ]
        );

        $this->merge_performance_option_defaults(
            'prorank_asset_optimization_settings',
            [
                'font_display_swap' => true,
            ]
        );
    }

    /**
     * Apply profile- and rollback-based performance safeguards before modules boot.
     *
     * @return void
     */
    public function apply_performance_engine_safeguards(): void {
        $suppressed = $this->get_suppressed_performance_modules();
        $stack = $this->get_performance_stack_signature();

        $status = [
            'profile' => $this->get_performance_profile(),
            'compatibility_mode' => $this->is_performance_compatibility_mode_enabled(),
            'auto_rollback' => $this->is_performance_auto_rollback_enabled(),
            'stack' => $stack,
            'suppressed_modules' => $suppressed,
            'updated_at' => time(),
        ];

        if (empty($suppressed)) {
            update_option('prorank_performance_engine_status', $status, false);
            return;
        }

        $settings = get_option('prorank_settings', []);
        if (!is_array($settings)) {
            $decoded = json_decode((string) $settings, true);
            $settings = is_array($decoded) ? $decoded : [];
        }

        $active_modules = isset($settings['active_modules']) && is_array($settings['active_modules'])
            ? $settings['active_modules']
            : [];

        $changed = false;
        foreach ($suppressed as $module_slug) {
            if ($this->is_truthy_setting($active_modules[$module_slug] ?? false)) {
                $active_modules[$module_slug] = false;
                $changed = true;
            }
        }

        $performance_enabled = false;
        foreach ($this->get_performance_child_module_slugs(array_keys($active_modules)) as $module_slug) {
            if ($this->is_truthy_setting($active_modules[$module_slug] ?? false)) {
                $performance_enabled = true;
                break;
            }
        }

        if ($this->is_truthy_setting($active_modules['performance'] ?? false) !== $performance_enabled) {
            $active_modules['performance'] = $performance_enabled;
            $changed = true;
        }

        if ($changed) {
            $settings['active_modules'] = $active_modules;
            update_option('prorank_settings', $settings);
        }

        $group_states = get_option('prorank_seo_module_groups', []);
        if (!is_array($group_states)) {
            $group_states = [];
        }

        if (!array_key_exists('performance', $group_states) || $this->is_truthy_setting($group_states['performance']) !== $performance_enabled) {
            $group_states['performance'] = $performance_enabled;
            update_option('prorank_seo_module_groups', $group_states);
        }

        update_option('prorank_performance_engine_status', $status, false);
    }

    /**
     * Read performance engine settings from advanced settings with sane defaults.
     *
     * @return array<string,mixed>
     */
    private function get_performance_engine_settings(): array {
        $defaults = [
            'performance_profile' => 'safe',
            'performance_compatibility_mode' => true,
            'performance_auto_rollback' => true,
            'structural_guard_enabled' => true,
        ];

        $settings = get_option('prorank_seo_advanced_settings', []);
        if (!is_array($settings) || $settings === []) {
            $legacy = get_option('prorank_advanced_settings', []);
            if (is_array($legacy) && $legacy !== []) {
                $settings = $legacy;
            }
        }

        return wp_parse_args(is_array($settings) ? $settings : [], $defaults);
    }

    /**
     * Get the active performance profile.
     *
     * @return string
     */
    private function get_performance_profile(): string {
        $profile = sanitize_key((string) ($this->get_performance_engine_settings()['performance_profile'] ?? 'safe'));

        return in_array($profile, ['safe', 'balanced', 'aggressive'], true)
            ? $profile
            : 'safe';
    }

    /**
     * Whether compatibility-aware suppression is enabled.
     *
     * @return bool
     */
    private function is_performance_compatibility_mode_enabled(): bool {
        return $this->is_truthy_setting($this->get_performance_engine_settings()['performance_compatibility_mode'] ?? true);
    }

    /**
     * Whether automatic rollback is enabled.
     *
     * @return bool
     */
    private function is_performance_auto_rollback_enabled(): bool {
        return $this->is_truthy_setting($this->get_performance_engine_settings()['performance_auto_rollback'] ?? true);
    }

    /**
     * Detect the active frontend stack for compatibility decisions.
     *
     * @return array<string,mixed>
     */
    private function get_performance_stack_signature(): array {
        $plugins = $this->get_active_plugin_identifiers();
        $plugin_haystack = strtolower(implode("\n", $plugins));

        $theme = wp_get_theme();
        $theme_haystack = strtolower(implode(' ', array_filter([
            (string) $theme->get('Name'),
            (string) $theme->get_template(),
            (string) $theme->get_stylesheet(),
        ])));

        $builder = '';
        if (strpos($plugin_haystack, 'thrive-visual-editor') !== false || strpos($theme_haystack, 'thrive') !== false) {
            $builder = 'thrive';
        } elseif (strpos($plugin_haystack, 'elementor') !== false || strpos($theme_haystack, 'elementor') !== false) {
            $builder = 'elementor';
        } elseif (strpos($plugin_haystack, 'et-core') !== false || strpos($theme_haystack, 'divi') !== false) {
            $builder = 'divi';
        } elseif (strpos($plugin_haystack, 'beaver-builder-lite-version') !== false || strpos($theme_haystack, 'beaver') !== false) {
            $builder = 'beaver';
        }

        $host_cache = '';
        if (strpos($plugin_haystack, 'sg-cachepress') !== false) {
            $host_cache = 'siteground';
        } elseif (strpos($plugin_haystack, 'litespeed-cache') !== false) {
            $host_cache = 'litespeed';
        } elseif (strpos($plugin_haystack, 'cloudflare') !== false) {
            $host_cache = 'cloudflare';
        }

        $cache_plugin = '';
        foreach ([
            'wp-rocket/wp-rocket.php' => 'wp-rocket',
            'autoptimize/autoptimize.php' => 'autoptimize',
            'w3-total-cache/w3-total-cache.php' => 'w3-total-cache',
            'wp-fastest-cache/wpFastestCache.php' => 'wp-fastest-cache',
            'hummingbird-performance/wp-hummingbird.php' => 'hummingbird',
        ] as $needle => $label) {
            if (strpos($plugin_haystack, strtolower($needle)) !== false) {
                $cache_plugin = $label;
                break;
            }
        }

        return [
            'builder' => $builder,
            'host_cache' => $host_cache,
            'cache_plugin' => $cache_plugin,
            'woocommerce' => strpos($plugin_haystack, 'woocommerce/woocommerce.php') !== false,
            'contact_forms' => strpos($plugin_haystack, 'wpforms-lite/wpforms.php') !== false
                || strpos($plugin_haystack, 'contact-form-7/wp-contact-form-7.php') !== false
                || strpos($plugin_haystack, 'gravityforms/gravityforms.php') !== false,
            'theme' => $theme_haystack,
        ];
    }

    /**
     * Get active plugin file identifiers.
     *
     * @return array<int,string>
     */
    private function get_active_plugin_identifiers(): array {
        $plugins = get_option('active_plugins', []);
        if (!is_array($plugins)) {
            $plugins = [];
        }

        if (is_multisite()) {
            $sitewide = get_site_option('active_sitewide_plugins', []);
            if (is_array($sitewide)) {
                $plugins = array_merge($plugins, array_keys($sitewide));
            }
        }

        return array_values(array_unique(array_map('strval', $plugins)));
    }

    /**
     * Return modules suppressed by profile/stack compatibility.
     *
     * @param array<string,mixed> $stack Stack signature.
     * @return array<int,string>
     */
    private function get_profile_compatibility_suppressions(array $stack): array {
        $profile = $this->get_performance_profile();
        $suppressed = [];
        $has_builder = !empty($stack['builder']);
        $has_external_cache = !empty($stack['host_cache']) || !empty($stack['cache_plugin']);
        $tracker_delay_only = $this->is_tracker_delay_only_js_mode();

        if ($profile === 'safe') {
            if ($has_builder) {
                // Always suppress high-risk CSS operations on builder sites.
                $suppressed = array_merge($suppressed, ['css_opt', 'critical_css', 'unused_css']);

                // js_opt activates Buffer::init() and HTML-level script
                // rewriting, so it stays suppressed on builder sites.
                // Lightweight defer is handled separately by the
                // init_js_defer_fallback() path which only hooks
                // script_loader_tag without any output buffer mutation.
                // Builder safety wins even for tracker-delay-only mode.
                $suppressed[] = 'js_opt';
            }

            if ($has_external_cache) {
                $suppressed = array_merge($suppressed, ['cache', 'modern_cache', 'cache_preload']);
            }
        } elseif ($profile === 'balanced') {
            if ($has_builder) {
                $suppressed[] = 'unused_css';

                // Balanced allows js_opt (defer + delay) on builder sites.
                // That is the whole point of balanced vs safe.

                if (in_array((string) ($stack['builder'] ?? ''), ['thrive', 'elementor', 'divi'], true)) {
                    $suppressed = array_merge($suppressed, ['css_opt', 'critical_css']);
                }
            }

            if ($has_external_cache) {
                $suppressed = array_merge($suppressed, ['cache', 'modern_cache', 'cache_preload']);
            }
        }

        if (($profile === 'safe' || $profile === 'balanced') && !empty($stack['woocommerce']) && !$tracker_delay_only) {
            $suppressed[] = 'js_opt';
        }

        return array_values(array_unique($suppressed));
    }

    /**
     * Whether JS optimization is only being used for tracker interaction delay.
     *
     * In this mode we should not treat the site as having broad JS rewriting
     * enabled, so compatibility suppression can stay strict without blocking
     * the low-risk tracker-only path.
     *
     * @return bool
     */
    private function is_tracker_delay_only_js_mode(): bool {
        $cache_settings = get_option('prorank_cache_settings', []);
        if (!is_array($cache_settings)) {
            $cache_settings = [];
        }

        $asset_settings = get_option('prorank_asset_optimization_settings', []);
        if (!is_array($asset_settings)) {
            $asset_settings = [];
        }

        return $this->is_truthy_setting($cache_settings['tracker_delay_enabled'] ?? null)
            && !$this->is_truthy_setting($cache_settings['js_defer'] ?? null)
            && !$this->is_truthy_setting($cache_settings['js_delay'] ?? null)
            && !$this->is_truthy_setting($asset_settings['js_advanced_enabled'] ?? null);
    }

    /**
     * Return modules currently suppressed by automatic rollback.
     *
     * @return array<int,string>
     */
    private function get_temporarily_rolled_back_modules(): array {
        if (!$this->is_performance_auto_rollback_enabled()) {
            return [];
        }

        $rollbacks = get_option('prorank_performance_rollbacks', []);
        if (!is_array($rollbacks) || $rollbacks === []) {
            return [];
        }

        $now = time();
        $active = [];
        $changed = false;

        foreach ($rollbacks as $slug => $entry) {
            if (!is_array($entry)) {
                unset($rollbacks[$slug]);
                $changed = true;
                continue;
            }

            $disabled_until = (int) ($entry['disabled_until'] ?? 0);
            $last = (int) ($entry['last'] ?? 0);

            if ($disabled_until > $now) {
                $active[] = (string) $slug;
                continue;
            }

            if ($disabled_until > 0 || ($last > 0 && $last < ($now - DAY_IN_SECONDS))) {
                unset($rollbacks[$slug]);
                $changed = true;
            }
        }

        if ($changed) {
            update_option('prorank_performance_rollbacks', $rollbacks, false);
        }

        return array_values(array_unique(array_filter($active)));
    }

    /**
     * Determine modules that should be suppressed for the current environment.
     *
     * @return array<int,string>
     */
    private function get_suppressed_performance_modules(): array {
        $suppressed = [];

        try {
            $detector = new \ProRank\SEO\Core\Compatibility\CompetitorDetector();
            if ( $detector->should_enable_performance_safe_mode() ) {
                $suppressed = array_merge( $suppressed, $detector->get_performance_safe_mode_suppressions() );
            }
        } catch ( \Throwable $e ) {
            // Ignore conflict detector failures and fall back to profile-based suppression.
        }

        if ($this->is_performance_compatibility_mode_enabled()) {
            $suppressed = array_merge($suppressed, $this->get_profile_compatibility_suppressions($this->get_performance_stack_signature()));
        }

        $suppressed = array_merge($suppressed, $this->get_temporarily_rolled_back_modules());

        return array_values(array_unique(array_filter($suppressed)));
    }

    /**
     * Apply profile- and rollback-based suppression to module flags.
     *
     * @param array<string,bool> $flags
     * @return array<string,bool>
     */
    private function apply_performance_module_suppression(array $flags): array {
        // Allow explicit per-module overrides to bypass profile suppression.
        // Users who deliberately enable a module on a builder site accept the
        // risk; the profile should not silently undo their choice.
        $overrides = get_option('prorank_performance_module_overrides', []);
        if (!is_array($overrides)) {
            $overrides = [];
        }

        foreach ($this->get_suppressed_performance_modules() as $module_slug) {
            // Skip suppression if the user has an explicit override for this module.
            if (!empty($overrides[$module_slug])) {
                continue;
            }
            if (array_key_exists($module_slug, $flags)) {
                $flags[$module_slug] = false;
            }
        }

        return $flags;
    }

    /**
     * Whether the performance group has been explicitly switched off.
     *
     * @return bool
     */
    private function is_performance_group_explicitly_disabled(): bool {
        $group_states = get_option('prorank_seo_module_groups', []);
        if (is_array($group_states) && array_key_exists('performance', $group_states)) {
            return !$this->is_truthy_setting($group_states['performance']);
        }

        $settings = get_option('prorank_settings', []);
        if (!is_array($settings)) {
            $decoded = json_decode((string) $settings, true);
            $settings = is_array($decoded) ? $decoded : [];
        }

        $active_modules = isset($settings['active_modules']) && is_array($settings['active_modules'])
            ? $settings['active_modules']
            : [];

        return array_key_exists('performance', $active_modules)
            && !$this->is_truthy_setting($active_modules['performance']);
    }

    /**
     * Keep an explicitly disabled performance group disabled even when stale
     * option values from older releases are still present.
     *
     * @return void
     */
    private function normalize_explicitly_disabled_performance_group(): void {
        if ($this->has_enabled_performance_source_flags()) {
            $this->hard_disable_performance_runtime();
        }

        $settings = get_option('prorank_settings', []);
        if (!is_array($settings)) {
            $decoded = json_decode((string) $settings, true);
            $settings = is_array($decoded) ? $decoded : [];
        }

        $active_modules = isset($settings['active_modules']) && is_array($settings['active_modules'])
            ? $settings['active_modules']
            : [];

        $changed = false;
        foreach ($this->get_performance_child_module_slugs(array_keys($active_modules)) as $module_slug) {
            if (!array_key_exists($module_slug, $active_modules) || $this->is_truthy_setting($active_modules[$module_slug])) {
                $active_modules[$module_slug] = false;
                $changed = true;
            }
        }

        if (!array_key_exists('performance', $active_modules) || $this->is_truthy_setting($active_modules['performance'])) {
            $active_modules['performance'] = false;
            $changed = true;
        }

        if ($changed) {
            $settings['active_modules'] = $active_modules;
            update_option('prorank_settings', $settings);
        }

        $group_states = get_option('prorank_seo_module_groups', []);
        if (!is_array($group_states)) {
            $group_states = [];
        }

        if (!array_key_exists('performance', $group_states) || $this->is_truthy_setting($group_states['performance'])) {
            $group_states['performance'] = false;
            update_option('prorank_seo_module_groups', $group_states);
        }
    }

    /**
     * Build the expected performance child module flags from saved settings.
     *
     * @return array<string,bool>
     */
    private function get_expected_performance_module_flags_from_settings(): array {
        $cache_settings = get_option('prorank_cache_settings', []);
        if (!is_array($cache_settings)) {
            $cache_settings = [];
        }

        $asset_settings = get_option('prorank_asset_optimization_settings', []);
        if (!is_array($asset_settings)) {
            $asset_settings = [];
        }

        $image_settings = get_option('prorank_image_optimization_settings', []);
        if (!is_array($image_settings)) {
            $image_settings = [];
        }

        $page_cache_enabled = $this->is_truthy_setting(get_option('prorank_page_cache_enabled', false));
        $cache_enabled = $page_cache_enabled
            || $this->is_truthy_setting($cache_settings['cache_enabled'] ?? null)
            || $this->is_truthy_setting($cache_settings['enabled'] ?? null);

        return [
            'cache' => $cache_enabled,
            'modern_cache' => $cache_enabled,
            'browser_cache' => $this->is_truthy_setting($cache_settings['browser_cache_enabled'] ?? null),
            'cache_preload' => $this->is_truthy_setting($cache_settings['preload_enabled'] ?? null),
            'resource_hints' => $this->is_truthy_setting($cache_settings['dns_prefetch_enabled'] ?? null)
                || $this->is_truthy_setting($cache_settings['font_preload_enabled'] ?? null)
                || $this->is_truthy_setting($cache_settings['link_preload_enabled'] ?? null),
            'speculation_rules' => $this->is_truthy_setting($cache_settings['prefetch_enabled'] ?? null)
                || $this->is_truthy_setting($cache_settings['prerender_enabled'] ?? null),
            // css_async alone uses the lightweight style_loader_tag fallback.
            // Only boot the full css_opt module for higher-impact CSS work.
            'css_opt' => $this->is_truthy_setting($cache_settings['css_minify'] ?? null)
                || $this->is_truthy_setting($cache_settings['css_combine'] ?? null),
            'js_opt' => $this->is_truthy_setting($cache_settings['js_defer'] ?? null)
                || $this->is_truthy_setting($cache_settings['js_delay'] ?? null)
                || $this->is_truthy_setting($cache_settings['tracker_delay_enabled'] ?? null),
            'image_optimization' => $this->is_truthy_setting($image_settings['webp_enabled'] ?? null)
                || $this->is_truthy_setting($image_settings['avif_enabled'] ?? null)
                || $this->is_truthy_setting($image_settings['jxl_enabled'] ?? null)
                || $this->is_truthy_setting($image_settings['jpegli_enabled'] ?? null)
                || $this->is_truthy_setting($image_settings['optimize_on_upload'] ?? null)
                || $this->is_truthy_setting($image_settings['smart_compression'] ?? null),
            'lazyload' => $this->is_lazyload_enabled_in_image_settings($image_settings),
            'js_minify' => $this->is_truthy_setting($asset_settings['js_minify_enabled'] ?? null),
            'html_minify' => $this->is_truthy_setting($asset_settings['minify_html'] ?? null)
                || $this->is_truthy_setting($asset_settings['remove_html_comments'] ?? null),
            'font_optimization' => $this->is_truthy_setting($asset_settings['host_google_fonts_locally'] ?? null)
                || $this->is_truthy_setting($asset_settings['font_display_swap'] ?? null),
            'inp_optimizer' => $this->is_truthy_setting($asset_settings['inp_optimizer_enabled'] ?? null),
            'lcp_beacon' => $this->is_truthy_setting($asset_settings['lcp_optimizer_enabled'] ?? null)
                || $this->is_truthy_setting($asset_settings['lcp_fetchpriority_enabled'] ?? null)
                || $this->is_truthy_setting($asset_settings['lcp_preload_lcp'] ?? null)
                || $this->is_truthy_setting($asset_settings['lcp_optimize_first_image'] ?? null)
                || $this->is_truthy_setting($asset_settings['lcp_remove_above_fold_lazy'] ?? null),
            'lcp_optimizer' => $this->is_truthy_setting($asset_settings['lcp_optimizer_enabled'] ?? null)
                || $this->is_truthy_setting($asset_settings['lcp_fetchpriority_enabled'] ?? null)
                || $this->is_truthy_setting($asset_settings['lcp_preload_lcp'] ?? null)
                || $this->is_truthy_setting($asset_settings['lcp_optimize_first_image'] ?? null)
                || $this->is_truthy_setting($asset_settings['lcp_remove_above_fold_lazy'] ?? null),
        ];
    }

    /**
     * Whether any underlying performance source setting is still enabled.
     *
     * @return bool
     */
    private function has_enabled_performance_source_flags(): bool {
        $cache_settings = get_option('prorank_cache_settings', []);
        if (!is_array($cache_settings)) {
            $cache_settings = [];
        }

        foreach ([
            'enabled',
            'cache_enabled',
            'preload_enabled',
            'link_preload_enabled',
            'prefetch_enabled',
            'prerender_enabled',
            'browser_cache_enabled',
            'js_defer',
            'js_delay',
            'js_delay_jquery',
            'js_delay_elementor_modules',
            'tracker_delay_enabled',
            'css_minify',
            'css_combine',
            'css_async',
            'edge_cache_enabled',
            'preload_ai_enabled',
            'early_hints_enabled',
            'override_core',
            'link_fallback',
            'dns_prefetch_enabled',
            'font_preload_enabled',
            'htaccess_serving',
        ] as $key) {
            if ($this->is_truthy_setting($cache_settings[$key] ?? null)) {
                return true;
            }
        }

        if (trim((string) ($cache_settings['dns_prefetch_hosts'] ?? '')) !== '') {
            return true;
        }

        $asset_settings = get_option('prorank_asset_optimization_settings', []);
        if (!is_array($asset_settings)) {
            $asset_settings = [];
        }

        foreach ([
            'minify_html',
            'remove_html_comments',
            'css_minify',
            'css_combine',
            'js_advanced_enabled',
            'js_minify_enabled',
            'js_minify_inline',
            'host_google_fonts_locally',
            'font_display_swap',
            'inp_optimizer_enabled',
            'lcp_optimizer_enabled',
            'lcp_fetchpriority_enabled',
            'lcp_preload_lcp',
            'lcp_optimize_first_image',
            'lcp_remove_above_fold_lazy',
        ] as $key) {
            if ($this->is_truthy_setting($asset_settings[$key] ?? null)) {
                return true;
            }
        }

        $image_settings = get_option('prorank_image_optimization_settings', []);
        if (!is_array($image_settings)) {
            $image_settings = [];
        }

        foreach ([
            'optimize_on_upload',
            'lazyload_images',
            'lazyload_iframes',
            'lazyload_videos',
            'lazyload_enabled',
            'webp_enabled',
            'avif_enabled',
            'jxl_enabled',
            'jpegli_enabled',
            'smart_compression',
            'background_mode',
            'png_to_jpeg',
            'cmyk_to_rgb',
            'remove_exif',
            'optimize_pdf',
            'generate_retina',
            'automatic_optimization',
        ] as $key) {
            if ($this->is_truthy_setting($image_settings[$key] ?? null)) {
                return true;
            }
        }

        return $this->is_truthy_setting(get_option('prorank_page_cache_enabled', false));
    }

    /**
     * Determine performance child module slugs from the registered modules list.
     *
     * @param array<int,string> $fallback_slugs Existing active module keys.
     * @return array<int,string>
     */
    private function get_performance_child_module_slugs(array $fallback_slugs = []): array {
        try {
            $mm = $this->modules();
            if ($mm) {
                $slugs = [];
                foreach ($mm->get_modules() as $slug => $module) {
                    if (
                        is_object($module)
                        && method_exists($module, 'get_parent_slug')
                        && $module->get_parent_slug() === 'performance'
                    ) {
                        $slugs[] = (string) $slug;

                        if (method_exists($module, 'get_slug')) {
                            $module_slug = (string) $module->get_slug();
                            if ($module_slug !== '') {
                                $slugs[] = $module_slug;
                            }
                        }
                    }
                }

                $slugs = array_values(array_unique(array_filter($slugs, static function ($slug) {
                    return $slug !== '' && $slug !== 'performance';
                })));

                if (!empty($slugs)) {
                    return $slugs;
                }
            }
        } catch (\Throwable $e) {
            // Fall back below when module registry is unavailable.
        }

        return array_values(array_unique(array_filter($fallback_slugs, static function ($slug) {
            return $slug !== '' && $slug !== 'performance';
        })));
    }

    /**
     * Force-disable performance runtime toggles and purge cache artifacts.
     *
     * @return void
     */
    private function hard_disable_performance_runtime(): void {
        $cache_settings = get_option('prorank_cache_settings', []);
        if (!is_array($cache_settings)) {
            $cache_settings = [];
        }
        foreach ([
            'enabled',
            'cache_enabled',
            'preload_enabled',
            'link_preload_enabled',
            'prefetch_enabled',
            'prerender_enabled',
            'browser_cache_enabled',
            'js_defer',
            'js_delay',
            'js_delay_jquery',
            'js_delay_elementor_modules',
            'tracker_delay_enabled',
            'css_minify',
            'css_combine',
            'css_async',
            'edge_cache_enabled',
            'preload_ai_enabled',
            'early_hints_enabled',
            'override_core',
            'link_fallback',
            'dns_prefetch_enabled',
            'dns_prefetch_hosts',
            'font_preload_enabled',
            'htaccess_serving',
        ] as $key) {
            $cache_settings[$key] = ($key === 'dns_prefetch_hosts') ? '' : false;
        }
        update_option('prorank_cache_settings', $cache_settings);

        $asset_settings = get_option('prorank_asset_optimization_settings', []);
        if (!is_array($asset_settings)) {
            $asset_settings = [];
        }
        foreach ([
            'minify_html',
            'remove_html_comments',
            'css_minify',
            'css_combine',
            'js_minify_enabled',
            'js_minify_inline',
            'host_google_fonts_locally',
            'font_display_swap',
            'js_advanced_enabled',
            'inp_optimizer_enabled',
            'lcp_optimizer_enabled',
            'lcp_fetchpriority_enabled',
            'lcp_preload_lcp',
            'lcp_optimize_first_image',
            'lcp_remove_above_fold_lazy',
            'css_async',
            'js_defer',
            'lazyload_enabled',
            'automatic_optimization',
        ] as $key) {
            $asset_settings[$key] = false;
        }
        update_option('prorank_asset_optimization_settings', $asset_settings);

        $image_settings = get_option('prorank_image_optimization_settings', []);
        if (!is_array($image_settings)) {
            $image_settings = [];
        }
        foreach ([
            'optimize_on_upload',
            'lazyload_images',
            'lazyload_iframes',
            'lazyload_videos',
            'webp_enabled',
            'avif_enabled',
            'jxl_enabled',
            'jpegli_enabled',
            'smart_compression',
            'background_mode',
            'png_to_jpeg',
            'cmyk_to_rgb',
            'remove_exif',
            'optimize_pdf',
            'generate_retina',
            'lazyload_enabled',
            'automatic_optimization',
        ] as $key) {
            $image_settings[$key] = false;
        }
        update_option('prorank_image_optimization_settings', $image_settings);

        update_option('prorank_page_cache_enabled', false);

        // Remove drop-in to guarantee no pre-WP cache interception remains active.
        if (class_exists('\ProRank\SEO\Cache\AdvancedCache')) {
            \ProRank\SEO\Cache\AdvancedCache::uninstall();
        }

        // Purge existing cache files/htaccess rules when module instance is available.
        $modern_cache = $this->modules()->get_module('modern_cache');
        if (is_object($modern_cache)) {
            if (method_exists($modern_cache, 'remove_htaccess_rules')) {
                $modern_cache->remove_htaccess_rules();
            }
            if (method_exists($modern_cache, 'clear_all_cache')) {
                $modern_cache->clear_all_cache();
            }
        }
    }

    /**
     * Initialize REST API
     */
    public function init_rest_api(): void {
        // Security validation checkpoint - REST API
        if (class_exists('\ProRank\SEO\Core\SecurityValidator')) {
            \ProRank\SEO\Core\SecurityValidator::validate('cp_rest');
        }
        
        // Add CORS headers for REST API requests
        add_filter('rest_pre_serve_request', function($served, $result, $request, $server) {
            // Only add CORS headers for our endpoints
            $route = $request->get_route();
            if (strpos($route, '/prorank-seo/') === 0) {
                // Get the origin
                $origin = get_http_origin();
                
                // If origin is from the same host (possibly different port), allow it
                $home_host = wp_parse_url(home_url(), PHP_URL_HOST);
                $origin_host = wp_parse_url($origin, PHP_URL_HOST);
                
                if ($origin && $origin_host === $home_host) {
                    header('Access-Control-Allow-Origin: ' . $origin);
                    header('Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS');
                    header('Access-Control-Allow-Headers: Content-Type, Authorization, X-WP-Nonce');
                    header('Access-Control-Allow-Credentials: true');
                }
            }
            
            return $served;
        }, 10, 4);
        
        // Initialize REST API controllers with correct dependencies
        $controllers = [];
        $premium_active = defined('PRORANK_PREMIUM_VERSION');
        
        // Core controllers
        if (class_exists('ProRank\SEO\Core\RestApi\DashboardController')) {
            $controllers[] = new DashboardController($this->modules(), $this->license());
        }
        
        if (class_exists('ProRank\SEO\Core\RestApi\ModulesController')) {
            $modules_controller = new ModulesController();
            $modules_controller->set_module_manager($this->modules());
            $controllers[] = $modules_controller;
        }

        if (class_exists('ProRank\SEO\Core\RestApi\WizardStateController')) {
            $controllers[] = new WizardStateController();
        }
        
        if (class_exists('ProRank\SEO\Core\RestApi\NotificationsFeedController')) {
            $controllers[] = new NotificationsFeedController(
                new NotificationsFeed($this->license(), $this->modules())
            );
        }
        
        if (class_exists('ProRank\SEO\Core\RestApi\SettingsController')) {
            $controllers[] = new SettingsController($this->settings());
        }
        
        if (class_exists('ProRank\SEO\Core\RestApi\CacheController')) {
            $controllers[] = new CacheController();
        }
        
        if (class_exists('ProRank\SEO\Core\RestApi\AdvancedSettingsController')) {
            $controllers[] = new AdvancedSettingsController();
        }
        
        if (class_exists('ProRank\SEO\Core\RestApi\DataController')) {
            $controllers[] = new DataController();
        }
        
        if (class_exists('ProRank\SEO\Core\RestApi\FeedbackController')) {
            $controllers[] = new FeedbackController();
        }
        
        if (class_exists('ProRank\SEO\Core\RestApi\SystemController')) {
            $controllers[] = new SystemController();
        }
        
        if (class_exists('ProRank\SEO\Core\RestApi\EnhancedAuditController')) {
            try {
                $controller = new EnhancedAuditController();
                $controllers[] = $controller;
            } catch (\Throwable $e) {
                prorank_log('[ProRank SEO] EnhancedAuditController instantiation failed: ' . $e->getMessage());
                if (defined('WP_DEBUG') && WP_DEBUG) {
                    prorank_log($e->getTraceAsString());
                }
            }
        }

        if (class_exists('ProRank\SEO\Core\RestApi\AuditSettingsController')) {
            $controllers[] = new \ProRank\SEO\Core\RestApi\AuditSettingsController();
        }

        if (class_exists('ProRank\SEO\Core\RestApi\AuditExportController')) {
            $controllers[] = new \ProRank\SEO\Core\RestApi\AuditExportController();
        }

        if (class_exists('ProRank\SEO\Core\RestApi\CwvDataController')) {
            $controllers[] = new CwvDataController();
        }

        // SEO Feature controllers
        if (class_exists('ProRank\SEO\Core\RestApi\MetaController')) {
            $controllers[] = new MetaController();
        }
        
        if (class_exists('ProRank\SEO\Core\RestApi\OnboardingController')) {
            $controllers[] = new OnboardingController();
        }
        
        // Sitemap controllers
        if (class_exists('ProRank\SEO\Core\RestApi\SitemapsController')) {
            $controllers[] = new SitemapsController();
        }
        
        if (class_exists('ProRank\SEO\Core\RestApi\HtmlSitemapController')) {
            $controllers[] = new HtmlSitemapController();
        }
        
        // Content controllers
        if (class_exists('ProRank\SEO\Core\RestApi\ReadabilityController')) {
            $controllers[] = new ReadabilityController();
        }

        if (class_exists('ProRank\SEO\Core\RestApi\HeadlineAnalyzerController')) {
            $controllers[] = new HeadlineAnalyzerController();
        }

        // Some controllers are shipped in the Free plugin so UI components can call
        // their routes without getting 404s. Access control is enforced inside the
        // controllers (Premium plugin + license tier).
        // Skip if an internal-linking competitor (e.g. Link Whisper) is active.
        $il_detector = new \ProRank\SEO\Core\Compatibility\CompetitorDetector();
        if (!$il_detector->should_enable_internal_linking_safe_mode()) {
            if (class_exists('ProRank\SEO\Core\RestApi\LinkingController')) {
                $controllers[] = new LinkingController();
            }
        }
        
        // Technical SEO controllers
        if (class_exists('ProRank\SEO\Core\RestApi\RedirectsController')) {
            $controllers[] = new RedirectsController();
        }

        if (class_exists('ProRank\SEO\Core\RestApi\RedirectSettingsController')) {
            $controllers[] = new RedirectSettingsController();
        }

        if (class_exists('ProRank\SEO\Core\RestApi\RobotsIndexingController')) {
            $controllers[] = new RobotsIndexingController();
        }
        
        if (class_exists('ProRank\SEO\Core\RestApi\HeadCleanupController')) {
            $controllers[] = new HeadCleanupController();
        }
        
        if (class_exists('ProRank\SEO\Core\RestApi\UrlCleanupController')) {
            $controllers[] = new UrlCleanupController();
        }
        
        // Import/Export controller
        if (class_exists('ProRank\SEO\Core\RestApi\ImportExportController')) {
            $controllers[] = new ImportExportController($this->modules(), $this->license());
        }
        
        // Command Palette controller
        if (class_exists('ProRank\SEO\Core\RestApi\CommandPaletteController')) {
            $controllers[] = new CommandPaletteController($this->services->get('menus'));
        }
        
        // User Preferences controller
        if (class_exists('ProRank\SEO\Core\RestApi\UserPreferencesController')) {
            $controllers[] = new UserPreferencesController();
        }
        
        // Presets controller
        if (class_exists('ProRank\SEO\Core\RestApi\PresetsController')) {
            $controllers[] = new PresetsController($this->services->get('presets'));
        }
        
        // Settings Snapshot controller
        if (class_exists('ProRank\SEO\Core\RestApi\SettingsSnapshotController')) {
            $controllers[] = new SettingsSnapshotController($this->services->get('snapshots'));
        }
        
        // Cache controller
        if (class_exists('ProRank\SEO\Core\RestApi\CacheController')) {
            $controllers[] = new \ProRank\SEO\Core\RestApi\CacheController();
        }

        if (class_exists('ProRank\SEO\Core\RestApi\ImageOptimizationSettingsController')) {
            $controllers[] = new ImageOptimizationSettingsController();
        }
        
        // Image Optimization controller (available in free and premium).
        if (class_exists('ProRank\SEO\Core\RestApi\ImageOptimizationController')) {
            $controllers[] = new ImageOptimizationController();
        }

        // Images controller (available in free and premium).
        if (class_exists('ProRank\SEO\Core\RestApi\ImagesController')) {
            $controllers[] = new ImagesController();
        }
        
        // Font Optimization controller
        if (class_exists('ProRank\SEO\Core\RestApi\FontOptimizationController')) {
            $controllers[] = new FontOptimizationController();
        }
        
        // Database Optimization controller
        if (class_exists('ProRank\SEO\Core\RestApi\DatabaseOptimizationController')) {
            $controllers[] = new DatabaseOptimizationController();
        }
        
        // CSS Optimization controller
        if (class_exists('ProRank\SEO\Core\RestApi\CssOptimizationController')) {
            $controllers[] = new CssOptimizationController();
        }

        // Performance Settings controller (Critical CSS, Unused CSS, etc.)
        if (class_exists('ProRank\SEO\Core\RestApi\PerformanceSettingsController')) {
            $controllers[] = new PerformanceSettingsController();
        }

        if (class_exists('ProRank\SEO\Core\RestApi\CdnController')) {
            $controllers[] = new CdnController();
        }
        if (class_exists('ProRank\SEO\Core\RestApi\CdnSetupController')) {
            $controllers[] = new CdnSetupController();
        }

        // Version Management controller
        if (class_exists('ProRank\SEO\Core\RestApi\VersionController')) {
            $controllers[] = new VersionController();
        }
        
        // Browser Cache endpoint
        if (class_exists('ProRank\SEO\Api\Endpoints\BrowserCacheEndpoint')) {
            $controllers[] = new \ProRank\SEO\Api\Endpoints\BrowserCacheEndpoint();
        }
        
        // Register all routes
        foreach ( $controllers as $controller ) {
            if (method_exists($controller, 'register_routes')) {
                $controller->register_routes();
            }
        }
        
    }

    /**
     * Stream a protected plugin backup to an authenticated admin.
     *
     * @return void
     */
    public function handle_backup_download(): void {
        if (!current_user_can('manage_options')) {
            wp_die(esc_html__('You do not have permission to download plugin backups.', 'prorank-seo'));
        }

        $backup_name = isset($_GET['backup']) ? sanitize_text_field(wp_unslash($_GET['backup'])) : '';

        check_admin_referer('prorank_download_backup_' . $backup_name);

        $version_manager = new \ProRank\SEO\Core\VersionManager();
        $backup_file     = $version_manager->resolve_backup_file($backup_name);

        if (is_wp_error($backup_file)) {
            wp_die(esc_html($backup_file->get_error_message()));
        }

        while (ob_get_level() > 0) {
            ob_end_clean();
        }

        nocache_headers();
        header('Content-Description: File Transfer');
        header('Content-Type: application/zip');
        header('Content-Disposition: attachment; filename="' . $backup_file['name'] . '"');
        header('Content-Transfer-Encoding: binary');
        header('Content-Length: ' . (string) $backup_file['size']);
        header('X-Content-Type-Options: nosniff');

        readfile($backup_file['path']);
        exit;
    }
    
    /**
     * Check license status and show admin notice if invalid
     *
     * @return void
     */
    private function check_license_status(): void {
        return;
    }
    
    /**
     * Run license check
     *
     * @return void
     */
    public function run_license_check(): void {
        return;
    }
    
    /**
     * Run license refresh (Action Scheduler task)
     *
     * @return void
     */
    public function run_license_refresh(): void {
        return;
    }
}
