<?php

declare(strict_types=1);

namespace FleekDash\Admin;

use FleekDash\Core\ScriptHandles;
use FleekDash\Libs\Assets as AssetsLib;

// Load debug helpers (procedural functions require file load, not class use)
if (!function_exists('fleekdash_log_error')) {
    require_once FLEEKDASH_PLUGIN_DIR . 'includes/Core/debug-helpers.php';
}

/**
 * Class Assets
 *
 * Manages admin scripts and styles.
 *
 * @package FleekDash\Admin
 */
class Assets
{
    /**
     * Script handle for FleekDash.
     */
    const HANDLE = 'fleekdash';

    /**
     * JS Object name for FleekDash.
     */
    const OBJ_NAME = 'FleekDash';

    /**
     * Development script path for FleekDash.
     */
    const DEV_SCRIPT = 'src/admin/entry.tsx';

    /**
     * Frontend development script path for FleekDash.
     */
    const DEV_FRONTEND_SCRIPT = 'src/frontend/main.tsx';

    /**
     * Check if current environment is development
     *
     * @return bool
     */
    public function isDevelopmentEnvironment()
    {
        return AssetsLib\isDevelopmentEnvironment();
    }

    /**
     * List of WordPress plugin pages that need our scripts.
     *
     * @var array
     */
    private $pluginPages = array(
        'plugin-install.php',
        'theme-install.php',
        'plugins.php',
        'update-core.php',
        // admin_enqueue_scripts $hook is often `update-core` (no .php suffix).
        'update-core',
    );

    /**
     * Menu service reference.
     */
    private AdminMenu $menu;

    /**
     * @var AssetsEnqueueHelper
     */
    private $enqueueHelper;

    /**
     * @var AssetsLocalizationBuilder
     */
    private $localizationBuilder;

    /**
     * Constructor
     *
     * @param AdminMenu $menu
     */
    public function __construct(AdminMenu $menu)
    {
        $this->menu = $menu;
        $this->enqueueHelper = new AssetsEnqueueHelper($this);
        $this->localizationBuilder = new AssetsLocalizationBuilder();
    }

    /**
     * Initialize hooks
     */
    public function bootstrap()
    {
        // Enqueue scripts and styles
        \add_action('admin_enqueue_scripts', array($this, 'enqueueScript'), 10);
        \add_action('admin_enqueue_scripts', array($this, 'enqueueStyles'), 10);

        // Enqueue plugin scripts for WordPress plugin pages
        \add_action('admin_enqueue_scripts', array($this, 'enqueuePluginScripts'), 10);

        // Late deregister of WP React (runs after all other enqueues) - prevents 2 renderers
        \add_action('admin_enqueue_scripts', array($this, 'deregisterWordPressReactLate'), 9998);
    }

    /**
     * Late deregister of WordPress React - safety net for plugins that re-enqueue after us.
     * Prevents window.__REACT_DEVTOOLS_GLOBAL_HOOK__.renderers.size === 2
     *
     * @param string $hook Current admin page hook (from admin_enqueue_scripts)
     */
    public function deregisterWordPressReactLate($hook = ''): void
    {
        if (!is_admin() || isset($_GET['fleekdash-frame'])) {
            return;
        }

        $loadReactShell = ReactAdminShellResolver::shouldLoad((string) $hook);
        if (!$loadReactShell) {
            return;
        }

        foreach (ScriptHandles::WORDPRESS_REACT as $handle) {
            \wp_deregister_script($handle);
        }
    }

    /**
     * Set script type as module
     *
     * @param string $handle Script handle
     */
    public function setScriptTypeModule($handle)
    {
        \add_filter('script_loader_tag', function ($tag, $script_handle) use ($handle) {
            // Early return if $tag is not a string
            if (!is_string($tag) || $handle !== $script_handle) {
                return $tag;
            }
            return str_replace('<script ', '<script type="module" ', $tag);
        }, 10, 2);
    }

