<?php

namespace FleekDash\Admin\Iframe;

/**
 * Iframe script management
 *
 * Handles enqueuing and injection of JavaScript for iframe contexts,
 * including hotkey forwarding, theme synchronization, and interactions.
 *
 * @package FleekDash\Admin\Iframe
 * @since 2.2.0
 */
class IframeScriptManager
{
    /**
     * Add URL preservation script to prevent Gutenberg from stripping FleekDash params
     *
     * CRITICAL: This must load BEFORE Gutenberg to intercept history.replaceState
     *
     * @return void
     */
    public static function addUrlPreservationScript()
    {
        // Only load on post edit pages with FleekDash iframe parameters
        $has_fleekdash_params = isset($_GET['fleekdash-frame']) || isset($_GET['fleek-compat']) || isset($_GET['protected']);
        if (!$has_fleekdash_params) {
            return;
        }

        // Check if we're on a post edit page
        global $pagenow;
        $is_post_edit = in_array($pagenow, ['post.php', 'post-new.php'], true);
        if (!$is_post_edit) {
            return;
        }

        // Load the URL preservation script in the head (BEFORE Gutenberg)
        \wp_enqueue_script(
            'fleekdash-url-preserve',
            \FleekDash\Libs\Assets\getJsUrl('preserve-url-params'),
            [], // No dependencies - must load first
            FLEEKDASH_VERSION,
            false, // Load in head, not footer
        );
    }

    /**
     * Add iframe scripts (menu sync, interactions, navigation, theme sync)
     *
     * @return void
     */
    public static function addIframeScripts()
    {
        // Register iframe scripts once; hooks may run multiple times per request.
        if (wp_script_is('fleekdash-iframe', 'enqueued') || wp_script_is('fleekdash-iframe', 'registered')) {
            return; // Script already loaded, skip
        }

        // NOTE: Blocking theme script is added separately with priority -9999
        // Do NOT call addCriticalThemeScript here to avoid duplication

        // Add URL preservation script (prevents Gutenberg from stripping params)
        self::addUrlPreservationScript();

        // Add script to handle navigation (only once)
        \wp_enqueue_script(
            'fleekdash-iframe',
            \FleekDash\Libs\Assets\getJsUrl('iframe-menu-sync'),
            [],
            FLEEKDASH_VERSION,
            true,
        );

        \wp_enqueue_script(
            'fleekdash-iframe-interactions',
            \FleekDash\Libs\Assets\getJsUrl('iframe-interactions'),
            array('fleekdash-iframe'),
            FLEEKDASH_VERSION,
            true,
        );

        \wp_enqueue_script(
            'fleekdash-iframe-navigation',
            \FleekDash\Libs\Assets\getJsUrl('iframe-navigation'),
            array('fleekdash-iframe'),
            FLEEKDASH_VERSION,
            true,
        );

        $menuConfig = new \FleekDash\Admin\Menu\MenuConfigService();
        \wp_localize_script(
            'fleekdash-iframe-navigation',
            'fleekDashIframeNav',
            [
                'newTabUrls' => $menuConfig->getNewTabUrls(),
            ],
        );

        // Add unified theme mode sync module (loaded BEFORE other theme scripts)
        \wp_enqueue_script(
            'fleekdash-iframe-theme-sync',
            \FleekDash\Libs\Assets\getJsUrl('iframe-theme-mode-sync'),
            array(), // No dependencies - must load first
            FLEEKDASH_VERSION,
            true,
        );

        // Add hotkey forwarding script directly in iframe context
        // This ensures hotkeys work inside iframes without dynamic injection
        self::addInlineHotkeyScript();

        self::enqueueWpdsProviderSyncScript();

        // Add theme sync script
        self::addThemeSyncScript();

        // Remove WordPress beforeunload listeners in iframe context
        // WordPress adds these on plugin pages to prevent navigation during installs
        // But in iframe context, this causes annoying "Leave page?" alerts
        self::addBeforeUnloadCleanupScript();
    }

    /**
     * Register early iframe scripts (wpwrap pin must load before wpds-provider-sync).
     *
     * @return void
     */
    public static function registerEarlyIframeScripts(): void
    {
        if (wp_script_is('fleekdash-wpwrap-surface-pin', 'registered')) {
            return;
        }

        \wp_register_script(
            'fleekdash-wpwrap-surface-pin',
            \FleekDash\Libs\Assets\getJsUrl('wpwrap-surface-pin'),
            array(),
            FLEEKDASH_VERSION,
            false,
        );
    }

    /**
     * Print #wpwrap surface pin script in admin_head (after blocking theme + palette).
     *
     * @return void
     */
    public static function printWpwrapSurfacePinScript(): void
    {
        self::registerEarlyIframeScripts();
        \wp_enqueue_script('fleekdash-wpwrap-surface-pin');
        \wp_print_scripts('fleekdash-wpwrap-surface-pin');
    }

    /**
     * Init #wpwrap pin observers once palette vars from blocking script are applied.
     *
     * @return void
     */
    public static function printWpwrapSurfacePinInit(): void
    {
        ?>
        <script>
            if (globalThis.FleekDashIframeWpwrapPin) {
                globalThis.FleekDashIframeWpwrapPin.init();
            }
        </script>
        <?php
    }

