<?php

declare(strict_types=1);

namespace ProRank\SEO\Core\Compatibility;

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

/**
 * Central registry for builder/framework compatibility exclusions.
 *
 * Keep these patterns in one place so runtime fallback logic and CSS combine
 * protection stay aligned as support expands.
 */
final class PerformanceCompatibilityRegistry {
    /**
     * Delay/defer protection patterns that should remain synchronous.
     *
     * This centralizes the high-signal compatibility markers adapted from the
     * curated quick-exclusion datasets used by mature optimizers. The goal is
     * to avoid delaying builder bootstraps, consent runtimes, and high-risk
     * form/widget scripts even when aggressive JS optimization is enabled.
     *
     * @return array<int, string>
     */
    public static function get_delay_js_protected_patterns(): array {
        $patterns = self::get_default_delay_js_protected_patterns();

        foreach ( self::get_delay_js_quick_exclusion_map() as $plugin_file => $plugin_patterns ) {
            if ( ! self::is_plugin_active( $plugin_file ) ) {
                continue;
            }

            $patterns = array_merge( $patterns, $plugin_patterns );
        }

        foreach ( self::get_delay_js_theme_exclusion_map() as $theme_slug => $theme_patterns ) {
            if ( ! self::is_theme_active( $theme_slug ) ) {
                continue;
            }

            $patterns = array_merge( $patterns, $theme_patterns );
        }

        return self::normalize_patterns(
            apply_filters( 'prorank_delay_js_protected_patterns', $patterns )
        );
    }

    /**
     * Defer protection patterns that should remain synchronous.
     *
     * Delay and defer are different risk surfaces. This narrower list follows
     * the safer competitor model: protect high-risk widget/bootstrap URLs from
     * generic defer, while allowing broader tracker governance to stay separate.
     *
     * @return array<int, string>
     */
    public static function get_defer_js_protected_patterns(): array {
        $patterns = self::get_default_defer_js_protected_patterns();

        foreach ( self::get_delay_js_quick_exclusion_map() as $plugin_file => $plugin_patterns ) {
            if ( ! self::is_plugin_active( $plugin_file ) ) {
                continue;
            }

            $patterns = array_merge( $patterns, $plugin_patterns );
        }

        foreach ( self::get_delay_js_theme_exclusion_map() as $theme_slug => $theme_patterns ) {
            if ( ! self::is_theme_active( $theme_slug ) ) {
                continue;
            }

            $patterns = array_merge( $patterns, $theme_patterns );
        }

        return self::normalize_patterns(
            apply_filters( 'prorank_defer_js_protected_patterns', $patterns )
        );
    }