    /**
     * Add defer attribute to non-critical scripts to avoid render blocking
     *
     * @param string $handle Script handle
     */
    public function addDeferAttribute($handle)
    {
        \add_filter('script_loader_tag', function ($tag, $script_handle) use ($handle) {
            // Early return if $tag is not a string
            if (!is_string($tag) || $handle !== $script_handle) {
                return $tag;
            }

            // Avoid duplicating attributes
            if (strpos($tag, ' defer ') !== false || strpos($tag, ' defer>') !== false) {
                return $tag;
            }

            return str_replace('<script ', '<script defer ', $tag);
        }, 10, 2);
    }

    /**
     * Enqueue script based on the current screen.
     *
     * @param string $hook The current admin page.
     */
    public function enqueueScript($hook)
    {
        static $script_enqueued = false;
        $loadReactShell = ReactAdminShellResolver::shouldLoad((string) $hook);

        if ($script_enqueued) {
            return;
        }

        if (!is_admin() || isset($_GET['fleekdash-frame'])) {
            return;
        }

        $script_enqueued = true;
        $this->enqueueHelper->deregisterWordPressReactOnFleekDash($loadReactShell);
        $this->enqueueHelper->enqueueCommonAdminScripts();
        // Only FleekDash admin shells load the SPA; native wp-admin stays clean for stability.
        if ($loadReactShell) {
            $this->enqueueHelper->enqueueMainScript(true);
            $this->enqueueHelper->localizeFleekDashScript();
        } else {
            $this->enqueueHelper->injectMinimalClassicAdminBootstrap();
        }
    }

    /**
     * Add React Scan CDN script when FLEEKDASH_REACT_SCAN_DEV is defined.
     */
    public function addReactScanScript(): void
    {
        if (!defined('FLEEKDASH_REACT_SCAN_DEV') || !\FLEEKDASH_REACT_SCAN_DEV) {
            return;
        }

        echo '<script src="https://cdn.jsdelivr.net/npm/react-scan@0.0.47/dist/auto.global.js" crossorigin></script>';
        echo '<script>
(function(){
  function enableLog(){if(window.reactScan)window.reactScan({log:true});else setTimeout(enableLog,50);}
  if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",enableLog);else enableLog();
})();
</script>';
    }

    /**
     * Get the script configuration for enqueueAsset.
     *
     * @return array The script configuration options.
     */
    public function getConfig(): array
    {
        return array(
            'handle' => self::HANDLE,
            'dependencies' => array(),
            'in_footer' => true,
            'css-dependencies' => array(),
            'css-media' => 'all',
            'css-only' => false,
        );
    }

    /**
     * Verifies and corrects the WP_DEBUG value
     *
     * @return array
     */
    private function getDebugStatus()
    {
        // Check if WP_DEBUG is defined
        $wp_debug = defined('WP_DEBUG') ? WP_DEBUG : false;
        $wp_debug_log = defined('WP_DEBUG_LOG') ? WP_DEBUG_LOG : false;
        $wp_debug_display = defined('WP_DEBUG_DISPLAY') ? WP_DEBUG_DISPLAY : false;

        // Check if debug is overridden via database option
        $force_debug_option = \get_option('fleekdash_force_debug', null);

        // Three possible states:
        // null/undefined = use WP_DEBUG default
        // true = force active (override WP_DEBUG)
        // false = force inactive (override WP_DEBUG)

        $force_debug = null;
        $final_debug_status = $wp_debug; // Default: use WP_DEBUG

        if ($force_debug_option !== false && $force_debug_option !== null) {
            // Option exists in database - convert to boolean
            if (is_string($force_debug_option)) {
                $force_debug = $force_debug_option === '1' || $force_debug_option === 'true';
            } elseif (is_numeric($force_debug_option)) {
                $force_debug = (int) $force_debug_option === 1;
            } else {
                $force_debug = (bool) $force_debug_option;
            }

            // OVERRIDE WP_DEBUG with database value
            $final_debug_status = $force_debug;
            $source = 'database';
        } else {
            // Option doesn't exist - use WP_DEBUG
            $force_debug = null;
            $source = $wp_debug ? 'wp-config' : 'none';
        }

        // Check if debug logging is being overridden
        $debug_logging_overridden = false;
        if (class_exists('FleekDash\Core\DebugLogger')) {
            $debug_logging_overridden = \FleekDash\Core\DebugLogger::isDebugLoggingActive();
        }

        return array(
            'debug' => (bool) $final_debug_status,
            'enabled' => (bool) $final_debug_status, // Add 'enabled' for API consistency
            'wp_debug' => (bool) $wp_debug,
            'wp_debug_log' => (bool) $wp_debug_log, // FIXED: Use correct name
            'wp_debug_display' => (bool) $wp_debug_display, // FIXED: Use correct name
            'force_debug' => $force_debug, // null if not set, boolean if set
            'source' => $source,
            'debug_logging_overridden' => $debug_logging_overridden, // NEW: indicates if logging is overridden
        );
    }

