<?php

namespace _121Digital\Connect\Admin\Services;

/**
 * Notice Service - Centralized admin notice management
 *
 * Blocks all third-party plugin notices except those explicitly allowed.
 * Uses multiple strategies to catch notices regardless of implementation method.
 */
class NoticeService
{
    private static ?NoticeService $instance = null;

    private array $allowedPlugins = [];

    /**
     * Custom notice queue for our plugin
     */
    private static array $customNotices = [];

    /**
     * Track if register has been called to prevent duplicate registrations
     */
    private static bool $registered = false;

    public static function getInstance(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Add a custom notice for our plugin
     *
     * @param string $message The notice message
     * @param string $type Notice type: 'success', 'warning', 'error', 'info'
     * @param bool $dismissible Whether the notice is dismissible
     * @param string $id Optional notice ID for targeting
     * @return void
     */
    public static function addNotice(string $message, string $type = 'info', bool $dismissible = true, string $id = ''): void
    {
        // Ensure the instance exists to maintain singleton pattern
        self::getInstance();

        self::$customNotices[] = [
            'message' => $message,
            'type' => $type,
            'dismissible' => $dismissible,
            'id' => $id ?: 'notice-' . uniqid(),
        ];

        // Debug: Log when notices are added (remove in production if needed)
        if (defined('WP_DEBUG') && WP_DEBUG && defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) {
            error_log('NoticeService: Added ' . $type . ' notice: ' . substr(strip_tags($message), 0, 100));
        }
    }

    /**
     * Add a success notice
     */
    public static function addSuccess(string $message, bool $dismissible = true, string $id = ''): void
    {
        self::addNotice($message, 'success', $dismissible, $id);
    }

    /**
     * Add an error notice
     */
    public static function addError(string $message, bool $dismissible = true, string $id = ''): void
    {
        self::addNotice($message, 'error', $dismissible, $id);
    }

    /**
     * Add a warning notice
     */
    public static function addWarning(string $message, bool $dismissible = true, string $id = ''): void
    {
        self::addNotice($message, 'warning', $dismissible, $id);
    }

    /**
     * Add an info notice
     */
    public static function addInfo(string $message, bool $dismissible = true, string $id = ''): void
    {
        self::addNotice($message, 'info', $dismissible, $id);
    }

    public function register(): void
    {
        // Prevent duplicate registrations
        if (self::$registered) {
            return;
        }

        self::$registered = true;

        // Allow specific plugins and themes
        $this->allowedPlugins = [
            '121_conenct_build', // Our own plugin
            'elementor',
            'builderpro',
            'reg121-builderpro-base-1761338251', // Theme
        ];

        // Register our custom notice rendering system LAST (lowest priority)
        // This ensures all notices are added to the queue before we render
        add_action('admin_notices', [$this, 'renderCustomNotices'], 999); // Low priority to render AFTER all notices are added

        // Multiple strategies to block notices - run at lower priority to allow our plugin to register first
        add_action('admin_init', [$this, 'blockThirdPartyNotices'], 99);
        add_action('admin_head', [$this, 'injectNoticeBlockingCSS'], 999);
        add_action('admin_footer', [$this, 'injectNoticeBlockingJS'], 999);
        // DISABLED: Output buffering was causing layout issues
        // add_action('admin_init', [$this, 'startOutputBuffering'], 1);
        // add_action('shutdown', [$this, 'endOutputBuffering'], 999);
    }

    /**
     * Render our custom notices with protected CSS classes
     */
    public function renderCustomNotices(): void
    {
        // Debug: Log if notices are being rendered (remove in production if needed)
        if (defined('WP_DEBUG') && WP_DEBUG && defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) {
            error_log('NoticeService: renderCustomNotices called with ' . count(self::$customNotices) . ' notices queued');
        }

        if (empty(self::$customNotices)) {
            return;
        }

        foreach (self::$customNotices as $notice) {
            $classes = [
                'notice',
                'connect-121-notice', // Protected class that's never filtered
                'notice-' . $notice['type']
            ];

            if ($notice['dismissible']) {
                $classes[] = 'is-dismissible';
            }

            $classString = implode(' ', $classes);
            $id = !empty($notice['id']) ? ' id="' . esc_attr($notice['id']) . '"' : '';

            echo '<div class="' . esc_attr($classString) . '"' . $id . '>';
            echo '<p>' . wp_kses_post($notice['message']) . '</p>';
            echo '</div>';
        }

        // Clear notices after rendering
        self::$customNotices = [];
    }

    /**
     * Start output buffering to catch all notices
     */
    public function startOutputBuffering(): void
    {
        if (is_admin() && !wp_doing_ajax() && ob_get_level() === 0) {
            ob_start([$this, 'filterAdminOutput']);
        }
    }

    /**
     * End output buffering
     */
    public function endOutputBuffering(): void
    {
        if (ob_get_level() > 0) {
            ob_end_flush();
        }
    }

    /**
     * Filter admin page output to remove plugin notices
     */
    public function filterAdminOutput(string $content): string
    {
        // Generic patterns that match most plugin notices
        $patterns = [
            // Common notice div patterns
            '/<div[^>]*class="[^"]*notice[^"]*"[^>]*>.*?<\/div>/is',
            '/<div[^>]*class="[^"]*updated[^"]*"[^>]*>.*?<\/div>/is',
            '/<div[^>]*class="[^"]*error[^"]*"[^>]*>.*?<\/div>/is',
            '/<div[^>]*class="[^"]*warning[^"]*"[^>]*>.*?<\/div>/is',

            // Plugin-specific patterns (generic)
            '/<div[^>]*id="[^"]*notice[^"]*"[^>]*>.*?<\/div>/is',
            '/<div[^>]*class="[^"]*plugin-[^"]*notice[^"]*"[^>]*>.*?<\/div>/is',

            // Dismissible notices
            '/<div[^>]*class="[^"]*is-dismissible[^"]*"[^>]*>.*?<\/div>/is',

            // Common plugin notice containers
            '/<div[^>]*class="[^"]*wrap[^"]*"[^>]*>\s*<div[^>]*class="[^"]*notice[^"]*"[^>]*>.*?<\/div>\s*<\/div>/is',

            // Aggressive wpmet patterns
            '/<div[^>]*class="[^"]*button-container[^"]*notice-vert-space[^"]*"[^>]*>.*?<\/div>/is',
            '/<div[^>]*id="[^"]*metform[^"]*"[^>]*>.*?<\/div>/is',
            '/<div[^>]*class="[^"]*wpmet[^"]*"[^>]*>.*?<\/div>/is',
            '/<a[^>]*id="[^"]*metform[^"]*"[^>]*>.*?<\/a>/is',
            '/<a[^>]*class="[^"]*wpmet-notice-button[^"]*"[^>]*>.*?<\/a>/is',
        ];

        foreach ($patterns as $pattern) {
            $content = preg_replace_callback($pattern, [$this, 'filterNoticeCallback'], $content);
        }

        return $content;
    }

    /**
     * Check if a notice should be kept or removed
     */
    private function filterNoticeCallback(array $matches): string
    {
        $notice = $matches[0];

        // ALWAYS keep our custom notices with protected class
        if (stripos($notice, 'connect-121-notice') !== false) {
            return $notice;
        }

        // ALWAYS keep our own plugin notices (legacy support)
        if (stripos($notice, '121_conenct_build') !== false ||
            stripos($notice, '121Connect') !== false ||
            stripos($notice, '121Digital') !== false) {
            return $notice;
        }

        // Keep WordPress core notices
        if ($this->isWordPressCore($notice)) {
            return $notice;
        }

        // Keep allowed plugin notices
        foreach ($this->allowedPlugins as $plugin) {
            if ($plugin !== '121_conenct_build' && stripos($notice, $plugin) !== false) {
                return $notice;
            }
        }

        // Keep certain core WordPress notices
        $coreNoticePatterns = [
            'wp-core-ui',
            'settings-error',
            'update-nag',
            'wordpress',
            'wp-admin',
        ];

        foreach ($coreNoticePatterns as $pattern) {
            if (stripos($notice, $pattern) !== false) {
                return $notice;
            }
        }

        // Remove everything else
        return '';
    }

    /**
     * Check if notice is from WordPress core
     */
    private function isWordPressCore(string $notice): bool
    {
        // WordPress core notice indicators
        $coreIndicators = [
            'settings-error',
            'update-nag',
            'wp-core-ui',
            'notice-success',
            'notice-info',
            'notice-warning',
            'notice-error',
        ];

        // Check if it contains plugin-specific classes/IDs that indicate third-party
        $pluginIndicators = [
            'plugin-',
            'wpmet',
            'royal-addons',
            'wpr-',
            'elementor-', // Will be filtered by allowedPlugins
            'yoast',
            'woocommerce',
        ];

        // If it has plugin indicators, it's not core
        foreach ($pluginIndicators as $indicator) {
            if (stripos($notice, $indicator) !== false) {
                return false;
            }
        }

        // If it has core indicators, it's probably core
        foreach ($coreIndicators as $indicator) {
            if (stripos($notice, $indicator) !== false) {
                return true;
            }
        }

        // Default to false for unknown notices
        return false;
    }

    /**
     * Inject CSS to hide notices immediately
     */
    public function injectNoticeBlockingCSS(): void
    {
        echo '<style>
        /* NEVER hide our custom notices */
        .connect-121-notice {
            display: block !important;
        }
        
        /* Hide common plugin notice patterns */
        .notice[class*="plugin-"]:not([class*="121"]):not(.connect-121-notice),
        .notice[class*="wpmet"]:not(.connect-121-notice),
        .notice[class*="royal"]:not(.connect-121-notice),
        .notice[class*="wpr-"]:not(.connect-121-notice),
        .notice[id*="plugin"]:not([id*="121"]):not(.connect-121-notice),
        .notice[id*="wpmet"]:not(.connect-121-notice),
        div[class*="wpmet-notice"]:not(.connect-121-notice),
        div[id*="wpmet-notice"]:not(.connect-121-notice),
        div[class*="button-container notice-vert-space"]:not(.connect-121-notice),
        a[class*="wpmet-notice-button"]:not(.connect-121-notice),
        a[id*="metform_btn"]:not(.connect-121-notice),
        .wrap > .notice:not([class*="settings-error"]):not([class*="update-nag"]):not([class*="121"]):not(.connect-121-notice),
        .notice.is-dismissible:not([class*="elementor"]):not([class*="wordpress"]):not([class*="builderpro"]):not([class*="121"]):not(.connect-121-notice) {
            display: none !important;
        }
        
        /* Show allowed plugin notices and our own */
        .notice[class*="elementor"],
        .notice[class*="builderpro"],
        .notice[class*="121"],
        .notice[class*="121Digital"],
        .notice[class*="121Connect"],
        .connect-121-notice,
        .settings-error,
        .update-nag {
            display: block !important;
        }
        </style>';
    }

    /**
     * Inject JavaScript to remove notices after page load
     */
    public function injectNoticeBlockingJS(): void
    {
        echo '<script>
        jQuery(document).ready(function($) {
            // Remove plugin notices that made it through
            var pluginNoticeSelectors = [
                "div[class*=\"wpmet\"]:not([class*=\"121\"]):not([id*=\"121\"])",
                "div[id*=\"wpmet\"]:not([class*=\"121\"]):not([id*=\"121\"])", 
                "div[class*=\"plugin-notice\"]:not([class*=\"121\"]):not([id*=\"121\"])",
                "div[class*=\"royal\"]:not([class*=\"121\"]):not([id*=\"121\"])",
                "div[class*=\"wpr-\"]:not([class*=\"121\"]):not([id*=\"121\"])",
                "div[class*=\"button-container notice-vert-space\"]",
                "a[class*=\"wpmet-notice-button\"]",
                "a[id*=\"metform_btn\"]",
                ".notice:not([class*=\"elementor\"]):not([class*=\"builderpro\"]):not([class*=\"121\"]):not([class*=\"settings-error\"]):not([class*=\"update-nag\"])"
            ];
            
            pluginNoticeSelectors.forEach(function(selector) {
                try {
                    $(selector).remove();
                } catch(e) {
                    // Ignore selector errors
                }
            });
            
            // Monitor for dynamically added notices
            var observer = new MutationObserver(function(mutations) {
                mutations.forEach(function(mutation) {
                    mutation.addedNodes.forEach(function(node) {
                        if (node.nodeType === 1) { // Element node
                            var $node = $(node);
                            
                            // NEVER remove our custom notices
                            if ($node.hasClass("connect-121-notice")) {
                                return;
                            }
                            
                            // Remove wpmet specific elements
                            if ($node.hasClass("button-container") && $node.hasClass("notice-vert-space")) {
                                $node.remove();
                                return;
                            }
                            
                            // Check for our plugin notices and preserve them
                            if ($node.is("[class*=\"121\"],[id*=\"121\"]")) {
                                return; // Keep our plugin notices
                            }
                            
                            // Remove other plugin notices
                            if ($node.hasClass("notice") && !$node.is("[class*=\"elementor\"], [class*=\"builderpro\"], [class*=\"121\"], [class*=\"settings-error\"], [class*=\"update-nag\"], .connect-121-notice")) {
                                $node.remove();
                            }
                        }
                    });
                });
            });
            
            // Observe the admin area for dynamic notices
            if (document.querySelector(".wrap") || document.querySelector("#wpbody-content")) {
                observer.observe(document.querySelector(".wrap") || document.querySelector("#wpbody-content"), {
                    childList: true,
                    subtree: true
                });
            }
        });
        </script>';
    }

    /**
     * Block all third-party notices except WordPress core and allowed plugins
     */
    public function blockThirdPartyNotices(): void
    {
        global $wp_filter;

        $noticeHooks = ['admin_notices', 'network_admin_notices', 'all_admin_notices'];

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

            foreach ($wp_filter[$hook]->callbacks as $priority => $callbacks) {
                $toRemove = [];

                foreach ($callbacks as $key => $callback) {
                    $function = $callback['function'] ?? null;

                    if (!$function) {
                        continue;
                    }

                    $pluginSlug = $this->getCallbackSource($function);

                    // Always allow our own plugin notices
                    if ($pluginSlug === '121_conenct_build') {
                        continue;
                    }

                    // Allow if no slug (WordPress core) or if in allowed list
                    if (!$pluginSlug || in_array($pluginSlug, $this->allowedPlugins, true)) {
                        continue;
                    }

                    // Block this notice
                    $toRemove[] = $key;
                }

                // Remove blocked notices
                foreach ($toRemove as $key) {
                    unset($wp_filter[$hook]->callbacks[$priority][$key]);
                }
            }
        }
    }