    /**
     * Re-sync WP 7 WPDS ThemeProvider tokens after React injects inline <style> blocks.
     *
     * @return void
     */
    private static function enqueueWpdsProviderSyncScript(): void
    {
        if (
            wp_script_is('fleekdash-wpds-provider-sync', 'enqueued')
            || wp_script_is('fleekdash-wpds-provider-sync', 'registered')
        ) {
            return;
        }

        self::registerEarlyIframeScripts();

        \wp_enqueue_script(
            'fleekdash-wpds-provider-sync',
            \FleekDash\Libs\Assets\getJsUrl('wpds-provider-sync'),
            array('fleekdash-wpwrap-surface-pin'),
            FLEEKDASH_VERSION,
            false,
        );
    }

    /**
     * Add script to remove WordPress beforeunload listeners in iframe context
     *
     * WordPress adds beforeunload listeners on certain pages (plugins, updates, etc.)
     * to prevent accidental navigation during operations. In iframe context within
     * FleekDash, this causes unwanted "Leave page?" browser alerts.
     *
     * This script removes those listeners while preserving FleekDash's own listeners.
     *
     * @return void
     */
    private static function addBeforeUnloadCleanupScript()
    {
        // Use admin_footer with late priority to run AFTER WordPress adds its listeners
        \add_action('admin_footer', function () {
            ?>
            <script>
            (function() {
                'use strict';

                // Disable beforeunload dialogs WITHOUT breaking WordPress functionality
                // The key is to intercept ONLY the returnValue, not the event propagation

                // Method 1: Override BeforeUnloadEvent prototype to ignore returnValue
                // This is the SAFEST method - doesn't interfere with event flow
                try {
                    Object.defineProperty(BeforeUnloadEvent.prototype, 'returnValue', {
                        get: function() {
                            return ''; // Always return empty (no dialog)
                        },
                        set: function(value) {
                            // Silently ignore - don't set the value
                            // This prevents the confirmation dialog
                            // but doesn't interfere with other functionality
                        },
                        configurable: true
                    });
                    console.debug('[FleekDash Iframe] BeforeUnloadEvent.returnValue intercepted');
                } catch (e) {
                    console.debug('[FleekDash] Could not override BeforeUnloadEvent prototype:', e);
                }

                // Method 2: Continuously clear window.onbeforeunload
                // WordPress may set this directly at any time
                let clearIntervalId = setInterval(function() {
                    if (window.onbeforeunload !== null) {
                        window.onbeforeunload = null;
                    }
                }, 100);

                // Stop interval after 60 seconds
                setTimeout(function() {
                    clearInterval(clearIntervalId);
                }, 60000);

                // Method 3: Clear onbeforeunload on specific WordPress AJAX actions
                // Listen for WordPress AJAX events that might set beforeunload
                if (typeof jQuery !== 'undefined') {
                    jQuery(document).ajaxComplete(function() {
                        window.onbeforeunload = null;
                    });
                }

                console.debug('[FleekDash Iframe] beforeunload cleanup v3 initialized');
            })();
            </script>
            <?php
        }, 999); // Priority 999 - run late to override WordPress
    }

    /**
     * Sync html/body embed markers immediately after the admin <body> opens.
     *
     * Blocking admin_head only sets html.fleekdash-inside-iframe; body.inside-iframe is
     * required by CSS (e.g. body.fleekdash-iframe.inside-iframe) and must not wait for footer JS.
     *
     * @return void
     */
    public static function addEarlyInsideIframeBodyClassScript(): void
    {
        ?>
        <script>
            (function () {
                'use strict';
                if (globalThis.self === globalThis.top) {
                    return;
                }
                document.documentElement.classList.add('fleekdash-inside-iframe');
                if (document.body) {
                    document.body.classList.add('inside-iframe');
                }
            })();
        </script>
        <?php
    }

