<?php

declare(strict_types=1);

namespace FleekDash\Admin;

/**
 * Admin Bar Customizer
 *
 * Customizes WordPress admin bar to show FleekDash branding
 * instead of WordPress logo.
 *
 * SINGLETON PATTERN: Prevents duplicate hook registration when called
 * from both admin (Dashboard.php) and frontend (Assets.php) contexts.
 *
 * @package FleekDash\Admin
 * @since 2.2.0
 */
class AdminBarCustomizer
{
    /**
     * Singleton instance
     *
     * @var AdminBarCustomizer|null
     */
    private static $instance = null;

    /**
     * Static flag to prevent multiple hook registrations across all instances
     * This ensures hooks are only registered once, even if bootstrap() is called
     * from multiple contexts (admin and frontend).
     *
     * @var bool
     */
    private static $hooksRegistered = false;

    /**
     * Position hooks registered independently so they run on all pages
     * including FleekDash React app (where logo hooks are skipped).
     *
     * @var bool
     */
    private static $positionHooksRegistered = false;

    /**
     * Instance flag to track if this instance has been bootstrapped
     *
     * @var bool
     */
    private $bootstrapped = false;

    /**
     * Private constructor to prevent direct instantiation
     */
    private function __construct()
    {
        // Private constructor for singleton
    }

