<?php

declare(strict_types=1);

namespace FleekDash\Core\ResourceBlocker;

/**
 * Responsible for inline-script protection, DOM sanitisation and aggressive
 * removal of external hooks that try to inject scripts into the FleekDash page.
 */
class InlineGuard
{
    private BlockerContext $context;

    public function __construct(BlockerContext $context)
    {
        $this->context = $context;
    }

    public function blockInlineScripts(): void
    {
        if (!$this->context->shouldBlock()) {
            return;
        }

        // Reserved for future inline blocking logic.
    }

    public function startContentFiltering(): void
    {
        if (!$this->context->shouldBlock()) {
            return;
        }

        ob_start([$this, 'filterContent']);
    }

    public function stopContentFiltering(): void
    {
        if (!$this->context->shouldBlock()) {
            return;
        }

        if (ob_get_level()) {
            ob_end_flush();
        }
    }

    public function filterContent($content)
    {
        // Early return if $content is not a valid string (ob_get_contents can return false)
        if (!is_string($content) || $content === '') {
            return $content;
        }

        if (!$this->context->shouldBlock()) {
            return $content;
        }

        $blockedPlugins = [
            'w3tc', 'w3-total-cache', 'wp-rocket', 'litespeed', 'autoptimize',
            'wp-super-cache', 'wp-fastest-cache', 'cache-enabler', 'comet-cache',
            'swift-performance', 'nitropack', 'cloudflare', 'keycdn', 'rankmath',
            'rank-math', 'yoast', 'yoast-seo', 'elementor', 'divi',
        ];

        foreach ($blockedPlugins as $plugin) {
            $patterns = [
                '/<script[^>]*>.*?' . preg_quote($plugin, '/') . '.*?<\/script>/is',
                '/<script[^>]*>.*?jQuery\([^)]*\)\.ready.*?<\/script>/is',
                '/<script[^>]*>.*?\$\([^)]*\)\.ready.*?<\/script>/is',
                '/<script[^>]*>.*?toplevel_page_' . preg_quote($plugin, '/') . '.*?<\/script>/is',
            ];

            foreach ($patterns as $pattern) {
                $content = preg_replace($pattern, '', $content);
            }
        }

        $jqueryPatterns = [
            '/<script[^>]*>.*?jQuery\([^)]*\)\.ready.*?<\/script>/is',
            '/<script[^>]*>.*?\$\([^)]*\)\.ready.*?<\/script>/is',
            '/<script[^>]*>.*?toplevel_page_.*?<\/script>/is',
            '/<script[^>]*>.*?\.prop\([^)]*\)\.prop\([^)]*\).*?<\/script>/is',
            '/<script[^>]*>.*?w3tc_dashboard.*?<\/script>/is',
            '/<script[^>]*>.*?w3-edge\.com.*?<\/script>/is',
            '/<script[^>]*>.*?api\.w3-edge\.com.*?<\/script>/is',
        ];

        foreach ($jqueryPatterns as $pattern) {
            $content = preg_replace($pattern, '', $content);
        }

        $specificPatterns = [
            '/<script[^>]*>.*?jQuery\(document\)\.ready\(function\(\$\)\s*\{[^}]*toplevel_page_w3tc_dashboard[^}]*\}.*?<\/script>/is',
            '/<script[^>]*>.*?jQuery\(document\)\.ready\(function\(\$\)\s*\{[^}]*toplevel_page_[^}]*\}.*?<\/script>/is',
            '/<script[^>]*>.*?\.prop\([^)]*target[^)]*\)_blank[^}]*<\/script>/is',
        ];

        foreach ($specificPatterns as $pattern) {
            $content = preg_replace($pattern, '', $content);
        }

        return $content;
    }

    public function implementComprehensiveBlocking(): void
    {
        if (!$this->context->shouldBlock()) {
            return;
        }

        $this->removeAllExternalPluginHooks();
        $this->addComprehensiveScriptBlocker();
    }