    /**
     * @return array{debug: bool, enabled: bool}
     */
    public function getClassicAdminHeadBootstrapData(): array
    {
        $status = $this->getDebugStatus();
        return array(
            'debug' => $status['debug'],
            'enabled' => $status['enabled'],
        );
    }

    /**
     * Get data for script localization.
     *
     * @return array The localized script data.
     */
    public function getData()
    {
        static $cached_data = null;

        if ($cached_data !== null) {
            return $cached_data;
        }

        $admin_menu = $this->getAdminMenuWithRetry();
        $toolbar_data = $this->menu->getAdminToolbarData();
        $user_info = \wp_get_current_user();
        $active_integrations = $this->getActiveIntegrations();
        $discovered_integrations = $this->getDiscoveredIntegrations();
        $debug_status = $this->getDebugStatus();

        $cached_data = $this->localizationBuilder->buildLocalizationData(
            $admin_menu,
            $toolbar_data,
            $user_info,
            $active_integrations,
            $discovered_integrations,
            $debug_status,
        );

        $this->localizationBuilder->injectDebugScriptOnce($debug_status, $cached_data);

        return $cached_data;
    }

    /**
     * Get admin menu items, retrying capture if empty.
     *
     * @return array
     */
    private function getAdminMenuWithRetry(): array
    {
        $admin_menu = $this->menu->getAdminMenuItems();

        if (empty($admin_menu)) {
            global $menu;
            if (!empty($menu)) {
                $this->menu->captureAdminMenuComplete();
                $admin_menu = $this->menu->getAdminMenuItems();
            }
        }

        return $admin_menu ?: [];
    }

    /**
     * Get discovered integrations from IntegrationManager.
     *
     * @return array
     */
    private function getDiscoveredIntegrations(): array
    {
        try {
            $manager = \FleekDash\Core\IntegrationManager::getInstance();
            return $manager->getAll();
        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash V2.2.0: Error getting discovered integrations: ' . $e->getMessage());
            return [];
        }
    }

    /**
     * Resolve active integrations (V2.0 sourced MU-plugins; V3 uses IntegrationManager)
     *
     * @return array
     */
    private function getActiveIntegrations()
    {
        static $cached_integrations = null;
        static $cache_attempted = false;

        if ($cached_integrations !== null && $cache_attempted) {
            return $cached_integrations;
        }

        $cache_attempted = true;

        // V3.0: Use IntegrationManager for integration status check
        try {
            $manager = \FleekDash\Core\IntegrationManager::getInstance();
            $integrations = $manager->getAll();

            $discovered_integrations = [];
            foreach (['plugin', 'theme', 'api'] as $category) {
                if (isset($integrations[$category])) {
                    $discovered_integrations = array_merge($discovered_integrations, $integrations[$category]);
                }
            }

            return $integrations;

        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash V2.2.0: Error getting integrations: ' . $e->getMessage());
        }

        return [
            'plugin' => [],
            'theme' => [],
            'api' => []
        ];
    }

    /**
     * Whether the admin screen hook matches plugin/update-related pages that need overlay scripts.
     */
    private function isPluginRelatedAdminHook(string $hook): bool
    {
        foreach ($this->pluginPages as $page) {
            if (strpos($hook, $page) !== false) {
                return true;
            }
        }

        return false;
    }