    /**
     * Third-party Google service markers used by tracker governance.
     *
     * @return array<int, string>
     */
    public static function get_google_service_patterns(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_google_service_patterns',
                [
                    'google_gtagjs',
                    'googlesitekit',
                    'googletagmanager.com/gtag/js',
                    'googletagmanager.com/gtm.js',
                    'googletagmanager.com',
                    'google-analytics.com',
                    'googlesyndication.com',
                    'doubleclick.net',
                    'window.datalayer',
                    'gtm.start',
                    'gtag(',
                ]
            )
        );
    }

    /**
     * Inline Google measurement/bootstrap markers that should stay synchronous.
     *
     * Mature optimizers do not interaction-delay GTM/gtag bootstraps blindly.
     * These markers capture the common inline state/config emitted by Site Kit,
     * GTM, and gtag integrations so ProRank can stay conservative when those
     * bootstraps are present.
     *
     * @return array<int, string>
     */
    public static function get_google_service_inline_bootstrap_patterns(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_google_service_inline_bootstrap_patterns',
                [
                    'window.datalayer = window.datalayer || [];function gtag(){datalayer.push(arguments);}',
                    'gtag("config"',
                    "gtag('config'",
                    'gtag("js"',
                    "gtag('js'",
                    'gtm.start',
                    'async-hide',
                    'window._googlesitekit',
                    'window._googlesitekitconsentcategorymap',
                    'window._googlesitekitconsents',
                    'window.google_tag_data',
                    'google_tag_data',
                    'window.google_tag_manager',
                    'google_tag_manager',
                    'google_tag_params',
                ]
            )
        );
    }

    /**
     * Review-widget markers used by tracker governance.
     *
     * @return array<int, string>
     */
    public static function get_review_widget_patterns(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_review_widget_patterns',
                [
                    'cdn.trustindex.io/loader.js',
                    'trustindex',
                    'widget.reviews.io',
                    'assets.reviews.io',
                    'widget.reviews.co.uk',
                    'trustbox_settings',
                    'feedbackcompany',
                    'widget.reviewability.com',
                ]
            )
        );
    }

    /**
     * Consent runtime markers used by tracker governance.
     *
     * @return array<int, string>
     */
    public static function get_consent_script_patterns(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_consent_script_patterns',
                [
                    'cdn-cookieyes.com',
                    'cookieyes',
                    'cookie-law-info',
                    'app.termly.io/resource-blocker/',
                    'termly',
                    'consent.cookiebot.com',
                    'consent.cookiebot.com/uc.js',
                    'cookiebot.com',
                    'complianz',
                    'cmplz',
                    'borlabs',
                    'real-cookie-banner',
                    'realcookiebanner',
                    'moove-gdpr',
                    'usercentrics',
                    'usercentrics.eu',
                    'cc.cdn.civiccomputing.com',
                    'tarteaucitron',
                    'cookielaw.org',
                    'onetrust',
                    'cookie-script',
                ]
            )
        );
    }

    /**
     * Identity widget markers used by tracker governance.
     *
     * @return array<int, string>
     */
    public static function get_identity_widget_patterns(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_identity_widget_patterns',
                [
                    'accounts.google.com/gsi/client',
                    'google.accounts.id',
                    'g_id_onload',
                ]
            )
        );
    }

    /**
     * Inline identity-widget bootstrap markers that should stay synchronous.
     *
     * These are usually immediate bootstraps for auth widgets. Delaying the
     * inline initializer while also deferring the external client can break the
     * login button or one-tap surface completely.
     *
     * @return array<int, string>
     */
    public static function get_identity_widget_inline_bootstrap_patterns(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_identity_widget_inline_bootstrap_patterns',
                [
                    'google.accounts.id.initialize',
                    'google.accounts.id.renderbutton',
                    'google.accounts.id.prompt',
                    'google.accounts.oauth2.inittokenclient',
                    'google.accounts.oauth2.initcodeclient',
                    'googlesitekit-sign-in-with-google',
                    'g_id_onload',
                ]
            )
        );
    }

    /**
     * Low-risk tracker markers that are generally safe to push behind interaction.
     *
     * @return array<int, string>
     */
    public static function get_low_risk_tracker_patterns(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_low_risk_tracker_patterns',
                [
                    'static.getclicky.com',
                    'clicky.init',
                    'clicky_site_ids',
                    'clarity.ms/tag',
                    'clarity(',
                    'hotjar.com',
                    'hj(',
                    'cdn.segment.com',
                    'js.hs-analytics.net',
                    'analytics.tiktok.com',
                    'bat.bing.com/bat.js',
                    'snapchat.com/scevent',
                    'snap.licdn.com',
                    'linkedin_insight',
                    'js.adsrvr.org',
                    'mouseflow',
                    'fullstory',
                    'web-2-tel.com/org-sdk',
                    'xg4ken.com/js/v2/ktag.js',
                ]
            )
        );
    }


    /**
     * Builder stylesheet handles that should remain render-blocking.
     *
     * @return array<int, string>
     */
    public static function get_builder_blocking_style_handle_patterns(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_builder_blocking_style_handle_patterns',
                [
                    'elementor-frontend',
                    'elementor-pro-frontend',
                    'elementor-icons',
                    'elementor-post-',
                    'elementor-widget-',
                    'e-animations',
                    'swiper',
                    'e-swiper',
                    'woocommerce-',
                    'astra-theme-css',
                    'astra-addon-css',
                    'astra-menu-animation',
                    'kadence-global',
                    'kadence-header',
                    'kadence-content',
                    'generate-style',
                    'generate-navigation',
                    'generateblocks',
                    'oceanwp-style',
                    'oceanwp-icons',
                    'hello-elementor',
                    'hello-elementor-theme-style',
                    'hello-elementor-header-footer',
                    'hfe-style',
                    'fl-builder',
                    'fl-builder-layout',
                    'bricks-',
                    'bricks-frontend',
                    'breakdance-',
                    'ct-builder',
                    'ct-main-styles',
                    'ct-main-rtl-styles',
                    'ct-admin-frontend-styles',
                    'ct-page-title-styles',
                    'ct-sidebar-styles',
                    'ct-comments-styles',
                    'ct-author-box-styles',
                    'ct-posts-nav-styles',
                    'ct-featured-image-styles',
                    'ct-pagination-styles',
                    'ct-entries-styles',
                    'ct-flexy-styles',
                    'ct-elementor-styles',
                    'ct-elementor-woocommerce-styles',
                    'oxygen',
                    'et-builder',
                    'et-core',
                    'et-divi',
                    'et_pb',
                    'generateblocks',
                    'tve_frontend',
                    'thrive-',
                ]
            )
        );
    }

    /**
     * Builder stylesheet href patterns that should remain render-blocking.
     *
     * @return array<int, string>
     */
    public static function get_builder_blocking_style_href_patterns(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_builder_blocking_style_href_patterns',
                [
                    '/wp-content/uploads/elementor/css/post-',
                    '/wp-content/uploads/elementor/css/global.css',
                    '/wp-content/plugins/elementor/assets/css/frontend',
                    '/wp-content/plugins/elementor-pro/assets/css/frontend',
                    '/wp-content/plugins/elementor/assets/lib/eicons/',
                    '/wp-content/plugins/elementor/assets/lib/swiper/',
                    '/wp-content/plugins/elementor/assets/lib/animations/',
                    '/wp-content/plugins/elementor/assets/css/conditionals/e-swiper',
                    '/wp-content/plugins/elementor/assets/css/widget-',
                    '/wp-content/plugins/elementor-pro/assets/css/widget-',
                    '/wp-content/plugins/woocommerce/assets/css/',
                    '/wp-content/plugins/woocommerce/assets/client/',
                    '/wp-content/plugins/woocommerce/packages/woocommerce-blocks/build/',
                    '/wp-content/plugins/woocommerce/assets/fonts/',
                    '/compatibility/woocommerce/',
                    '/wp-content/themes/astra/assets/css/',
                    '/wp-content/themes/astra/assets/min-css/',
                    '/wp-content/plugins/astra-addon/assets/css/',
                    '/wp-content/themes/kadence/assets/css/',
                    '/wp-content/themes/generatepress/assets/css/',
                    '/wp-content/themes/oceanwp/assets/css/',
                    '/wp-content/themes/hello-elementor/',
                    '/wp-content/themes/blocksy/static/bundle/',
                    '/wp-content/uploads/oxygen/css/',
                    '/wp-content/uploads/bb-plugin/cache/',
                    '/wp-content/plugins/bb-plugin/css/',
                    '/wp-content/plugins/bb-plugin/fonts/',
                    '/wp-content/uploads/bricks/css/post-',
                    '/wp-content/themes/bricks/assets/css/',
                    '/wp-content/plugins/breakdance/plugin/',
                    '/wp-content/uploads/generateblocks/',
                    '/et-cache/',
                    '/thrive-visual-editor/',
                    '/astra-local-fonts/',
                ]
            )
        );
    }

    /**
     * Builder stylesheet handles that should not be moved into combined CSS.
     *
     * @return array<int, string>
     */
    public static function get_builder_combine_protected_handles(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_builder_combine_protected_handles',
                [
                    'elementor-frontend',
                    'elementor-pro-frontend',
                    'elementor-icons',
                    'elementor-icons-shared-0',
                    'elementor-icons-fa-solid',
                    'elementor-icons-fa-regular',
                    'elementor-icons-fa-brands',
                    'elementor-widget-',
                    'e-animations',
                    'swiper',
                    'e-swiper',
                    'woocommerce-',
                    'astra-theme-css',
                    'astra-addon-css',
                    'astra-menu-animation',
                    'kadence-global',
                    'kadence-header',
                    'kadence-content',
                    'generate-style',
                    'oceanwp-style',
                    'hello-elementor',
                    'hello-elementor-theme-style',
                    'hello-elementor-header-footer',
                    'fl-builder-layout',
                    'bricks-frontend',
                    'breakdance-',
                    'ct-builder',
                    'ct-main-styles',
                    'ct-page-title-styles',
                    'ct-pagination-styles',
                    'ct-entries-styles',
                    'ct-flexy-styles',
                    'oxygen',
                    'generateblocks',
                ]
            )
        );
    }

    /**
     * Builder stylesheet href patterns that should not be moved into combined CSS.
     *
     * @return array<int, string>
     */
    public static function get_builder_combine_protected_href_patterns(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_builder_combine_protected_href_patterns',
                [
                    '/wp-content/plugins/elementor/assets/css/frontend',
                    '/wp-content/plugins/elementor-pro/assets/css/frontend',
                    '/wp-content/plugins/elementor/assets/lib/eicons/css/elementor-icons',
                    '/wp-content/plugins/elementor/assets/lib/eicons/',
                    '/wp-content/plugins/elementor/assets/lib/swiper/',
                    '/wp-content/plugins/elementor/assets/lib/animations/',
                    '/wp-content/plugins/elementor/assets/css/conditionals/e-swiper',
                    '/wp-content/plugins/elementor/assets/css/widget-',
                    '/wp-content/plugins/elementor-pro/assets/css/widget-',
                    '/wp-content/uploads/elementor/css/post-',
                    '/wp-content/uploads/elementor/css/global.css',
                    '/wp-content/plugins/woocommerce/assets/css/',
                    '/wp-content/plugins/woocommerce/assets/client/',
                    '/wp-content/plugins/woocommerce/packages/woocommerce-blocks/build/',
                    '/wp-content/plugins/woocommerce/assets/fonts/',
                    '/compatibility/woocommerce/',
                    '/wp-content/themes/astra/assets/css/',
                    '/wp-content/themes/astra/assets/min-css/',
                    '/wp-content/plugins/astra-addon/assets/css/',
                    '/wp-content/themes/kadence/assets/css/',
                    '/wp-content/themes/generatepress/assets/css/',
                    '/wp-content/themes/oceanwp/assets/css/',
                    '/wp-content/themes/hello-elementor/',
                    '/wp-content/themes/blocksy/static/bundle/',
                    '/wp-content/uploads/oxygen/css/',
                    '/wp-content/uploads/bb-plugin/cache/',
                    '/wp-content/plugins/bb-plugin/css/',
                    '/wp-content/plugins/breakdance/plugin/',
                    '/wp-content/uploads/bricks/css/post-',
                    '/wp-content/themes/bricks/assets/css/',
                    '/wp-content/uploads/generateblocks/',
                    '/et-cache/',
                    '/thrive-visual-editor/',
                    '/astra-local-fonts/',
                ]
            )
        );
    }

    /**
     * Exact builder runtime handles that should remain synchronous in JS fallback mode.
     *
     * @return array<int, string>
     */
    public static function get_builder_blocking_script_exact_handles(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_builder_blocking_script_exact_handles',
                [
                    'tve_frontend',
                    'theme-frontend-js',
                    'tve-dash-frontend-js',
                    'elementor-frontend',
                    'elementor-frontend-modules',
                    'elementor-webpack-runtime',
                    'elementor-pro-frontend',
                    'elementor-pro-webpack',
                    'elementor-waypoints',
                    'elementor-dialog',
                    'imagesloaded',
                    'swiper',
                    'smartmenus',
                    'jquery.smartmenus',
                    'neve-script',
                    'astra-theme-js',
                    'astra-theme-js-pro',
                    'astra-addon-js',
                    'astra-mobile-cart',
                    'kad-splide',
                    'kadence-navigation',
                    'kadence-slide-init',
                    'kadence-simplelightbox',
                    'kadence-lightbox-init',
                    'kadence-sticky-header',
                    'kadence-scroll-to-top',
                    'generate-menu',
                    'generate-navigation',
                    'generate-dropdown-click',
                    'generate-modal',
                    'generate-navigation-search',
                    'generate-back-to-top',
                    'generatepress-menu',
                    'oceanwp-main',
                    'oceanwp-mobile-menu',
                    'oceanwp-drop-down-mobile-menu',
                    'oceanwp-full-screen-menu',
                    'oceanwp-vertical-header',
                    'oceanwp-full-screen-mobile-menu',
                    'oceanwp-sidebar-mobile-menu',
                    'oceanwp-drop-down-search',
                    'oceanwp-header-replace-search',
                    'oceanwp-overlay-search',
                    'oceanwp-mobile-search-icon',
                    'oceanwp-equal-height-elements',
                    'oceanwp-slider',
                    'oceanwp-scroll-effect',
                    'oceanwp-scroll-top',
                    'oceanwp-select',
                    'ow-isotop',
                    'ow-flickity',
                    'ow-magnific-popup',
                    'ow-sidr',
                    'ow-perfect-scrollbar',
                    'oceanwp-superfish',
                    'hello-theme-frontend',
                    'fl-builder-layout',
                    'fl-builder-global',
                    'bricks-scripts',
                    'bricks-frontend',
                    'breakdance-frontend',
                    'breakdance-utils',
                    'oxygen-frontend',
                    'ct-frontend',
                    'ct-scripts',
                    'ct-events',
                ]
            )
        );
    }

    /**
     * Builder runtime handle patterns that should remain synchronous in JS fallback mode.
     *
     * @return array<int, string>
     */
    public static function get_builder_blocking_script_handle_patterns(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_builder_blocking_script_handle_patterns',
                [
                    'elementor-frontend',
                    'elementor-pro-frontend',
                    'divi-custom-script',
                    'et-builder-modules-script',
                    'astra-',
                    'kadence-',
                    'generatepress',
                    'generate-',
                    'oceanwp',
                    'fl-builder',
                    'bricks-',
                    'breakdance-',
                    'oxygen',
                    'tve_frontend',
                    'thrive-',
                ]
            )
        );
    }

    /**
     * Builder runtime src patterns that should remain synchronous in JS fallback mode.
     *
     * @return array<int, string>
     */
    public static function get_builder_blocking_script_src_patterns(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_builder_blocking_script_src_patterns',
                [
                    '/elementor/assets/js/frontend.min.js',
                    '/elementor/assets/js/frontend-modules.min.js',
                    '/elementor/assets/js/webpack.runtime.min.js',
                    '/elementor-pro/assets/js/frontend.min.js',
                    '/elementor-pro/assets/js/webpack-pro.runtime.min.js',
                    'elements-handlers.min.js',
                    'imagesloaded.min.js',
                    'swiper.min.js',
                    'jquery.smartmenus.min.js',
                    'jquery.sticky.min.js',
                    '/themes/neve/assets/js/build/modern/frontend.js',
                    '/neve-pro-addon/includes/modules/elementor_booster/assets/js/',
                    '/themes/astra/assets/js/',
                    '/plugins/astra-addon/assets/js/',
                    '/themes/kadence/assets/js/',
                    '/themes/generatepress/assets/js/',
                    '/themes/oceanwp/assets/js/',
                    '/plugins/bb-plugin/js/',
                    '/wp-content/uploads/bb-plugin/cache/',
                    '/themes/bricks/assets/js/',
                    '/plugins/breakdance/plugin/',
                    '/plugins/oxygen/',
                    '/themes/hello-elementor/',
                    '/themes/blocksy/static/bundle/',
                    '/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/',
                ]
            )
        );
    }

    /**
     * Inline runtime/config markers that should never be interaction-delayed.
     *
     * These are typically theme or builder bootstraps rather than low-risk
     * trackers, so delaying them can hide menus/widgets or break initialization.
     *
     * @return array<int, string>
     */
    public static function get_inline_runtime_protected_patterns(): array {
        $patterns = [
            'elementorfrontendconfig',
            'elementorprofrontendconfig',
            'jetenginesettings',
            'jetmenupublicsettings',
            'hasjetblogplaylist',
            'siteparameters',
            'uncode.',
            'nfforms',
            'var nfforms',
            'gform',
            'gforms_recaptcha_',
            'wpforms_settings',
            'wpformsrecaptchacallback',
            'wpcf7',
            'hbspt.forms.create',
            'hbspt.cta.load',
            'trustbox_settings',
            'borlabscookieprioritize',
            'tarteaucitron',
            'usercentrics',
            'borlabscookie',
            'realcookiebanner',
            'huoptions',
            'daextlwcnf-',
            'renderinvisiblerecaptcha',
            'bookinginprogress',
            'window.lazyloadoptions',
            'lazyloadoptions',
            'lazyloadinstance',
            'var gtm4wp',
            'var datalayer_content',
            'window._googlesitekitconsentcategorymap',
            'window._googlesitekitconsents',
            'window._googlesitekit',
            'google_tag_params',
            'dataLayer.push(',
            'gtag("config"',
            "gtag('config'",
            'gtm.start',
            'kadenceconfig',
            'kadenceslideconfig',
            'oceanwplocalize',
            'ct_localizations',
        ];

        $patterns = array_merge(
            $patterns,
            self::get_google_service_inline_bootstrap_patterns(),
            self::get_identity_widget_inline_bootstrap_patterns()
        );

        return self::normalize_patterns(
            apply_filters( 'prorank_inline_runtime_protected_patterns', $patterns )
        );
    }

    /**
     * Image markup markers that should block automatic intrinsic-dimension injection.
     *
     * Some builders/themes manage responsive aspect behavior with their own
     * runtime attributes. Forcing width/height onto those images can distort
     * layout or conflict with the builder's sizing logic.
     *
     * @return array<int, string>
     */
    public static function get_image_dimension_skip_markers(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_image_dimension_skip_markers',
                [
                    'data-height-percentage',
                ]
            )
        );
    }

    /**
     * Generic delay/defer protection markers.
     *
     * @return array<int, string>
     */
    private static function get_default_delay_js_protected_patterns(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_default_delay_js_protected_patterns',
                [
                    'jquery.js',
                    'jquery.min.js',
                    'jquery-migrate.min.js',
                    '/google-site-kit/dist/assets/js/',
                    'js-before',
                    'js-after',
                    'document.write(',
                    'jqueryparams',
                    'et_link_options_data',
                    'turnstile',
                    'recaptcha',
                    'hcaptcha',
                    'js.hsforms.net',
                    'js-eu1.hsforms.net',
                    'js.hscta.net/cta/current.js',
                    'app.mailerlite.com',
                    'static.mailerlite.com/data/',
                    'forms.aweber.com',
                    'simplybook.',
                    'cognitoforms.com',
                    'www.vbt.io/ext/vbtforms.js',
                    'www.paypal.com/sdk/js',
                    'gateway.sumup.com',
                    'checkout.sumup.com',
                    'sumup-payment-gateway',
                    'verify.authorize.net/anetseal',
                    'smarticon.geotrust.com',
                    'use.typekit.net',
                    'use.typekit.com',
                    'elementorfrontendconfig',
                    'elementorprofrontendconfig',
                    'jetenginesettings',
                    'jetmenupublicsettings',
                    'hasjetblogplaylist',
                    'siteparameters',
                    'gform',
                    'nfforms',
                    'gforms_recaptcha_',
                    'wpformsrecaptchacallback',
                    'wpforms_settings',
                    'hbspt.cta.load',
                    'renderinvisiblerecaptcha',
                    'bookinginprogress',
                    'bookacti.booking_system',
                    'booking-suedtirol-js',
                    'ct_checkjs',
                    'window.lazyloadoptions',
                    'lazyloadoptions',
                    'lazyloadinstance',
                    'var gtm4wp',
                    'var datalayer_content',
                ]
            )
        );
    }

    /**
     * Generic defer protection markers.
     *
     * These are the higher-risk external/provider and WordPress runtime files
     * that should not be blindly deferred in raw HTML. This mirrors the more
     * conservative external exclusion datasets carried by mature optimizers.
     *
     * @return array<int, string>
     */
    private static function get_default_defer_js_protected_patterns(): array {
        return self::normalize_patterns(
            apply_filters(
                'prorank_default_defer_js_protected_patterns',
                [
                    'jquery.js',
                    'jquery.min.js',
                    'jquery-migrate.min.js',
                    '/google-site-kit/dist/assets/js/',
                    '/wp-includes/js/dist/vendor/lodash',
                    '/wp-includes/js/dist/api-fetch',
                    '/wp-includes/js/dist/i18n',
                    '/wp-includes/js/dist/vendor/wp-polyfill',
                    '/wp-includes/js/dist/url',
                    '/wp-includes/js/dist/hooks',
                    'document.write(',
                    'turnstile',
                    'recaptcha',
                    'hcaptcha',
                    'js.hsforms.net',
                    'js-eu1.hsforms.net',
                    'js.hscta.net/cta/current.js',
                    'app.mailerlite.com',
                    'static.mailerlite.com/data/',
                    'forms.aweber.com',
                    'simplybook.',
                    'cognitoforms.com',
                    'www.vbt.io/ext/vbtforms.js',
                    'www.paypal.com/sdk/js',
                    'gateway.sumup.com',
                    'checkout.sumup.com',
                    'sumup-payment-gateway',
                    'verify.authorize.net/anetseal',
                    'smarticon.geotrust.com',
                    'use.typekit.net',
                    'use.typekit.com',
                    'bookinginprogress',
                    'bookacti.booking_system',
                    'booking-suedtirol-js',
                    'ct_checkjs',
                    'renderinvisiblerecaptcha',
                    'window.lazyloadoptions',
                    'lazyloadoptions',
                    'lazyloadinstance',
                    'rocket_lcp_data',
                    'rocket_beacon_data',
                ]
            )
        );
    }

    /**
     * High-signal quick exclusions keyed by plugin file.
     *
     * @return array<string, array<int, string>>
     */
    private static function get_delay_js_quick_exclusion_map(): array {
        $map = [
            'borlabs-cookie/borlabs-cookie.php' => [
                '/plugins/borlabs-cookie/',
                'borlabs-cookie',
                'borlabscookie',
                'jquery.min.js',
            ],
            'complianz-gdpr/complianz-gpdr.php' => [
                'complianz',
                'cmplz',
            ],
            'cookie-law-info/cookie-law-info.php' => [
                'cookie-law-info',
                'cookieyes',
                'jquery.min.js',
            ],
            'cookie-notice/cookie-notice.php' => [
                '/plugins/cookie-notice/js/front.min.js',
                'cnargs',
                'cdn.hu-manity.co',
                'huoptions',
            ],
            'cookiebot/cookiebot.php' => [
                'consent.cookiebot.com/uc.js',
                'cookiebot',
            ],
            'gdpr-cookie-compliance/moove-gdpr.php' => [
                '/plugins/gdpr-cookie-compliance/',
                'moove_gdpr',
                'jquery.min.js',
            ],
            'real-cookie-banner-pro/index.php' => [
                'vendor-banner.pro.js',
                'banner.pro.js',
                'realcookiebanner',
                'real-cookie-banner-pro-banner-js-before',
            ],
            'fluentform/fluentform.php' => [
                'jquery.min.js',
                'fluentform',
                'turnstile',
            ],
            'fluentformpro/fluentformpro.php' => [
                'jquery.min.js',
                'fluentform',
                'turnstile',
            ],
            'gravityforms/gravityforms.php' => [
                'jquery.min.js',
                'gravityforms',
                'gform',
                'moxiejs-js',
                'plupload-js',
            ],
            'wpforms-lite/wpforms.php' => [
                'wpforms_settings',
                'wpforms',
                'recaptcha',
                'hcaptcha',
            ],
            'wpforms/wpforms.php' => [
                'wpforms_settings',
                'wpforms',
                'recaptcha',
                'hcaptcha',
            ],
            'contact-form-7/wp-contact-form-7.php' => [
                'wpcf7',
                '/contact-form-7/',
                'recaptcha',
            ],
            'ninja-forms/ninja-forms.php' => [
                'jquery.min.js',
                '/wp-includes/js/underscore.min.js',
                '/wp-includes/js/backbone.min.js',
                '/ninja-forms/assets/js/min/front-end.js',
                '/ninja-forms/assets/js/min/front-end-deps.js',
                'nfforms',
                'nf-',
            ],
            'forminator/forminator.php' => [
                'forminatorfront',
                'forminator',
                'recaptcha',
                'hcaptcha',
            ],
            'termly/termly.php' => [
                'app.termly.io/resource-blocker/',
                'termly',
            ],
            'lightweight-cookie-notice-free/init.php' => [
                '/lightweight-cookie-notice-free/public/assets/js/production/general.js',
                'daextlwcnf-general-js-after',
                'daextlwcnf-general-js-extra',
            ],
            'usercentrics/usercentrics.php' => [
                'usercentrics',
                'usercentrics.eu',
            ],
            'atarim-visual-collaboration/atarim-visual-collaboration.php' => [
                'jquery.min.js',
                '/atarim-client-interface/',
                'jquery_wpf',
                'upgrade_url',
            ],
            'elementor/elementor.php' => [
                'jquery.min.js',
                'jquery.smartmenus.min.js',
                'jquery.sticky.min.js',
                'webpack.runtime.min.js',
                'webpack-pro.runtime.min.js',
                '/elementor/assets/js/frontend.min.js',
                '/elementor-pro/assets/js/frontend.min.js',
                'frontend-modules.min.js',
                'elements-handlers.min.js',
                'elementorfrontendconfig',
                'elementorprofrontendconfig',
                'imagesloaded.min.js',
                'swiper.min.js',
                'e-gallery',
            ],
            'jet-menu/jet-menu.php' => [
                'jquery.min.js',
                'jquery-migrate.min.js',
                '/elementor-pro/',
                '/elementor/',
                '/jet-blog/assets/js/lib/slick/slick.min.js',
                '/jet-elements/',
                '/jet-menu/',
                'elementorfrontendconfig',
                'elementorprofrontendconfig',
                'hasjetblogplaylist',
                'jetenginesettings',
                'jetmenupublicsettings',
            ],
            'kadence-blocks/kadence-blocks.php' => [
                'kadence-blocks-tabs-js',
                'kad-splide-js',
                'kadence-splide-js',
                'kadence-slide-init-js',
                'kadence-blocks-splide-init-js',
                'kadence-blocks-pro-splide-init-js',
                '--scrollbar-offset',
            ],
            'generateblocks-pro/plugin.php' => [
                '/generateblocks-pro/dist/classic-menu.js',
                '/generateblocks-pro/dist/sticky-element.js',
            ],
            'revslider/revslider.php' => [
                'jquery.min.js',
                'jquery-migrate.min.js',
                'revslider',
                'rev_slider',
                'setrevstartsize',
                'window.rs_modules',
                'sr7',
            ],
            'smart-slider-3/smart-slider-3.php' => [
                '/smart-slider-3/',
                '_n2',
            ],
            'nextend-smart-slider3-pro/nextend-smart-slider3-pro.php' => [
                '/nextend-smart-slider3-pro/',
                '_n2',
            ],
            'surecart/surecart.php' => [
                'surecart',
                'hooks.min.js',
                'i18n.min.js',
                'url.min.js',
                'api-fetch.min.js',
                'a11y.min.js',
                'dom-ready.min.js',
            ],
            'honeypot/wp-armour.php' => [
                'wpa_field_info',
            ],
            'js_composer/js_composer.php' => [
                'jquery.min.js',
                'js_composer_front.min.js',
                'wpb_composer_front_js',
                'vc_accordion',
                'vc_tabs',
                'vc_tta_accordion',
                'vc_carousel',
                'vc_grid',
                'isotope.pkgd',
                'prettyphoto',
                '/plugins/js_composer/assets/',
            ],
            'coming-soon/coming-soon.php' => [
                'seedprod-landing-page',
                'seedprod-tsparticles-js',
                'seedprod-masonry-js',
                'seedprod-imagesloaded-js',
                'seedprod-isotope-js',
                'seedprod-iframeresizer',
                '/plugins/coming-soon/public/js/',
                'sp-scripts.min.js',
                'tsparticles.min.js',
                'masonry.pkgd.js',
                'imagesloaded.pkgd.min.js',
                'isotope.pkgd.js',
                'tubular.js',
                'lightbox.min.js',
            ],
            'seedprod-coming-soon-pro/seedprod-coming-soon-pro.php' => [
                'seedprod-landing-page',
                'seedprod_pro_js',
                'seedprod-tsparticles-js',
                'seedprod-masonry-js',
                'seedprod-imagesloaded-js',
                'seedprod-isotope-js',
                'seedprod-iframeresizer',
                '/plugins/seedprod-coming-soon-pro/public/js/',
                'sp-scripts.min.js',
                'tsparticles.min.js',
                'masonry.pkgd.js',
                'imagesloaded.pkgd.min.js',
                'isotope.pkgd.js',
                'tubular.js',
                'lightbox.min.js',
            ],
            'seedprod-coming-soon-pro-5/seedprod-coming-soon-pro-5.php' => [
                'seedprod-landing-page',
                'seedprod_pro_js',
                'seedprod-tsparticles-js',
                'seedprod-masonry-js',
                'seedprod-imagesloaded-js',
                'seedprod-isotope-js',
                'seedprod-iframeresizer',
                '/plugins/seedprod-coming-soon-pro-5/public/js/',
                'sp-scripts.min.js',
                'tsparticles.min.js',
                'masonry.pkgd.js',
                'imagesloaded.pkgd.min.js',
                'isotope.pkgd.js',
                'tubular.js',
                'lightbox.min.js',
            ],
            'ws-form/ws-form.php' => [
                'jquery.min.js',
                'jquery/ui',
                'ws-form',
                'wsf-wp-footer',
                'quicktags-js-extra',
            ],
            'ws-form-pro/ws-form.php' => [
                'jquery.min.js',
                'jquery/ui',
                'ws-form',
                'wsf-wp-footer',
                'quicktags-js-extra',
            ],
            'termageddon-usercentrics/termageddon-usercentrics.php' => [
                'jquery.min.js',
                'termageddon',
                'usercentrics',
                'uc_ui',
            ],
            'thrive-leads/thrive-leads.php' => [
                'jquery.min.js',
                'tve_frontend-js',
            ],
            'woocommerce/woocommerce.php' => [
                'jquery.min.js',
                'flexslider',
                'single-product.min.js',
                'slick',
                'functions.min.js',
                'waypoint',
                'photoswipe',
                'jquery.zoom.min.js',
            ],
            'social-pug/index.php' => [
                'dpsp-frontend-js-pro-js',
            ],
            'grow-for-wp/grow-for-wp.php' => [
                'mv-script-wrapper-js',
            ],
            'shortpixel-adaptive-images/short-pixel-ai.php' => [
                'jquery.min.js',
                'spai',
            ],
            'modula-slider/modula-slider.php' => [
                'jquery.min.js',
                '/modula-slider/',
            ],
            'presto-player/presto-player.php' => [
                'presto',
            ],
            'wp-recipe-maker/wp-recipe-maker.php' => [
                'wprm-public-js',
                'wprm-public-js-extra',
                'wprmp-public-js',
                'wprmp-public-js-extra',
                'wprm-shared-js',
                'wprmp-admin-js',
                'wprm-admin-js',
                'wprm-admin-modal-js',
            ],
            'plausible-analytics/plausible-analytics.php' => [
                'plausible',
            ],
            'mediavine-control-panel/mediavine-control-panel.php' => [
                'mediavine',
            ],
            'adthrive-ads/adthrive-ads.php' => [
                'adthrive',
                'raptive',
                'admanagementconfig',
            ],
            'monumetric-ads/monumetric-ads.php' => [
                '$mmt',
                'monu.delivery',
            ],
            'sheknows-infuse/sheknows-infuse.php' => [
                'blogherads',
            ],
            'slick-engagement/slick-engagement.php' => [
                'slickstream',
            ],
        ];

        return apply_filters( 'prorank_delay_js_quick_exclusion_map', $map );
    }

    /**
     * High-signal quick exclusions keyed by active theme slug.
     *
     * This mirrors the safer competitor approach of carrying theme-aware
     * compatibility data for delay/defer logic. These are intentionally narrow
     * markers for theme bootstraps and navigation/runtime scripts that are
     * known to break when treated like low-risk trackers.
     *
     * @return array<string, array<int, string>>
     */
    private static function get_delay_js_theme_exclusion_map(): array {
        $map = [
            'astra' => [
                'jquery.min.js',
                'astra',
                'astra-theme-js',
                'astra-theme-js-pro',
                'astra-mobile-cart',
            ],
            'avada' => [
                'jquery.min.js',
                'avada-header.js',
                'modernizr.js',
                'jquery.easing.js',
                'avadaheadervars',
            ],
            'bricks' => [
                '/themes/bricks/assets/js/bricks.min.js',
                '/themes/bricks/assets/js/libs/swiper.min.js',
                '/themes/bricks/assets/js/libs/splide.min.js',
                'bricks-scripts-js-extra',
            ],
            'divi' => [
                'jquery.min.js',
                'jquery-migrate.min.js',
                '/divi/js/scripts.min.js',
                '/divi/js/custom.unified.js',
                '/js/magnific-popup.js',
                'et_pb_custom',
                'et_animation_data',
                'var divi',
                'elm.style.display',
                'easypiechart.js',
            ],
            'generatepress' => [
                'generateblog',
                '/generatepress/assets/js/menu.min.js',
                'generatepressmenu',
                'generate-menu',
                'generate-dropdown-click',
                'generate-modal',
                'generate-navigation-search',
                'generate-back-to-top',
                'masonry.min.js',
                'imagesloaded.min.js',
                'offside.min.js',
                'offside',
            ],
            'kadence' => [
                'kadence-navigation-js',
                'kadence-navigation',
                'kadenceconfig',
                'kad-splide',
                'kadence-slide-init',
                'kadenceslideconfig',
                'kadence-simplelightbox',
                'kadence-lightbox-init',
            ],
            'hello-elementor' => [
                'hello-theme-frontend',
                '/themes/hello-elementor/',
            ],
            'blocksy' => [
                'ct-scripts',
                'ct-events',
                'ct_localizations',
                '/themes/blocksy/static/bundle/',
            ],
            'newspaper' => [
                'jquery.min.js',
                'jquery-migrate.min.js',
                'tagdiv_theme.min.js',
                'tdblocksarray',
            ],
            'oceanwp' => [
                'oceanwp-main',
                'drop-down-mobile-menu.min.js',
                'oceanwplocalize',
                'oceanwpLocalize',
                'oceanwp-drop-down-mobile-menu',
                'oceanwp-full-screen-menu',
                'oceanwp-vertical-header',
                'oceanwp-full-screen-mobile-menu',
                'oceanwp-sidebar-mobile-menu',
                'oceanwp-drop-down-search',
                'oceanwp-header-replace-search',
                'oceanwp-overlay-search',
                'oceanwp-mobile-search-icon',
                'oceanwp-slider',
                'ow-sidr',
                'ow-flickity',
                'ow-magnific-popup',
                'ow-perfect-scrollbar',
            ],
            'neve' => [
                'jquery.min.js',
                'neve-script',
                '/themes/neve/assets/js/build/modern/frontend.js',
                '/neve-pro-addon/includes/modules/elementor_booster/assets/js/',
            ],
            'salient' => [
                'jquery.min.js',
                'jquery-migrate.min.js',
                '/salient/',
                '/salient-nectar-slider/js/nectar-slider.js',
            ],
        ];

        return apply_filters( 'prorank_delay_js_theme_exclusion_map', $map );
    }

    /**
     * Return active plugin files for the current site.
     *
     * @return array<int, string>
     */
    private static function get_active_plugin_files(): array {
        static $plugins = null;

        if ( is_array( $plugins ) ) {
            return $plugins;
        }

        $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 ) );
            }
        }

        $plugins = array_values( array_unique( array_map( 'strval', $plugins ) ) );

        return $plugins;
    }

    /**
     * Return active theme identifiers for the current site.
     *
     * Includes stylesheet and template slugs so child themes still match parent
     * theme compatibility rules when needed.
     *
     * @return array<int, string>
     */
    private static function get_active_theme_slugs(): array {
        static $slugs = null;

        if ( is_array( $slugs ) ) {
            return $slugs;
        }

        $slugs = [];
        $theme = wp_get_theme();

        if ( $theme instanceof \WP_Theme ) {
            $slugs[] = strtolower( trim( (string) $theme->get_stylesheet() ) );
            $slugs[] = strtolower( trim( (string) $theme->get_template() ) );
        }

        $slugs = array_values( array_unique( array_filter( $slugs ) ) );

        return $slugs;
    }

    /**
     * Check whether a plugin file is active.
     *
     * @param string $plugin_file Plugin basename.
     * @return bool
     */
    private static function is_plugin_active( string $plugin_file ): bool {
        return in_array( $plugin_file, self::get_active_plugin_files(), true );
    }

    /**
     * Check whether a theme slug is active as the site theme or parent theme.
     *
     * @param string $theme_slug Theme directory slug.
     * @return bool
     */
    private static function is_theme_active( string $theme_slug ): bool {
        $theme_slug = strtolower( trim( $theme_slug ) );
        if ( $theme_slug === '' ) {
            return false;
        }

        return in_array( $theme_slug, self::get_active_theme_slugs(), true );
    }

    /**
     * Normalize and de-duplicate pattern lists.
     *
     * @param mixed $patterns Candidate patterns.
     * @return array<int, string>
     */
    private static function normalize_patterns($patterns): array {
        if ( ! is_array( $patterns ) ) {
            return [];
        }

        $normalized = [];
        foreach ( $patterns as $pattern ) {
            $pattern = strtolower( trim( (string) $pattern ) );
            if ( $pattern === '' ) {
                continue;
            }

            $normalized[ $pattern ] = $pattern;
        }

        return array_values( $normalized );
    }
}