    public function addJQueryPolyfill(): void
    {
        if (!$this->context->shouldBlock()) {
            return;
        }

        echo '<script>
            (function() {
                if (typeof window.jQuery !== "undefined" || typeof window.$ !== "undefined") {
                    return;
                }

                var jqueryProxy = function() {
                    return {
                        ready: function(callback) {
                            if (typeof callback === "function") {
                                if (document.readyState === "complete" || document.readyState === "interactive") {
                                    callback(window.jQuery);
                                } else {
                                    document.addEventListener("DOMContentLoaded", function() {
                                        callback(window.jQuery);
                                    });
                                }
                            }
                            return this;
                        },
                        on: function() { return this; },
                        trigger: function() { return this; },
                        ajax: function() {
                            console.warn("[FleekDash] jQuery.ajax is disabled inside FleekDash main app.");
                            return this;
                        },
                        each: function(collection, callback) {
                            if (!collection || !callback) {
                                return this;
                            }
                            var length = collection.length || 0;
                            for (var index = 0; index < length; index++) {
                                callback.call(collection[index], index, collection[index]);
                            }
                            return this;
                        },
                        /** Graceful no-op for Elementor\'s custom helpers */
                        pseudos: {}
                    };
                };

                window.jQuery = jqueryProxy();
                window.$ = window.jQuery;
            })();
        </script>';
    }

    private function removeAllExternalPluginHooks(): void
    {
        $externalHooks = [
            'w3tc_admin_head', 'w3tc_admin_footer', 'w3tc_admin_print_scripts', 'w3tc_admin_print_footer_scripts',
            'rank_math_admin_head', 'rank_math_admin_footer', 'rank_math_admin_print_scripts',
            'wpseo_admin_head', 'wpseo_admin_footer', 'wpseo_admin_print_scripts',
            'elementor/admin/head', 'elementor/admin/footer',
            'wp_rocket_admin_head', 'wp_rocket_admin_footer',
            'litespeed_admin_head', 'litespeed_admin_footer',
            'autoptimize_admin_head', 'autoptimize_admin_footer',
        ];

        foreach ($externalHooks as $hook) {
            \remove_action('admin_head', $hook);
            \remove_action('admin_footer', $hook);
            \remove_action('admin_print_scripts', $hook);
            \remove_action('admin_print_footer_scripts', $hook);
        }

        global $wp_filter;
        $hooksToClean = ['admin_head', 'admin_footer', 'admin_print_scripts', 'admin_print_footer_scripts'];

        foreach ($hooksToClean as $hook) {
            if (!isset($wp_filter[$hook])) {
                continue;
            }

            foreach ($wp_filter[$hook]->callbacks as $priority => $callbacks) {
                foreach ($callbacks as $callback) {
                    if (
                        is_array($callback['function']) &&
                        is_object($callback['function'][0]) &&
                        $this->isExternalPluginClass(get_class($callback['function'][0]))
                    ) {
                        \remove_action($hook, $callback['function'], $priority);
                        continue;
                    }

                    if (
                        is_string($callback['function']) &&
                        $this->isExternalPluginFunction($callback['function'])
                    ) {
                        \remove_action($hook, $callback['function'], $priority);
                    }
                }
            }
        }
    }

    private function isExternalPluginClass(string $className): bool
    {
        $externalPlugins = [
            'W3TC', 'WpRocket', 'LiteSpeed', 'Autoptimize', 'RankMath', 'Yoast',
            'Elementor', 'Divi', 'WP_Super_Cache', 'Cache_Enabler', 'Comet_Cache',
            'Swift_Performance', 'Nitropack', 'Cloudflare', 'KeyCDN',
        ];

        foreach ($externalPlugins as $plugin) {
            if (strpos($className, $plugin) !== false) {
                return true;
            }
        }

        return false;
    }

    private function isExternalPluginFunction(string $functionName): bool
    {
        $externalFunctions = [
            'w3tc_', 'wp_rocket_', 'litespeed_', 'autoptimize_', 'rank_math_',
            'wpseo_', 'elementor_', 'divi_', 'wp_super_cache_', 'cache_enabler_',
            'comet_cache_', 'swift_performance_', 'nitropack_', 'cloudflare_', 'keycdn_',
        ];

        foreach ($externalFunctions as $prefix) {
            if (strpos($functionName, $prefix) === 0) {
                return true;
            }
        }

        return false;
    }