    /**
     * Script version: base plugin version, or version.filemtime in dev when the file exists (cache bust).
     *
     * @param string $fileName e.g. update-overlay.js under includes/js/
     */
    private function getPluginPageScriptVersion(string $fileName): string
    {
        $version = FLEEKDASH_VERSION;
        $entry = preg_replace('/\.js$/', '', $fileName) ?? $fileName;
        $useIncludesPath = \FleekDash\Libs\Assets\isLocalDevelopmentHost();
        $relativePath = $useIncludesPath
            ? 'includes/js/' . $fileName
            : 'assets/' . $entry . '.js';
        $fullPath = FLEEKDASH_PLUGIN_DIR . $relativePath;

        if (!\is_readable($fullPath)) {
            return $version;
        }

        $mtime = \filemtime($fullPath);
        if ($mtime === false) {
            return $version;
        }

        return $version . '.' . (string) $mtime;
    }

    /**
     * Enqueue plugin scripts for WordPress plugin pages
     */
    public function enqueuePluginScripts(string $hook): void
    {
        if (!$this->isPluginRelatedAdminHook($hook)) {
            return;
        }

        // Enqueue update-overlay.js first (dependency)
        // Use getJsUrl() which automatically handles dev vs production paths
        \wp_enqueue_script(
            'fleekdash-update-overlay',
            \FleekDash\Libs\Assets\getJsUrl('update-overlay'),
            array(),
            $this->getPluginPageScriptVersion('update-overlay.js'),
            true
        );

        // Shared REST upload client (plugins/upload + themes/upload)
        \wp_enqueue_script(
            'fleekdash-package-upload-rest',
            \FleekDash\Libs\Assets\getJsUrl('package-upload-rest'),
            array(),
            $this->getPluginPageScriptVersion('package-upload-rest.js'),
            true
        );

        \wp_localize_script('fleekdash-package-upload-rest', 'fleekdashUploadConfig', [
            'restUrl' => \esc_url_raw(\rest_url('fleekdash/v1/')),
            'restNonce' => \wp_create_nonce('wp_rest'),
        ]);

        // Enqueue plugin-upload.js (plugin-install.php upload tab)
        \wp_enqueue_script(
            'fleekdash-plugin-upload',
            \FleekDash\Libs\Assets\getJsUrl('plugin-upload'),
            array('fleekdash-update-overlay', 'fleekdash-package-upload-rest'),
            $this->getPluginPageScriptVersion('plugin-upload.js'),
            true
        );

        // Intercept theme ZIP upload on theme-install.php → UpdateOverlay + redirect (plugin-upload parity)
        \wp_enqueue_script(
            'fleekdash-theme-upload',
            \FleekDash\Libs\Assets\getJsUrl('theme-upload'),
            array('fleekdash-update-overlay', 'fleekdash-package-upload-rest'),
            $this->getPluginPageScriptVersion('theme-upload.js'),
            true
        );

        // Enqueue plugin-install-handler.js (fixes false "installation failed" messages)
        \wp_enqueue_script(
            'fleekdash-plugin-install-handler',
            \FleekDash\Libs\Assets\getJsUrl('plugin-install-handler'),
            array(),
            FLEEKDASH_VERSION,
            true
        );

        // Intercept native "Install Now" on plugin-install.php → FleekDash REST + UpdateOverlay
        \wp_enqueue_script(
            'fleekdash-plugin-directory-install',
            \FleekDash\Libs\Assets\getJsUrl('plugin-directory-install'),
            array('fleekdash-update-overlay'),
            $this->getPluginPageScriptVersion('plugin-directory-install.js'),
            true
        );

        // Intercept native theme directory install on theme-install.php → FleekDash REST + UpdateOverlay.
        // Do not list core handle "theme" as a dependency: theme.min.js requires _wpThemeSettings from
        // theme-install.php/themes.php wp_localize_script; pulling "theme" via deps can print it before
        // that inline data. Core already enqueues "theme" on theme-install.php; we only need "updates".
        \wp_enqueue_script(
            'fleekdash-theme-directory-install',
            \FleekDash\Libs\Assets\getJsUrl('theme-directory-install'),
            array('fleekdash-update-overlay', 'updates'),
            $this->getPluginPageScriptVersion('theme-directory-install.js'),
            true
        );

        // Enqueue plugin-update.js (filemtime in dev to bust browser cache on every file change)
        \wp_enqueue_script(
            'fleekdash-plugin-update',
            \FleekDash\Libs\Assets\getJsUrl('plugin-update'),
            array('fleekdash-update-overlay'),
            $this->getPluginPageScriptVersion('plugin-update.js'),
            true
        );

        // Enqueue core-update.js
        \wp_enqueue_script(
            'fleekdash-core-update',
            \FleekDash\Libs\Assets\getJsUrl('core-update'),
            array('fleekdash-update-overlay'),
            FLEEKDASH_VERSION,
            true
        );

        // Enqueue theme-update.js (filemtime in dev to bust cache when testing)
        \wp_enqueue_script(
            'fleekdash-theme-update',
            \FleekDash\Libs\Assets\getJsUrl('theme-update'),
            array('fleekdash-update-overlay'),
            $this->getPluginPageScriptVersion('theme-update.js'),
            true
        );

        // Do NOT use the global name "ajaxurl" — WordPress expects a string; an object breaks core $.post(ajaxurl, …).
        \wp_localize_script('fleekdash-update-overlay', 'fleekdashPluginAjax', [
            'ajaxUrl' => \admin_url('admin-ajax.php'),
        ]);

        // Expose REST root + nonce so plugin-upload.js can call our verification endpoint
        // (plugins/file-signature) without going through the SPA AuthManager (this page is
        // the native wp-admin upload form, not the React shell).
        // fleekdashUploadConfig is localized on fleekdash-package-upload-rest (shared by both upload scripts).

        // Localize wpApiSettings for scripts that need it
        \wp_localize_script('fleekdash-plugin-update', 'wpApiSettings', array(
            'root' => \esc_url_raw(\rest_url()),
            'nonce' => \wp_create_nonce('wp_rest')
        ));
        \wp_localize_script('fleekdash-core-update', 'wpApiSettings', array(
            'root' => \esc_url_raw(\rest_url()),
            'nonce' => \wp_create_nonce('wp_rest')
        ));
        \wp_localize_script('fleekdash-theme-update', 'wpApiSettings', array(
            'root' => \esc_url_raw(\rest_url()),
            'nonce' => \wp_create_nonce('wp_rest')
        ));
        \wp_localize_script('fleekdash-plugin-directory-install', 'wpApiSettings', array(
            'root' => \esc_url_raw(\rest_url()),
            'nonce' => \wp_create_nonce('wp_rest')
        ));
        \wp_localize_script('fleekdash-theme-directory-install', 'wpApiSettings', array(
            'root' => \esc_url_raw(\rest_url()),
            'nonce' => \wp_create_nonce('wp_rest')
        ));
    }

    /**
     * Enqueue plugin-cards helper for the Browse Plugins screen (.plugin-card modals).
     * Kept isolated from unrelated wp-admin routes to reduce script surface outside Fleek SPA.
     *
     * @param string $hook Current admin hook (passed by WP on admin_enqueue_scripts)
     */
    public function enqueueStyles($hook = '')
    {
        static $cards_enqueued = false;
        if ($cards_enqueued) {
            return;
        }

        if ($hook !== 'plugin-install.php') {
            return;
        }

        $cards_enqueued = true;

        // Future: Consider dynamic imports for plugin-cards to reduce initial load.

        // Register and enqueue plugin card JavaScript
        \wp_register_script(
            'fleekdash-plugin-cards',
            \FleekDash\Libs\Assets\getJsUrl('plugin-cards'),
            array('jquery'),
            FLEEKDASH_VERSION,
            true,
        );
        \wp_enqueue_script('fleekdash-plugin-cards');
        $this->addDeferAttribute('fleekdash-plugin-cards');
    }
}