    /**
     * Get singleton instance
     *
     * @return self
     */
    public static function getInstance(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Prevent cloning
     */
    private function __clone()
    {
        // Prevent cloning
    }

    /**
     * Prevent unserialization
     */
    public function __wakeup(): void
    {
        throw new CannotUnserializeSingletonException('Cannot unserialize singleton');
    }

    /**
     * Bootstrap the customizer
     *
     * This method can be safely called from multiple contexts (admin/frontend)
     * but hooks will only be registered once thanks to static flag.
     *
     * Static flag check moved FIRST to prevent duplicate hook registration
     * when called from both admin (Dashboard.php) and frontend (Assets.php) contexts.
     * Instance flag is only used for performance optimization, not to block hook registration.
     *
     * @return void
     */
    public function bootstrap(): void
    {
        // Position hooks: ALWAYS register when admin bar could show (independent of logo/styles)
        // This ensures "position at bottom" works on ALL admin pages including FleekDash React app
        // where Mount removes admin_footer - we use admin_print_footer_scripts instead
        if (!self::$positionHooksRegistered) {
            self::$positionHooksRegistered = true;
            \add_filter('admin_body_class', [$this, 'addPositionBodyClass']);
            \add_filter('body_class', [$this, 'addPositionBodyClass']);
            // admin_print_footer_scripts runs on FleekDash page (Mount's admin_footer calls it)
            \add_action('admin_print_footer_scripts', [$this, 'addPositionStyles'], 999);
            \add_action('wp_head', [$this, 'addPositionStyles'], 999);
            \add_action('wp_footer', [$this, 'addPositionStyles'], 999);
        }

        // CRITICAL: Check static flag to prevent duplicate hook registration
        if (self::$hooksRegistered) {
            return;
        }

        // Check if we should add logo (always, except in React app)
        $shouldAddLogo = $this->shouldAddLogo();
        // Check if we should apply custom styles (only in modern mode)
        $shouldApplyStyles = $this->shouldCustomizeAdminBar();

        if (!$shouldAddLogo && !$shouldApplyStyles) {
            $this->bootstrapped = true;
            return;
        }

        self::$hooksRegistered = true;
        $this->bootstrapped = true;

        // Always remove WordPress logo when adding our logo
        if ($shouldAddLogo) {
            \add_action('admin_bar_menu', [$this, 'removeWordpressLogo'], 999);
            \add_action('admin_bar_menu', [$this, 'addFleekdashLogo'], 1);
        }

        // Add auto-hide functionality only on frontend and only in modern mode
        if ($shouldAddLogo && !\is_admin() && $shouldApplyStyles) {
            $autoHide = new AdminBarAutoHide();
            \add_action('wp_head', [$autoHide, 'render'], 999);
        }

        // Only add custom styles if in modern mode
        if ($shouldApplyStyles) {
            $modernStyles = new AdminBarModernStyles();
            \add_action('admin_head', [$modernStyles, 'render'], 999);
            \add_action('wp_head', [$modernStyles, 'render'], 999);
        } elseif ($shouldAddLogo) {
            \add_action('admin_head', [$this, 'addClassicLogoStyles'], 999);
            \add_action('wp_head', [$this, 'addClassicLogoStyles'], 999);
        }

        \add_action('wp_footer', [$this, 'debugAdminBar'], 999);
    }

    /**
     * Add body class for admin bar bottom position
     *
     * @param string|string[] $classes Existing classes (string for admin, array for frontend)
     * @return string|string[] Modified classes
     */
    public function addPositionBodyClass($classes)
    {
        $shouldSkip = !\is_user_logged_in()
            || !\is_admin_bar_showing()
            || \get_option('fleekdash_admin_bar_style', 'modern') === 'classic';

        if ($shouldSkip) {
            return $classes;
        }

        $positionAtBottom = (bool) \get_option('fleekdash_admin_bar_position_bottom', false);
        $themeColor = (string) \get_option('fleekdash_admin_bar_theme_color', 'auto');
        $toAdd = $this->collectPositionBodyClasses($positionAtBottom, $themeColor);

        return $this->mergeClasses($classes, $toAdd);
    }

    /**
     * Collect body classes for admin bar position and theme
     *
     * @param bool $positionAtBottom Whether admin bar is at bottom
     * @param string $themeColor Theme color setting (auto|dark|light)
     * @return string[] Classes to add
     */
    private function collectPositionBodyClasses(bool $positionAtBottom, string $themeColor): array
    {
        $toAdd = [];
        if ($positionAtBottom) {
            $toAdd[] = 'fleekdash-admin-bar-bottom';
        }
        if ($themeColor === 'dark') {
            $toAdd[] = 'fleekdash-admin-bar-theme-dark';
        } elseif ($themeColor === 'light') {
            $toAdd[] = 'fleekdash-admin-bar-theme-light';
        }
        return $toAdd;
    }

    /**
     * Merge additional classes into existing classes (array or string)
     *
     * @param string|string[] $classes Existing classes
     * @param string[] $toAdd Classes to add
     * @return string|string[] Modified classes
     */
    private function mergeClasses($classes, array $toAdd)
    {
        if (empty($toAdd)) {
            return $classes;
        }
        if (\is_array($classes)) {
            return \array_merge($classes, $toAdd);
        }
        return $classes . ' ' . \implode(' ', $toAdd);
    }

    /**
     * Output inline CSS for admin bar bottom position
     *
     * @return void
     */
    public function addPositionStyles(): void
    {
        if (!\is_user_logged_in() || !\is_admin_bar_showing()) {
            return;
        }

        // Position only applies to Modern mode; Classic mode blocks FleekDash customizations
        if (\get_option('fleekdash_admin_bar_style', 'modern') === 'classic') {
            return;
        }

        $positionAtBottom = (bool) \get_option('fleekdash_admin_bar_position_bottom', false);
        if (!$positionAtBottom) {
            return;
        }

        ?>
        <style id="fleekdash-admin-bar-position-bottom">
            body.fleekdash-admin-bar-bottom.admin-bar #wpadminbar {
                top: auto !important;
                bottom: 0 !important;
                border-bottom: none !important;
                border-top: 1px solid var(--border, #e4e4e7) !important;
                box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.04) !important;
            }
            body.fleekdash-admin-bar-bottom.admin-bar:not(.fleekdash-adminbar-hidden) {
                padding-bottom: 32px !important;
            }
            html:has(body.fleekdash-admin-bar-bottom.admin-bar) {
                margin-top: 0 !important;
                padding-top: 0 !important;
            }
            body.fleekdash-admin-bar-bottom.admin-bar #wpadminbar .ab-sub-wrapper {
                top: auto !important;
                bottom: 100% !important;
                border-radius: 8px 8px 0 0 !important;
            }
        </style>
        <?php
    }

    /**
     * Check if logo should be added (always, except in React app)
     *
     * @return bool
     */
    private function shouldAddLogo(): bool
    {
        // Check if user is logged in and admin bar is showing
        if (!\is_user_logged_in() || !\is_admin_bar_showing()) {
            return false;
        }

        // Don't add logo in React app (adminbar is always hidden for performance)
        $isReactApp = isset($_GET['page']) && $_GET['page'] === 'fleekdash';
        if ($isReactApp) {
            return false;
        }

        // Always add logo in all modes (classic and modern)
        return true;
    }

    /**
     * Check if admin bar should have custom styles applied (only in modern mode)
     *
     * @return bool
     */
    private function shouldCustomizeAdminBar(): bool
    {
        $adminBarStyle = \get_option('fleekdash_admin_bar_style', 'modern');
        $adminbarParam = isset($_GET['adminbar']) ? \sanitize_text_field($_GET['adminbar']) : '';
        $disabledViaUrl = $adminbarParam === 'false' || $adminbarParam === 'off';

        return $this->shouldAddLogo()
            && $adminBarStyle === 'modern'
            && !$disabledViaUrl;
    }

    /**
     * Remove WordPress logo from admin bar
     *
     * @param \WP_Admin_Bar $wp_admin_bar
     * @return void
     */
    public function removeWordpressLogo($wp_admin_bar): void
    {
        $wp_admin_bar->remove_node('wp-logo');
    }

    /**
     * Add FleekDash logo to admin bar
     *
     * @param \WP_Admin_Bar $wp_admin_bar
     * @return void
     */
    public function addFleekdashLogo($wp_admin_bar): void
    {
        // Get logo URLs from settings
        $lightLogo = \get_option('_fleekdash_logo', '');
        $darkLogo = \get_option('_fleekdash_dark_logo', '');
        $useDarkLogo = (bool) \get_option('_fleekdash_use_dark_logo', false);
        $autoInvertLogoDarkMode = (bool) \get_option('_fleekdash_auto_invert_logo_dark_mode', true);

        // Fallback to default FleekDash logo (same for both modes if not set)
        $defaultLogo = FLEEKDASH_PLUGIN_URL . 'public/images/fleekdash.svg';

        $lightLogoUrl = !empty($lightLogo) ? $lightLogo : $defaultLogo;

        // Check if user enabled a specific dark logo
        $hasDarkLogo = $useDarkLogo && !empty($darkLogo);
        $darkLogoUrl = $hasDarkLogo ? $darkLogo : $lightLogoUrl;

        // Check admin bar style setting
        $adminBarStyle = \get_option('fleekdash_admin_bar_style', 'modern');
        $isClassicMode = ($adminBarStyle === 'classic');

        // In classic mode, always use dark logo (since WP admin bar is dark)
        if ($isClassicMode) {
            $logoHtml = $this->getClassicLogoHtml($darkLogoUrl, $autoInvertLogoDarkMode);
        } else {
            // In modern mode, use logo with light/dark switching
            $logoHtml = $this->getLogoHtml(
                $lightLogoUrl,
                $darkLogoUrl,
                $hasDarkLogo,
                $autoInvertLogoDarkMode,
            );
        }

        // FleekDash dashboard URL
        $dashboardUrl = \admin_url('admin.php?page=fleekdash#/dashboard');

        // Add logo node
        $wp_admin_bar->add_node([
            'id'    => 'fleekdash-logo',
            'title' => $logoHtml,
            'href'  => $dashboardUrl,
            'meta'  => [
                'class' => 'fleekdash-admin-bar-logo',
                'title' => 'FleekDash Dashboard',
            ],
        ]);
    }

    /**
     * Get logo HTML for classic mode (dark logo only)
     *
     * @param string $darkLogo Dark logo URL
     * @param bool $autoInvertLogoDarkMode Whether to auto-invert fallback logos
     * @return string HTML for logo
     */
    private function getClassicLogoHtml(string $darkLogo, bool $autoInvertLogoDarkMode): string
    {
        // In classic mode, always use dark logo (WP admin bar is dark)
        return sprintf(
            '<img src="%s" alt="FleekDash" class="fleekdash-admin-bar-logo-img fleekdash-classic-logo" data-auto-invert-dark-mode="%s" />',
            \esc_url($darkLogo),
            $autoInvertLogoDarkMode ? 'true' : 'false'
        );
    }

    /**
     * Get logo HTML with light/dark variants (for modern mode)
     *
     * @param string $lightLogo Light mode logo URL
     * @param string $darkLogo Dark mode logo URL
     * @param bool $hasDarkLogo Whether user has enabled a specific dark logo
     * @param bool $autoInvertLogoDarkMode Whether to auto-invert fallback logos
     * @return string HTML for logo
     */
    private function getLogoHtml(
        string $lightLogo,
        string $darkLogo,
        bool $hasDarkLogo,
        bool $autoInvertLogoDarkMode,
    ): string {
        // data-has-dark-logo tells JS whether to use invert filter or swap to dark logo
        return sprintf(
            '<img src="%s" alt="FleekDash" class="fleekdash-admin-bar-logo-img" data-light-logo="%s" data-dark-logo="%s" data-has-dark-logo="%s" data-auto-invert-dark-mode="%s" />',
            \esc_url($lightLogo),
            \esc_url($lightLogo),
            \esc_url($darkLogo),
            $hasDarkLogo ? 'true' : 'false',
            $autoInvertLogoDarkMode ? 'true' : 'false'
        );
    }

    /**
     * Add minimal styles for logo in classic mode (only logo, no other customizations)
     *
     * @return void
     */
    public function addClassicLogoStyles(): void
    {
        // Double-check: Only apply in classic mode (safety check)
        $adminBarStyle = \get_option('fleekdash_admin_bar_style', 'modern');
        if ($adminBarStyle !== 'classic') {
            // Should not be called in modern mode, but if it is, exit early
            return;
        }

        ?>
        <style id="fleekdash-admin-bar-classic-logo-styles">
            /* FleekDash Logo in Admin Bar - Classic Mode */
            #wp-admin-bar-fleekdash-logo {
                margin-left: 0 !important;
            }

            #wp-admin-bar-fleekdash-logo>.ab-item {
                padding: 0 12px !important;
                height: 32px !important;
                display: flex !important;
                align-items: center !important;
                border: none !important;
                box-shadow: none !important;
                background: transparent !important;
            }

            #wp-admin-bar-fleekdash-logo>.ab-item:hover {
                background: rgba(255, 255, 255, 0.1) !important;
            }

            .fleekdash-admin-bar-logo-img.fleekdash-classic-logo {
                height: 20px !important;
                width: auto !important;
                max-width: 120px !important;
                object-fit: contain !important;
                display: block !important;
                transition: opacity 0.2s ease !important;
                filter: none !important;
            }

            .fleekdash-admin-bar-logo-img.fleekdash-classic-logo[data-auto-invert-dark-mode="true"] {
                filter: invert(1) brightness(1.1) !important;
            }

            #wp-admin-bar-fleekdash-logo:hover .fleekdash-admin-bar-logo-img {
                opacity: 0.8;
            }

        </style>
        <?php
    }

    /**
     * Debug helper - check if admin bar customization is working
     *
     * @return void
     */
    public function debugAdminBar(): void
    {
        if (!isset($_GET['debug_adminbar'])) {
            return;
        }

        ?>
        <script>
            console.log('[FleekDash AdminBar Debug]', {
                wpLogo: document.querySelector('#wp-admin-bar-wp-logo'),
                fleekdashLogo: document.querySelector('#wp-admin-bar-fleekdash-logo'),
                logoImg: document.querySelector('.fleekdash-admin-bar-logo-img'),
                adminbarParam: '<?php echo isset($_GET['adminbar']) ? \esc_js(\sanitize_text_field($_GET['adminbar'])) : 'not set'; ?>',
                isAdmin: <?php echo \is_admin() ? 'true' : 'false'; ?>,
                isUserLoggedIn: <?php echo \is_user_logged_in() ? 'true' : 'false'; ?>
            });
        </script>
        <?php
    }
}
