<?php

declare(strict_types=1);

namespace FleekDash\Core\ResourceBlocker;

use FleekDash\Core\ScriptHandles;

if (!\function_exists('fleekdash_log_debug')) {
    require_once FLEEKDASH_PLUGIN_DIR . 'includes/Core/debug-helpers.php';
}

/**
 * Handles all queue-based protections: removing disallowed handles,
 * disabling concatenation, and purging late enqueues from third-party plugins.
 */
class QueueGuard
{
    private BlockerContext $context;

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

    /**
     * Register filters that prevent WordPress from loading blocked resources.
     */
    public function preventResourceLoading(): void
    {
        \add_filter('style_loader_src', [$this, 'filterStyleLoaderSrc'], 999);
        \add_filter('script_loader_src', [$this, 'filterScriptLoaderSrc'], 999);
        \add_filter('style_loader_tag', [$this, 'filterStyleLoaderTag'], 999, 2);
        \add_filter('script_loader_tag', [$this, 'filterScriptLoaderTag'], 999, 2);
        \add_action('wp_default_scripts', [$this, 'removeDisallowedScripts'], 100);
        \add_action('wp_enqueue_media', [$this, 'dequeueMediaHints'], 999);
    }

    /**
     * @param string|false $src Style source URL from WordPress
     * @return string|false Original src or false to block loading
     */
    public function filterStyleLoaderSrc(string|false $src): string|false
    {
        if ($src === false || !$this->context->shouldBlock()) {
            return $src;
        }
        $blockLoadStyles = strpos($src, 'load-styles.php') !== false;
        return $blockLoadStyles ? false : $src;
    }

    /**
     * @param string|false $src Script source URL from WordPress
     * @return string|false Original src or false to block loading
     */
    public function filterScriptLoaderSrc(string|false $src): string|false
    {
        if ($src === false || !$this->context->shouldBlock()) {
            return $src;
        }
        if ($this->context->shouldBlockThirdParty() && $this->context->isThirdPartyScript($src)) {
            $this->logBlockedScript($src);
            return false;
        }
        $blocked = strpos($src, 'load-scripts.php') !== false || strpos($src, 'media-hints.min.js') !== false;
        return $blocked ? false : $src;
    }

    /**
     * @param string $tag HTML tag for the style
     * @param string $handle Enqueued handle name
     * @return string Original tag or empty string to suppress output
     */
    public function filterStyleLoaderTag(string $tag, string $handle): string
    {
        if (!$this->context->shouldBlock()) {
            return $tag;
        }
        return $this->context->isAllowedStyleHandle($handle) ? $tag : '';
    }

    /**
     * @param string $tag HTML tag for the script
     * @param string $handle Enqueued handle name
     * @return string Original tag or empty string to suppress output
     */
    public function filterScriptLoaderTag(string $tag, string $handle): string
    {
        if (!$this->context->shouldBlock()) {
            return $tag;
        }
        return $this->context->isAllowedScriptHandle($handle) ? $tag : '';
    }

    /**
     * @param \WP_Scripts $scripts WordPress scripts manager
     */
    public function removeDisallowedScripts(\WP_Scripts $scripts): void
    {
        if (!is_admin() || !$this->context->shouldBlock()) {
            return;
        }
        foreach (array_keys($scripts->registered) as $handle) {
            if (!$this->context->isAllowedScriptHandle($handle)) {
                $scripts->remove($handle);
            }
        }
        if (isset($scripts->registered['media-hints'])) {
            $scripts->remove('media-hints');
        }
    }

    public function dequeueMediaHints(): void
    {
        if (!$this->context->shouldBlock()) {
            return;
        }
        \wp_dequeue_script('media-hints');
        \wp_deregister_script('media-hints');
    }

    /**
     * Aggressively dequeues scripts and styles before WordPress renders the page.
     */
    public function dequeueResources(): void
    {
        if (!$this->context->shouldBlock()) {
            return;
        }

        global $wp_styles, $wp_scripts;

        if (is_object($wp_styles)) {
            $this->disableConcatenation($wp_styles);

            $wp_styles->queue = [];
            $wp_styles->to_do = [];

            foreach ($this->context->getAllowedStyles() as $style) {
                \wp_enqueue_style($style);
            }
        }

        if (is_object($wp_scripts)) {
            $this->disableConcatenation($wp_scripts);

            $wp_scripts->queue = [];
            $wp_scripts->to_do = [];

            foreach ($this->context->getAllowedScripts() as $script) {
                \wp_enqueue_script($script);
            }
        }

        \remove_action('wp_print_styles', 'print_emoji_styles');
        \remove_action('admin_print_scripts', 'print_emoji_detection_script');
    }

    /**
     * Filters the print styles array so only allowed handles remain.
     *
     * @param array<string> $styles List of style handles to print
     * @return array<string> Filtered list of allowed handles
     */
    public function filterPrintStyles(array $styles): array
    {
        if (!$this->context->shouldBlock()) {
            return $styles;
        }

        return array_values(array_intersect($styles, $this->context->getAllowedStyles()));
    }