    /**
     * Add BLOCKING theme script (prevents flash by blocking rendering)
     *
     * This is the ULTIMATE solution - uses blocking technique to prevent
     * ANY HTML rendering until styles are fully applied.
     *
     * Technique: Injects script that blocks document parsing until styles are ready.
     *
     * @return void
     */
    public static function addBlockingThemeScript()
    {
        $criticalScrollbarCssJson = \wp_json_encode(
            IframeCSSManager::getCriticalScrollbarCssForFirstPaint(),
        );
        $exposeNativeAdminBarInEmbed = EmbeddedShellAdminBarPolicy::isNativeAdminBarVisibleInEmbed();
        $criticalAdminBarHideCssJson = \wp_json_encode(
            $exposeNativeAdminBarInEmbed ? '' : IframeCSSManager::getCriticalAdminBarHideCssForFirstPaint(),
        );
        $criticalWpwrapSurfaceCssJson = \wp_json_encode(
            IframeCSSManager::getCriticalWpwrapSurfaceCssForFirstPaint(),
        );
        ?>
        <script>
            // CRITICAL: This script MUST execute BEFORE any HTML renders
            // It blocks rendering until styles are applied

            (function() {
                'use strict';

                // Get HTML element immediately
                const html = document.documentElement;

                // CRITICAL: Determine theme mode and apply styles SYNCHRONOUSLY
                // This must happen BEFORE any CSS loads

                try {
                    // 1. Detect mode
                    const urlParams = new URLSearchParams(window.location.search);
                    const compatMode = urlParams.get('fleek-compat');
                    let detectedMode = null;

                    if (compatMode && ['trusted', 'windowed', 'windowed-light', 'windowed-dark', 'protected', 'protected-dark'].includes(compatMode)) {
                        detectedMode = compatMode;
                    } else {
                        try {
                            if (window.parent && window.parent !== window && window.frameElement) {
                                const parentFleekWindow = window.frameElement.closest('.fleek-window');
                                if (parentFleekWindow) {
                                    detectedMode = parentFleekWindow.getAttribute('data-mode') || 'windowed';
                                } else {
                                    detectedMode = 'trusted';
                                }
                            } else {
                                detectedMode = 'windowed';
                            }
                        } catch {
                            detectedMode = 'windowed';
                        }
                    }

                    const mode = detectedMode || 'windowed';
                    const urlProtected = urlParams.get('protected') === 'true';
                    const allowsCustomThemes =
                        !urlProtected &&
                        mode !== 'protected' &&
                        mode !== 'protected-dark' &&
                        mode !== 'windowed-light' &&
                        mode !== 'windowed-dark';

                    // 2. Determine dark mode
                    let isDark = false;
                    if (mode === 'protected-dark') {
                        isDark = true;
                    } else if (urlProtected || mode === 'protected') {
                        isDark = false;
                    } else if (mode === 'windowed-light') {
                        isDark = false;
                    } else if (mode === 'windowed-dark') {
                        isDark = true;
                    } else {
                        let parentIsDark = null;
                        try {
                            if (window.parent && window.parent !== window && window.parent.document) {
                                const parentHtml = window.parent.document.documentElement;
                                if (parentHtml && parentHtml.classList) {
                                    parentIsDark = parentHtml.classList.contains('dark');
                                }
                            }
                        } catch {}

                        if (parentIsDark !== null) {
                            isDark = parentIsDark;
                        } else {
                            try {
                                isDark = localStorage.getItem('fleekdash-dark-mode') === 'true';
                            } catch {
                                isDark = false;
                            }
                        }
                    }

                    // 3. CRITICAL: Apply background color IMMEDIATELY (blocks white flash)
                    if (isDark) {
                        html.style.setProperty('background-color', '#0a0a0a', 'important');
                        html.style.setProperty('color', '#e5e5e5', 'important');
                    } else {
                        html.style.setProperty('background-color', '#ffffff', 'important');
                        html.style.setProperty('color', '#0a0a0a', 'important');
                    }

                    // 4. Apply classes
                    html.classList.remove('dark', 'light');
                    html.classList.add(isDark ? 'dark' : 'light');

                    const themeName = allowsCustomThemes
                        ? (localStorage.getItem('fleekdash-theme') || 'starter')
                        : 'starter';
                    html.setAttribute('data-theme', themeName);

                    // 5. Apply mode class
                    const modeClasses = ['fleek-mode-trusted', 'fleek-mode-windowed', 'fleek-mode-windowed-light', 'fleek-mode-windowed-dark', 'fleek-mode-protected', 'fleek-mode-protected-dark'];
                    html.classList.remove(...modeClasses);
                    html.classList.add('fleek-mode-' + mode);

                    if (window.self !== window.top) {
                        html.classList.add('fleekdash-inside-iframe');
                    }

                    // 5b. Scrollbar first-paint (before body / before enqueued CSS — avoids native bar flash + shift)
                    var criticalScrollbarCss = <?php echo $criticalScrollbarCssJson; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
                    var criticalAdminBarHideCss = <?php echo $criticalAdminBarHideCssJson; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
                    var criticalWpwrapSurfaceCss = <?php echo $criticalWpwrapSurfaceCssJson; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
                    if (document.readyState === 'loading' && typeof document.write === 'function') {
                        document.write('<style id="fleekdash-critical-scrollbar">' + criticalScrollbarCss + '</style>');
                        if (criticalAdminBarHideCss) {
                            document.write('<style id="fleekdash-critical-adminbar-hide">' + criticalAdminBarHideCss + '</style>');
                        }
                        if (criticalWpwrapSurfaceCss) {
                            document.write('<style id="fleekdash-critical-wpwrap-surface">' + criticalWpwrapSurfaceCss + '</style>');
                        }
                    } else if (document.head) {
                        var scrollbarStyle = document.getElementById('fleekdash-critical-scrollbar');
                        if (!scrollbarStyle) {
                            scrollbarStyle = document.createElement('style');
                            scrollbarStyle.id = 'fleekdash-critical-scrollbar';
                            document.head.insertBefore(scrollbarStyle, document.head.firstChild);
                        }
                        scrollbarStyle.textContent = criticalScrollbarCss;

                        if (criticalAdminBarHideCss) {
                            var adminBarHideStyle = document.getElementById('fleekdash-critical-adminbar-hide');
                            if (!adminBarHideStyle) {
                                adminBarHideStyle = document.createElement('style');
                                adminBarHideStyle.id = 'fleekdash-critical-adminbar-hide';
                                document.head.insertBefore(adminBarHideStyle, document.head.firstChild);
                            }
                            adminBarHideStyle.textContent = criticalAdminBarHideCss;
                        }

                        if (criticalWpwrapSurfaceCss) {
                            var wpwrapSurfaceStyle = document.getElementById('fleekdash-critical-wpwrap-surface');
                            if (!wpwrapSurfaceStyle) {
                                wpwrapSurfaceStyle = document.createElement('style');
                                wpwrapSurfaceStyle.id = 'fleekdash-critical-wpwrap-surface';
                                document.head.insertBefore(wpwrapSurfaceStyle, document.head.firstChild);
                            }
                            wpwrapSurfaceStyle.textContent = criticalWpwrapSurfaceCss;
                        }
                    }

                    // 6. CRITICAL: Inject palette variables SYNCHRONOUSLY
                    try {
                        const paletteVarsCss = localStorage.getItem('fleekdash:palette-vars');
                        if (paletteVarsCss) {
                            if (allowsCustomThemes) {
                                // Use document.write if still parsing (BLOCKS rendering until complete)
                                if (document.readyState === 'loading' && typeof document.write === 'function') {
                                    const escaped = paletteVarsCss.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/<\/style/gi, '<\\/style');
                                    document.write('<style id="fleekdash-active-palette-vars">' + escaped + '</style>');
                                } else {
                                    // Inject immediately at start of head
                                    let style = document.getElementById('fleekdash-active-palette-vars');
                                    if (!style) {
                                        style = document.createElement('style');
                                        style.id = 'fleekdash-active-palette-vars';
                                        if (document.head) {
                                            if (document.head.firstChild) {
                                                document.head.insertBefore(style, document.head.firstChild);
                                            } else {
                                                document.head.appendChild(style);
                                            }
                                        } else {
                                            // Head doesn't exist - wait synchronously
                                            while (!document.head) {
                                                // Block until head exists
                                            }
                                            if (document.head.firstChild) {
                                                document.head.insertBefore(style, document.head.firstChild);
                                            } else {
                                                document.head.appendChild(style);
                                            }
                                        }
                                    }
                                    style.textContent = paletteVarsCss;
                                }
                            } else {
                                // Extract limited variables for protected/windowed modes
                                const isWindowedMode = mode === 'windowed-light' || mode === 'windowed-dark';
                                const cardSurfaceVars = ['--card', '--card-2', '--card-5', '--card-10', '--card-20', '--card-50', '--card-foreground'];
                                let allowedVars = ['--primary', '--secondary', '--accent',
                                                     '--primary-foreground', '--secondary-foreground', '--accent-foreground',
                                                     '--primary-rgb', '--accent-rgb'];
                                if (isWindowedMode) {
                                    allowedVars = allowedVars.concat([
                                        '--background', '--background-2', '--background-5', '--background-10', '--background-20',
                                    ]).concat(cardSurfaceVars);
                                } else {
                                    allowedVars = allowedVars.concat(cardSurfaceVars);
                                }

                                const modeStr = isDark ? 'dark' : 'light';
                                const extractedVars = {};
                                let match;

                                const paletteVarPattern = isWindowedMode
                                    ? '--palette-' + modeStr + '-(background(?:-2|-5|-10|-20)?|card(?:-2|-5|-foreground)?|primary(?:-foreground|-rgb)?|secondary(?:-foreground)?|accent(?:-foreground|-rgb)?)'
                                    : '--palette-' + modeStr + '-(card(?:-2|-5|-foreground)?|primary(?:-foreground|-rgb)?|secondary(?:-foreground)?|accent(?:-foreground|-rgb)?)';

                                const paletteVarRegex = new RegExp(paletteVarPattern + '\\s*:\\s*([^;]+);', 'gi');
                                while ((match = paletteVarRegex.exec(paletteVarsCss)) !== null) {
                                    const fullVarName = match[1];
                                    const varValue = match[2].trim();
                                    const cssVarName = '--' + fullVarName;
                                    if (allowedVars.includes(cssVarName)) {
                                        extractedVars[cssVarName] = varValue;
                                    }
                                }

                                const modeSelector = isDark ? '\\.dark' : '\\.light';
                                const directVarPattern = isWindowedMode
                                    ? '--(background(?:-2|-5|-10|-20)?|card(?:-2|-5|-foreground)?|primary(?:-foreground|-rgb)?|secondary(?:-foreground)?|accent(?:-foreground|-rgb)?)'
                                    : '--(card(?:-2|-5|-foreground)?|primary(?:-foreground|-rgb)?|secondary(?:-foreground)?|accent(?:-foreground|-rgb)?)';

                                const directVarRegex = new RegExp('(?:html|:root)(?:' + modeSelector + ')?\\s*\\{[^}]*' + directVarPattern + '\\s*:\\s*([^;]+);', 'gi');
                                while ((match = directVarRegex.exec(paletteVarsCss)) !== null) {
                                    const fullVarName = match[1];
                                    const varValue = match[2].trim();
                                    const cssVarName = '--' + fullVarName;
                                    if (allowedVars.includes(cssVarName) && !extractedVars[cssVarName]) {
                                        extractedVars[cssVarName] = varValue;
                                    }
                                }

                                Object.keys(extractedVars).forEach(function(varName) {
                                    html.style.setProperty(varName, extractedVars[varName], 'important');
                                });
                            }
                        }
                    } catch (e) {
                        // Silent fail for palette vars
                    }

                    // 7. Remove background block after styles are applied (async to not block)
                    setTimeout(function() {
                        html.style.removeProperty('background-color');
                        html.style.removeProperty('color');
                    }, 0);

                } catch (e) {
                    // Fallback: apply light mode
                    html.classList.add('light');
                    html.setAttribute('data-theme', 'starter');
                    html.style.setProperty('background-color', '#ffffff', 'important');
                    html.style.setProperty('color', '#0a0a0a', 'important');
                }
            })();
        </script>
        <?php
    }

    /**
     * Add CRITICAL inline theme script (runs BEFORE any HTML rendering)
     *
     * This script prevents the white flash on hard reload by applying
     * the dark/light class IMMEDIATELY from localStorage, before any
     * CSS or content is rendered.
     *
     * Also applies custom theme (data-theme) and palette CSS variables
     * in modes that allow custom themes.
     *
     * In protected mode, extracts and applies only specific CSS variables
     * (--primary, --secondary, --accent) to allow basic color customization
     * without full theme injection that could break WordPress admin styles.
     *
     * NOTE: Full globals.css (Tailwind) is NOT injected into wp-admin iframe documents.
     * PHP enqueues iframe-tokens.css instead. This script applies palette variables only.
     *
     * CRITICAL: This must be the FIRST script in <head> to work!
     * RESPECTS: Protected mode (forces light when protected=true, limited theme vars)
     * RESPECTS: Windowed-light/dark modes (forces light/dark, limited theme vars)
     * ALLOWS: Full custom themes in trusted/windowed modes
     *
     * @return void
     */
    public static function addCriticalThemeScript()
    {
        ?>
        <script>
            (function() {
                'use strict';

                // CRITICAL: Block rendering until styles are applied
                // This prevents ANY flash by applying styles before HTML renders

                // Technique: Apply styles directly to HTML element BEFORE any CSS loads
                // This is faster than classes and prevents any visual flash

                try {
                    // Get HTML element immediately (available even during parsing)
                    const html = document.documentElement;

                    // CRITICAL: Apply background color FIRST to prevent white flash
                    // This creates a visual "block" that matches the theme
                    const applyBackgroundBlock = function(isDark) {
                        // Apply background color directly to HTML element
                        // This prevents white flash even before CSS loads
                        if (isDark) {
                            html.style.setProperty('background-color', '#0a0a0a', 'important');
                            html.style.setProperty('color', '#e5e5e5', 'important');
                        } else {
                            html.style.setProperty('background-color', '#ffffff', 'important');
                            html.style.setProperty('color', '#0a0a0a', 'important');
                        }
                    };
                    // Match getParentWindowMode() so compat mode matches the parent shell in production.

                    // 1. Check URL parameter FIRST (for pages opened in new tab)
                    const urlParams = new URLSearchParams(window.location.search);
                    const compatMode = urlParams.get('fleek-compat');
                    let detectedMode = null;

                    if (compatMode && ['trusted', 'windowed', 'windowed-light', 'windowed-dark', 'protected', 'protected-dark'].includes(compatMode)) {
                        detectedMode = compatMode;
                    } else {
                        // 2. Check parent fleek-window container (for iframe context)
                        try {
                            if (window.parent && window.parent !== window && window.frameElement) {
                                const parentFleekWindow = window.frameElement.closest('.fleek-window');
                                if (parentFleekWindow) {
                                    detectedMode = parentFleekWindow.getAttribute('data-mode') || 'windowed';
                                } else {
                                    detectedMode = 'trusted'; // No container = trusted context
                                }
                            } else {
                                // Not in iframe - default to windowed
                                detectedMode = 'windowed';
                            }
                        } catch {
                            // Cross-origin - default to windowed
                            detectedMode = 'windowed';
                        }
                    }

                    // Use detected mode or fallback to windowed
                    const mode = detectedMode || 'windowed';
                    const urlProtected = urlParams.get('protected') === 'true';

                    // Determine if custom themes are allowed
                    const allowsCustomThemes =
                        !urlProtected &&
                        mode !== 'protected' &&
                        mode !== 'protected-dark' &&
                        mode !== 'windowed-light' &&
                        mode !== 'windowed-dark';

                    let isDark = false;

                    // CRITICAL: Determine dark mode FIRST (before applying anything)
                    // This allows us to apply background block immediately

                    if (mode === 'protected-dark') {
                        // PROTECTED-DARK: No FleekDash admin CSS, dark chrome + dark document shell
                        isDark = true;
                    } else if (urlProtected || mode === 'protected') {
                        // PROTECTED MODE: Always force light
                        isDark = false;
                    } else if (mode === 'windowed-light') {
                        // WINDOWED-LIGHT MODE: Always force light
                        isDark = false;
                    } else if (mode === 'windowed-dark') {
                        // WINDOWED-DARK MODE: Always force dark
                        isDark = true;
                    } else {
                        // DYNAMIC MODE (trusted/windowed): Try to read from parent first, then localStorage
                        // CRITICAL: For trusted mode, try to sync with parent if accessible
                        let parentIsDark = null;
                        try {
                            if (window.parent && window.parent !== window && window.parent.document) {
                                const parentHtml = window.parent.document.documentElement;
                                if (parentHtml && parentHtml.classList) {
                                    parentIsDark = parentHtml.classList.contains('dark');
                                }
                            }
                        } catch {
                            // Cannot access parent - use localStorage
                        }

                        // Use parent value if available, otherwise fallback to localStorage
                        if (parentIsDark !== null) {
                            isDark = parentIsDark;
                        } else {
                            isDark = localStorage.getItem('fleekdash-dark-mode') === 'true';
                        }
                    }

                    // CRITICAL: Apply background block IMMEDIATELY (prevents white flash)
                    // This happens BEFORE any other styling
                    applyBackgroundBlock(isDark);

                    // Get theme name (from localStorage if custom themes allowed, otherwise default)
                    const themeName = allowsCustomThemes
                        ? (localStorage.getItem('fleekdash-theme') || 'starter')
                        : 'starter';

                    // Remove both classes first (cleanup)
                    html.classList.remove('dark', 'light');

                    // Add correct class
                    html.classList.add(isDark ? 'dark' : 'light');

                    // Also add data-theme attribute
                    html.setAttribute('data-theme', themeName);

                    // Add mode class for debugging
                    const modeClasses = ['fleek-mode-trusted', 'fleek-mode-windowed', 'fleek-mode-windowed-light', 'fleek-mode-windowed-dark', 'fleek-mode-protected', 'fleek-mode-protected-dark'];
                    html.classList.remove(...modeClasses);
                    html.classList.add('fleek-mode-' + mode);

                    // CRITICAL: Inject custom theme palette variables
                    // NOTE: We do NOT inject globals.css here because it conflicts with WordPress admin styles
                    // Only palette CSS variables are injected (they're safe - just CSS custom properties)
                    const paletteVarsCss = localStorage.getItem('fleekdash:palette-vars');

                    if (paletteVarsCss) {
                        if (allowsCustomThemes) {
                            // FULL MODE: Inject complete palette CSS variables
                            // CRITICAL: Use blocking technique to prevent flash

                            // Method 1: Use document.write if still parsing (BEST - prevents FOUC)
                            if (document.readyState === 'loading' && typeof document.write === 'function') {
                                const escaped = paletteVarsCss.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/<\/style/gi, '<\\/style');
                                document.write('<style id="fleekdash-active-palette-vars">' + escaped + '</style>');
                            } else {
                                // Method 2: Inject via DOM immediately (if head exists)
                                let style = document.getElementById('fleekdash-active-palette-vars');
                                if (!style) {
                                    // CRITICAL: Insert at the BEGINNING of head (before any CSS)
                                    style = document.createElement('style');
                                    style.id = 'fleekdash-active-palette-vars';
                                    if (document.head) {
                                        // Insert as first child to ensure priority
                                        if (document.head.firstChild) {
                                            document.head.insertBefore(style, document.head.firstChild);
                                        } else {
                                            document.head.appendChild(style);
                                        }
                                    } else {
                                        // Head doesn't exist yet - wait for it
                                        const observer = new MutationObserver(function(mutations, obs) {
                                            if (document.head) {
                                                if (document.head.firstChild) {
                                                    document.head.insertBefore(style, document.head.firstChild);
                                                } else {
                                                    document.head.appendChild(style);
                                                }
                                                style.textContent = paletteVarsCss;
                                                obs.disconnect();
                                            }
                                        });
                                        observer.observe(document.documentElement, { childList: true, subtree: true });
                                        // Fallback timeout
                                        setTimeout(function() {
                                            observer.disconnect();
                                            if (document.head && !style.parentNode) {
                                                if (document.head.firstChild) {
                                                    document.head.insertBefore(style, document.head.firstChild);
                                                } else {
                                                    document.head.appendChild(style);
                                                }
                                                style.textContent = paletteVarsCss;
                                            }
                                        }, 100);
                                    }
                                }
                                if (style.parentNode) {
                                    style.textContent = paletteVarsCss;
                                }
                            }
                        } else {
                            // FORCED MODE (windowed-light, windowed-dark, protected): Extract and apply limited variables
                            // For windowed-light/windowed-dark: Allow background customization (user palette)
                            // For protected: Only allow accent colors (no background customization)
                            const isWindowedMode = mode === 'windowed-light' || mode === 'windowed-dark';
                            const cardSurfaceVars = ['--card', '--card-2', '--card-5', '--card-10', '--card-20', '--card-50', '--card-foreground'];

                            // Base allowed variables (accent colors)
                            let allowedVars = ['--primary', '--secondary', '--accent',
                                                 '--primary-foreground', '--secondary-foreground', '--accent-foreground',
                                                 '--primary-rgb', '--accent-rgb'];

                            // For windowed modes, also allow background customization
                            if (isWindowedMode) {
                                allowedVars = allowedVars.concat([
                                    '--background', '--background-2', '--background-5', '--background-10', '--background-20',
                                ]).concat(cardSurfaceVars);
                            } else {
                                allowedVars = allowedVars.concat(cardSurfaceVars);
                            }

                            // Extract variable values from CSS
                            const modeStr = isDark ? 'dark' : 'light';
                            const extractedVars = {};

                            // Method 1: Extract from --palette-{mode}-{var} format
                            // Match format: --palette-light-background: value; or --palette-light-primary-rgb: value;
                            let match;

                            // Build regex pattern for palette variables
                            // For windowed modes, include background and card variables
                            const paletteVarPattern = isWindowedMode
                                ? '--palette-' + modeStr + '-(background(?:-2|-5|-10|-20)?|card(?:-2|-5|-foreground)?|primary(?:-foreground|-rgb)?|secondary(?:-foreground)?|accent(?:-foreground|-rgb)?)'
                                : '--palette-' + modeStr + '-(card(?:-2|-5|-foreground)?|primary(?:-foreground|-rgb)?|secondary(?:-foreground)?|accent(?:-foreground|-rgb)?)';

                            const paletteVarRegex = new RegExp(paletteVarPattern + '\\s*:\\s*([^;]+);', 'gi');

                            while ((match = paletteVarRegex.exec(paletteVarsCss)) !== null) {
                                const fullVarName = match[1]; // e.g., 'background', 'background-10', 'primary-rgb'
                                const varValue = match[2].trim();
                                const cssVarName = '--' + fullVarName;

                                if (allowedVars.includes(cssVarName)) {
                                    extractedVars[cssVarName] = varValue;
                                }
                            }

                            // Method 2: Extract from direct --{var} in :root or html selectors (for current mode)
                            // Match format: :root.light { --background: value; } or html.light { --primary: value; }
                            const modeSelector = isDark ? '\\.dark' : '\\.light';
                            const directVarPattern = isWindowedMode
                                ? '--(background(?:-2|-5|-10|-20)?|card(?:-2|-5|-foreground)?|primary(?:-foreground|-rgb)?|secondary(?:-foreground)?|accent(?:-foreground|-rgb)?)'
                                : '--(card(?:-2|-5|-foreground)?|primary(?:-foreground|-rgb)?|secondary(?:-foreground)?|accent(?:-foreground|-rgb)?)';

                            const directVarRegex = new RegExp('(?:html|:root)(?:' + modeSelector + ')?\\s*\\{[^}]*' + directVarPattern + '\\s*:\\s*([^;]+);', 'gi');

                            while ((match = directVarRegex.exec(paletteVarsCss)) !== null) {
                                const fullVarName = match[1]; // e.g., 'background', 'background-10', 'primary-rgb'
                                const varValue = match[2].trim();
                                const cssVarName = '--' + fullVarName;

                                if (allowedVars.includes(cssVarName) && !extractedVars[cssVarName]) {
                                    // Only use if not already extracted from palette vars (palette vars have priority)
                                    extractedVars[cssVarName] = varValue;
                                }
                            }

                            // Apply extracted variables directly to HTML element
                            // CRITICAL: Use 'important' flag to override any CSS that loads later
                            Object.keys(extractedVars).forEach(function(varName) {
                                html.style.setProperty(varName, extractedVars[varName], 'important');
                            });
                        }
                    }

                    // CRITICAL: Remove background block after styles are applied
                    // The CSS will handle colors from now on
                    // Use setTimeout to ensure CSS has loaded
                    setTimeout(function() {
                        html.style.removeProperty('background-color');
                        html.style.removeProperty('color');
                    }, 0);

                    // Silent - no console logs in production
                } catch (e) {
                    // Fallback to light mode on error
                    const html = document.documentElement;
                    html.classList.add('light');
                    html.setAttribute('data-theme', 'starter');
                    html.style.setProperty('background-color', '#ffffff', 'important');
                    html.style.setProperty('color', '#0a0a0a', 'important');
                }
            })();
        </script>
        <?php
    }

    /**
     * Add inline hotkey forwarding script to iframe
     *
     * Forwards keyboard shortcuts from iframe to parent window.
     * Includes sticky element fixes for WordPress admin bar.
     *
     * Execution order: {@see EmbeddedShellAdminBarPolicy::apply} runs on `current_screen`
     * and is re-invoked from {@see \FleekDash\Admin\Iframe::addBlockingThemeScript} when the screen exists but
     * policy has not run yet. Sets body.fleekdash-adminbar-hidden when the native bar is hidden.
     *
     * @return void
     */
    public static function addInlineHotkeyScript()
    {
        ?>
        <script>
            ( function () {
                'use strict';

                // Prevent duplicate initialization with detailed logging
                if ( window.fleekDashHotkeyInitialized ) {
                    console.log( '[FleekDash Iframe] Hotkey script already initialized - SKIPPING' );
                    return;
                }
                window.fleekDashHotkeyInitialized = true;

                // Generate unique instance ID for message tracking
                const instanceId = 'iframe-' + Math.random().toString( 36 ).substr( 2, 9 );

                // Function to send messages to parent
                function sendMessage ( data ) {
                    if ( window.parent && window.parent !== window ) {
                        try {
                            window.parent.postMessage( data, '*' );
                        } catch ( error ) {
                            // Silent fail - avoid console spam
                        }
                    }
                }

                // Debounce system to prevent duplicate messages
                let lastMessageSent = new Map();
                const DEBOUNCE_TIME = 150; // Reduced to 150ms for faster response now that double execution is fixed

                /**
                 * Fleek CMDK when embedded in FleekDash iframe (body.inside-iframe).
                 * Standalone tab with fleekdash-frame=1 keeps WP 7 Command Palette on Ctrl/Cmd+K.
                 * Same rules as iframe-menu-sync syncInsideIframeBodyClass().
                 */
                function shouldForwardCmdkToFleekDash() {
                    if ( document.body && document.body.classList.contains( 'inside-iframe' ) ) {
                        return true;
                    }
                    if ( document.documentElement && document.documentElement.classList.contains( 'fleekdash-inside-iframe' ) ) {
                        return true;
                    }
                    return window.self !== window.top;
                }

                // Helper function to check if user is typing in an input field
                function isTypingInInputField() {
                    const activeElement = document.activeElement;

                    if (!activeElement) {
                        return false;
                    }

                    // Check for input, textarea, and contenteditable elements
                    const isInputElement = activeElement.tagName === "INPUT";
                    const isTextareaElement = activeElement.tagName === "TEXTAREA";
                    const isContentEditable = activeElement.contentEditable === "true";

                    // Check for specific input types that should allow typing
                    const isTypableInput = isInputElement && (
                        activeElement.type === "text" ||
                        activeElement.type === "email" ||
                        activeElement.type === "password" ||
                        activeElement.type === "search" ||
                        activeElement.type === "url" ||
                        activeElement.type === "tel" ||
                        activeElement.type === "number" ||
                        activeElement.type === "date" ||
                        activeElement.type === "time" ||
                        activeElement.type === "datetime-local" ||
                        activeElement.type === "month" ||
                        activeElement.type === "week" ||
                        !activeElement.type // Default input type is text
                    );

                    // Check for select elements (dropdowns)
                    const isSelectElement = activeElement.tagName === "SELECT";

                    // Check for elements with specific roles that indicate text input
                    const isTextInputRole = activeElement.getAttribute("role") === "textbox" ||
                                           activeElement.getAttribute("role") === "searchbox";

                    // Check for elements with specific classes that indicate text input
                    const hasTextInputClass = activeElement.classList.contains("text-input") ||
                                             activeElement.classList.contains("search-input") ||
                                             activeElement.classList.contains("input-field");

                    return isTypableInput || isTextareaElement || isContentEditable ||
                           isSelectElement || isTextInputRole || hasTextInputClass;
                }

                // Hotkey detection with debouncing
                function handleKeydown ( event ) {
                    // Only process trusted events to avoid synthetic duplicates
                    if ( !event.isTrusted ) {
                        return;
                    }

                    // Build combination string
                    const key = event.key.toLowerCase();
                    const combination = `${ event.ctrlKey ? 'ctrl+' : '' }${ event.metaKey ? 'cmd+' : '' }${ event.altKey ? 'alt+' : '' }${ event.shiftKey ? 'shift+' : '' }${ key }`;

                    // FleekDash-forwarded hotkeys; Ctrl/Cmd+K only when inside FleekDash iframe (not standalone fleekdash-frame tab)
                    const hotkeys = [ 'ctrl+space', 'ctrl+ ', 'cmd+space', 'cmd+ ', 'ctrl+k', 'cmd+k', 'alt+m', 'alt+w', 'alt+t', 'ctrl+shift+f', 'cmd+shift+f', 'escape', '/' ];

                    if ( hotkeys.includes( combination ) ) {
                        if (
                            ( combination === 'ctrl+k' || combination === 'cmd+k' ) &&
                            ! shouldForwardCmdkToFleekDash()
                        ) {
                            return;
                        }
                        // Special handling for "/" key - only trigger if not typing in input field
                        if ( combination === '/' && isTypingInInputField() ) {
                            return; // Don't process the hotkey if typing in input field
                        }
                        const normalizedCombo = combination === 'ctrl+ ' ? 'ctrl+space' : combination;

                        // Debounce check
                        const now = Date.now();
                        const lastSent = lastMessageSent.get( normalizedCombo ) || 0;

                        if ( now - lastSent < DEBOUNCE_TIME ) {
                            return;
                        }

                        lastMessageSent.set( normalizedCombo, now );

                        // Prevent default behavior - do this immediately
                        event.preventDefault();
                        event.stopPropagation();
                        event.stopImmediatePropagation();

                        // Send to parent
                        sendMessage( {
                            type: 'hotkey',
                            combination: normalizedCombo,
                            timestamp: now,
                            instanceId: instanceId
                        } );
                    }
                }

                // Add multiple layers of event capturing with MAXIMUM PRIORITY
                const eventOptions = { capture: true, passive: false };

                document.addEventListener( 'keydown', handleKeydown, eventOptions );
                window.addEventListener( 'keydown', handleKeydown, eventOptions );

                // Also add to body for extra coverage
                if ( document.body ) {
                    document.body.addEventListener( 'keydown', handleKeydown, eventOptions );
                } else {
                    document.addEventListener( 'DOMContentLoaded', () => {
                        document.body?.addEventListener( 'keydown', handleKeydown, eventOptions );
                    } );
                }
            } )();
        </script>
        <?php
    }

    /**
     * Add theme sync script (uses shared module)
     *
     * Previously had ~329 lines of inline JS, now optimized to use shared module.
     *
     * @return void
     */
    public static function addThemeSyncScript()
    {
        ?>
        <script>
            // Wait for shared module to load
            (function waitForThemeSync() {
                if (typeof window.fleekDashThemeSync !== 'undefined') {
                    // Module loaded - setup complete theme sync
                    window.fleekDashThemeSync.setupThemeSync({
                        verbose: false, // Enable for debugging
                        observeParent: true,
                        listenStorage: true,
                        listenMessages: true
                    });
                    // Theme sync initialized
                } else {
                    // Module not loaded yet - wait
                    setTimeout(waitForThemeSync, 50);
                }
            })();
        </script>
        <?php
    }
}