    private function addComprehensiveScriptBlocker(): void
    {
        $allowedSrcPatterns = array_merge(
            [
                'fleekdash',
                '@vite/client',
                '/src/admin/',
                '/src/__app-components__/',
                'react',
                'react-dom',
            ],
            array_map('esc_js', $this->context->getCustomAllowedPaths())
        );

        $allowedInlinePatterns = [
            'window.fleekdash',
            'window.fleekDash',
            'wpcolorscheme',
            'document.body.classname',
            'customize-support',
            'window.history.replacestate',
            'wponload',
            'window.jquery',
            'window.$',
            'jqueryproxy',
        ];

        $blockedInlinePatterns = [
            'jquery(',
            '$(',
            'elementor',
            'rank_math',
            'rankmath',
            'yoast',
            'wpseo',
            'w3tc',
            'autoptimize',
            'nitropack',
            'litespeed',
        ];

        echo '<script>
            (function() {
                const allowedSrcPatterns = ' . json_encode(array_values(array_unique($allowedSrcPatterns))) . ';
                const allowedInlinePatterns = ' . json_encode($allowedInlinePatterns) . ';
                const blockedInlinePatterns = ' . json_encode($blockedInlinePatterns) . ';

                function includesPattern(value, patterns) {
                    const lowerValue = (value || "").toLowerCase();
                    return patterns.some(function(pattern) {
                        return lowerValue.includes(pattern.toLowerCase());
                    });
                }

                function isAllowedScript(scriptContent, src) {
                    if (src && includesPattern(src, allowedSrcPatterns)) {
                        return true;
                    }

                    if (!scriptContent || scriptContent.trim() === "") {
                        return true;
                    }

                    if (includesPattern(scriptContent, allowedInlinePatterns)) {
                        return true;
                    }

                    if (includesPattern(scriptContent, blockedInlinePatterns)) {
                        return false;
                    }

                    return true;
                }

                const originalCreateElement = document.createElement;
                document.createElement = function(tagName) {
                    const element = originalCreateElement.call(document, tagName);

                    if (tagName && tagName.toLowerCase() === "script") {
                        const originalSetAttribute = element.setAttribute;
                        const originalAppendChild = element.appendChild;
                        const originalInnerHTML = Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML");

                        element.setAttribute = function(name, value) {
                            if (name === "src" && value && !isAllowedScript("", value)) {
                                console.debug("[FleekDash] Blocked external script:", value);
                                return;
                            }
                            return originalSetAttribute.call(this, name, value);
                        };

                        element.appendChild = function(child) {
                            try {
                                if (
                                    child &&
                                    child.tagName &&
                                    child.tagName.toLowerCase() === "script"
                                ) {
                                    const nestedSrc = child.getAttribute("src");
                                    const nestedContent = child.innerHTML || "";

                                    if (!isAllowedScript(nestedContent, nestedSrc)) {
                                    fleekdash.logDebug && fleekdash.logDebug("[ResourceBlocker] appendChild blocked:", nestedSrc || nestedContent);
                                        return child;
                                    }
                                }
                            } catch (error) {
                            fleekdash.logDebug && fleekdash.logDebug("[ResourceBlocker] appendChild guard error:", error);
                            }

                            return originalAppendChild.call(this, child);
                        };

                        if (originalInnerHTML && originalInnerHTML.set && originalInnerHTML.get) {
                            Object.defineProperty(element, "innerHTML", {
                                set: function(value) {
                                    if (!isAllowedScript(value || "", null)) {
                                        if (window.FleekDash?.debug) {
                                            console.debug("[FleekDash] Blocked inline script:", value);
                                        }
                                        return value;
                                    }
                                    return originalInnerHTML.set.call(this, value);
                                },
                                get: function() {
                                    return originalInnerHTML.get.call(this);
                                }
                            });
                        }
                    }

                    return element;
                };

                const observer = new MutationObserver(function(mutations) {
                    mutations.forEach(function(mutation) {
                        mutation.addedNodes.forEach(function(node) {
                            if (
                                node.nodeType === Node.ELEMENT_NODE &&
                                node.tagName &&
                                node.tagName.toLowerCase() === "script"
                            ) {
                                const content = node.innerHTML || "";
                                const src = node.getAttribute && node.getAttribute("src");

                                if (!isAllowedScript(content, src)) {
                                    console.debug("[FleekDash] Removing injected script:", src || content);
                                    node.parentNode && node.parentNode.removeChild(node);
                                }
                            }
                        });
                    });
                });

                observer.observe(document.documentElement || document.body, {
                    childList: true,
                    subtree: true
                });
            })();
        </script>';
    }
}