    /**
     * Blocks CartFlows admin_print_styles hook that directly outputs load-styles.php HTML.
     * CartFlows wizard outputs: <link rel='stylesheet' href='...load-styles.php...'>
     */
    public function blockCartFlowsAdminStyles(): void
    {
        if (!$this->context->shouldBlock()) {
            return;
        }

        // Debug: Log that we're blocking CartFlows admin styles
        if (defined('WP_DEBUG') && WP_DEBUG) {
            fleekdash_log_debug('ResourceBlocker: Blocking CartFlows admin_print_styles output');
        }

        // Block any direct output of load-styles.php links
        // by intercepting the output buffer during admin_print_styles
        $this->interceptLoadStylesOutput();
    }

    /**
     * Intercepts and removes any load-styles.php output during admin_print_styles
     */
    private function interceptLoadStylesOutput(): void
    {
        ob_start([$this, 'stripLoadStylesFromBuffer']);

        add_action('admin_print_styles', function () {
            if (ob_get_level() > 0) {
                ob_end_flush();
            }
        }, PHP_INT_MAX);
    }

    /**
     * Output buffer callback: removes load-styles.php links from HTML.
     * Must return string. On preg_replace failure or exception, returns original buffer.
     *
     * @param string $buffer Captured output
     * @return string Processed buffer (never empty, fallback to original on error)
     */
    private function stripLoadStylesFromBuffer(string $buffer): string
    {
        try {
            $filtered = preg_replace(
                '/<link[^>]*href=["\'][^"\']*load-styles\.php[^"\']*["\'][^>]*>/i',
                '',
                $buffer
            );

            return $filtered ?? $buffer;
        } catch (\Throwable $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                fleekdash_log_debug('ResourceBlocker: output buffer callback error: ' . $e->getMessage());
            }

            return $buffer;
        }
    }

    /**
     * Final safety net executed after all plugins enqueue their assets.
     * Removes any disallowed handles that slipped in later in the lifecycle.
     */
    public function purgeLateQueuedResources(): void
    {
        if (!$this->context->shouldBlock()) {
            return;
        }

        global $wp_styles, $wp_scripts;

        if ($wp_scripts instanceof \WP_Scripts) {
            foreach (ScriptHandles::WORDPRESS_REACT as $handle) {
                if (isset($wp_scripts->registered[$handle])) {
                    $wp_scripts->remove($handle);
                }
            }

            $this->purgeManager($wp_scripts, $this->context->getAllowedScripts());
        }

        if ($wp_styles instanceof \WP_Styles) {
            $this->purgeManager($wp_styles, $this->context->getAllowedStyles());
        }
    }

    /**
     * @param \WP_Scripts|\WP_Styles $manager WordPress scripts or styles manager
     * @param array<string> $allowedHandles Whitelisted handles to keep
     */
    private function purgeManager(\WP_Scripts|\WP_Styles $manager, array $allowedHandles): void
    {
        $allowedHandles = $this->augmentAllowedHandles($allowedHandles, $manager);
        $this->disableConcatenation($manager);

        $allowedLookup = array_flip($allowedHandles);

        $queues = ['queue', 'to_do', 'done'];
        foreach ($queues as $property) {
            if (!isset($manager->{$property}) || !is_array($manager->{$property})) {
                continue;
            }

            $manager->{$property} = array_values(array_filter(
                $manager->{$property},
                static function ($handle) use ($allowedLookup) {
                    return isset($allowedLookup[$handle]);
                }
            ));
        }

        if (isset($manager->registered) && is_array($manager->registered)) {
            foreach (array_keys($manager->registered) as $handle) {
                if (!isset($allowedLookup[$handle])) {
                    unset($manager->registered[$handle]);
                }
            }
        }

        foreach ($allowedHandles as $handle) {
            if ($manager instanceof \WP_Scripts) {
                \wp_enqueue_script($handle);
            } elseif ($manager instanceof \WP_Styles) {
                \wp_enqueue_style($handle);
            }
        }
    }

    /**
     * Ensure platform-required handles (like vite-client in dev) stay whitelisted.
     *
     * @param array<string> $handles Base allowed handles
     * @param \WP_Scripts|\WP_Styles $manager WordPress scripts or styles manager
     * @return array<string>
     */
    private function augmentAllowedHandles(array $handles, \WP_Scripts|\WP_Styles $manager): array
    {
        if ($manager instanceof \WP_Scripts) {
            $handles[] = 'vite-client';
        }

        return array_values(array_unique($handles));
    }

    /**
     * @param \WP_Scripts|\WP_Styles $manager WordPress scripts or styles manager
     */
    private function disableConcatenation(\WP_Scripts|\WP_Styles $manager): void
    {
        if (isset($manager->concat)) {
            $manager->concat = false;
        }

        if (isset($manager->do_concat)) {
            $manager->do_concat = false;
        }
    }

    private function logBlockedScript(string $src): void
    {
        if (!defined('WP_DEBUG') || !WP_DEBUG) {
            return;
        }

        static $blockedScriptsLogged = [];
        static $blockedCount = 0;

        $domain = parse_url($src, PHP_URL_HOST) ?? $src;

        if ($blockedCount >= 5 || isset($blockedScriptsLogged[$domain])) {
            return;
        }

        fleekdash_log_debug('Blocking third party script: ' . $src);
        $blockedScriptsLogged[$domain] = true;
        $blockedCount++;
    }
}