    private function getCallbackSource($callback): ?string
    {
        try {
            if (is_string($callback)) {
                if (!function_exists($callback)) {
                    return null;
                }
                $reflection = new \ReflectionFunction($callback);
                $file = $reflection->getFileName();
            } elseif (is_array($callback) && isset($callback[0], $callback[1])) {
                $class = $callback[0];

                if (is_object($class)) {
                    // Check if this is our plugin class by namespace
                    $className = get_class($class);
                    if (strpos($className, '_121Digital\\Connect\\') === 0) {
                        return '121_conenct_build'; // Always identify our plugin
                    }
                    $reflection = new \ReflectionClass($className);
                } elseif (is_string($class)) {
                    // Check if this is our plugin class by namespace
                    if (strpos($class, '_121Digital\\Connect\\') === 0) {
                        return '121_conenct_build'; // Always identify our plugin
                    }
                    if (!class_exists($class)) {
                        return null;
                    }
                    $reflection = new \ReflectionClass($class);
                } else {
                    return null;
                }
                $file = $reflection->getFileName();
            } elseif (is_object($callback) && method_exists($callback, '__invoke')) {
                $className = get_class($callback);
                if (strpos($className, '_121Digital\\Connect\\') === 0) {
                    return '121_conenct_build'; // Always identify our plugin
                }
                $reflection = new \ReflectionClass($className);
                $file = $reflection->getFileName();
            } else {
                return null;
            }

            // Also check file path to identify our plugin
            if (isset($file) && strpos($file, '121_conenct_build') !== false) {
                return '121_conenct_build';
            }

            return isset($file) ? $this->extractPluginSlug($file) : null;
        } catch (\Exception $e) {
            return null;
        }
    }

    private function extractPluginSlug(string $file): ?string
    {
        // Check if file is in plugins directory
        if (strpos($file, WP_PLUGIN_DIR . '/') !== false) {
            $relative = str_replace(WP_PLUGIN_DIR . '/', '', $file);
            $parts = explode('/', $relative);
            return $parts[0] ?? null;
        }

        // Check if file is in theme directory (for theme notices)
        if (strpos($file, get_theme_root() . '/') !== false) {
            $relative = str_replace(get_theme_root() . '/', '', $file);
            $parts = explode('/', $relative);
            return $parts[0] ?? null;
        }

        return null;
    }
}
