<?php
/**
 * Admin class for Blaminhor Essentials
 *
 * @package BlaminhorEssentials
 */

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Admin functionality class
 */
class Blaminhor_Essentials_Admin
{

    /**
     * Main plugin instance
     *
     * @var Blaminhor_Essentials
     */
    private $plugin;

    /**
     * Constructor
     *
     * @param Blaminhor_Essentials $plugin Main plugin instance.
     */
    public function __construct($plugin)
    {
        $this->plugin = $plugin;
        $this->register_ajax_handlers();

        // Add plugin action links (Settings link in plugins list)
        add_filter( 'plugin_action_links_' . BLAMINHOR_ESSENTIALS_PLUGIN_BASENAME, array( $this, 'add_plugin_action_links' ) );

        // Check for activation redirect
        add_action( 'admin_init', array( $this, 'maybe_redirect_after_activation' ) );

        // Redirect old ?page=blaminhor-essentials-logs URL to the Advanced page.
        // Called inline from the constructor (which runs at 'init'). Hooking on
        // 'admin_init' would be too late — wp-admin/admin.php emits the
        // "Sorry, you are not allowed" wp_die for unregistered pages BEFORE it.
        $this->maybe_redirect_legacy_logs_page();

        // Add admin bar menu
        add_action( 'admin_bar_menu', array( $this, 'add_admin_bar_menu' ), 100 );
    }

    /**
     * Redirect to plugin dashboard after activation
     */
    public function maybe_redirect_after_activation() {
        if ( get_transient( 'blaminhor_essentials_activation_redirect' ) ) {
            delete_transient( 'blaminhor_essentials_activation_redirect' );
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Not processing form data, just checking activation context.
            if ( ! isset( $_GET['activate-multi'] ) ) {
                wp_safe_redirect( admin_url( 'admin.php?page=blaminhor-essentials' ) );
                exit;
            }
        }
    }

    /**
     * Add Settings link to plugin action links
     *
     * @param array $links Existing plugin action links.
     * @return array
     */
    public function add_plugin_action_links( $links ) {
        $settings_link = sprintf(
            '<a href="%s">%s</a>',
            esc_url( admin_url( 'admin.php?page=blaminhor-essentials' ) ),
            esc_html__( 'Settings', 'blaminhor-essentials' )
        );
        // Insert after 'deactivate' link if it exists, otherwise at end
        if ( isset( $links['deactivate'] ) ) {
            $new_links = array();
            foreach ( $links as $key => $link ) {
                $new_links[ $key ] = $link;
                if ( 'deactivate' === $key ) {
                    $new_links['settings'] = $settings_link;
                }
            }
            return $new_links;
        }
        $links['settings'] = $settings_link;
        return $links;
    }

    /**
     * Register AJAX handlers
     */
    private function register_ajax_handlers()
    {
        add_action('wp_ajax_blaminhor_essentials_toggle_module', array($this, 'ajax_toggle_module'));
        add_action('wp_ajax_blaminhor_essentials_send_feedback', array($this, 'ajax_send_feedback'));
        add_action('wp_ajax_blaminhor_essentials_dismiss_feedback', array($this, 'ajax_dismiss_feedback'));
        add_action('wp_ajax_blaminhor_essentials_logs_download', array($this, 'ajax_logs_download'));
        add_action('wp_ajax_blaminhor_essentials_logs_record_client', array($this, 'ajax_logs_record_client'));
    }

    /**
     * Add admin menu
     */
    public function add_admin_menu()
    {
        // Main menu - Custom bow tie SVG icon
        // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
        $bow_tie_icon = 'data:image/svg+xml;base64,' . base64_encode(
            '<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">' .
            '<path fill="#a7aaad" d="M1 5l6 5-6 5V5zm18 0l-6 5 6 5V5zM8 8h4v4H8z"/>' .
            '</svg>'
        );

        add_menu_page(
            __('Blaminhor Essentials', 'blaminhor-essentials'),
            __('Blaminhor', 'blaminhor-essentials'),
            'manage_options',
            'blaminhor-essentials',
            array($this, 'render_dashboard_page'),
            $bow_tie_icon,
            65
        );

        // Dashboard submenu
        add_submenu_page(
            'blaminhor-essentials',
            __('Dashboard', 'blaminhor-essentials'),
            __('Dashboard', 'blaminhor-essentials'),
            'manage_options',
            'blaminhor-essentials',
            array($this, 'render_dashboard_page')
        );

        // Add submenus for active modules (sorted alphabetically by translated name)
        $active_modules = $this->plugin->get_active_modules();
        $available_modules = $this->plugin->get_available_modules();

        // Build array with names for sorting (names are already translated in module init)
        $modules_to_sort = array();
        foreach ($active_modules as $module_id => $module) {
            // Use get_name() which returns the already-translated name from the module
            $module_name = method_exists( $module, 'get_name' ) ? $module->get_name() : $module_id;
            $modules_to_sort[$module_id] = array(
                'module' => $module,
                'name'   => $module_name,
            );
        }

        // Sort by translated name (case-insensitive, accent-insensitive)
        uasort($modules_to_sort, function($a, $b) {
            // Use remove_accents() to normalize accented characters (É -> E, etc.)
            $name_a = remove_accents($a['name']);
            $name_b = remove_accents($b['name']);
            return strcasecmp($name_a, $name_b);
        });

        // Add sorted submenus
        foreach ($modules_to_sort as $module_data) {
            $module_data['module']->add_admin_menu();
        }

        // Cross-module Advanced page (Blaminhor → Advanced). Always visible
        // to admins; groups Logs (off by default) and a WP-CLI reference for
        // every active module. Tabs are URL-based via ?tab=logs|wp-cli.
        add_submenu_page(
            'blaminhor-essentials',
            __( 'Advanced', 'blaminhor-essentials' ),
            __( 'Advanced', 'blaminhor-essentials' ),
            'manage_options',
            'blaminhor-essentials-advanced',
            array( $this, 'render_advanced_page' )
        );
    }

    /**
     * Add admin bar menu
     *
     * @param WP_Admin_Bar $wp_admin_bar The admin bar instance.
     */
    public function add_admin_bar_menu( $wp_admin_bar ) {
        // Only show for users who can manage options
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }

        // Only show in admin area
        if ( ! is_admin() ) {
            return;
        }

        // Bow tie SVG icon for admin bar (same as sidebar)
        // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
        $bow_tie_svg = 'data:image/svg+xml;base64,' . base64_encode(
            '<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">' .
            '<path fill="#a0a5aa" d="M1 5l6 5-6 5V5zm18 0l-6 5 6 5V5zM8 8h4v4H8z"/>' .
            '</svg>'
        );

        // Add main menu item
        $wp_admin_bar->add_node( array(
            'id'    => 'blaminhor-essentials',
            'title' => '<img src="' . esc_attr( $bow_tie_svg ) . '" alt="" style="height: 20px; width: 20px; vertical-align: middle; margin-right: 6px; margin-top: -2px;">' . esc_html__( 'Blaminhor', 'blaminhor-essentials' ),
            'href'  => admin_url( 'admin.php?page=blaminhor-essentials' ),
            'meta'  => array(
                'title' => __( 'Blaminhor Essentials', 'blaminhor-essentials' ),
            ),
        ) );

        // Add Dashboard submenu
        $wp_admin_bar->add_node( array(
            'id'     => 'blaminhor-essentials-dashboard',
            'parent' => 'blaminhor-essentials',
            'title'  => __( 'Dashboard', 'blaminhor-essentials' ),
            'href'   => admin_url( 'admin.php?page=blaminhor-essentials' ),
        ) );

        // Get active modules sorted alphabetically by translated name
        $active_modules = $this->plugin->get_active_modules();

        if ( empty( $active_modules ) ) {
            return;
        }

        // Build array with names for sorting
        $modules_to_sort = array();
        foreach ( $active_modules as $module_id => $module ) {
            $module_name = method_exists( $module, 'get_name' ) ? $module->get_name() : $module_id;
            $modules_to_sort[ $module_id ] = array(
                'module' => $module,
                'name'   => $module_name,
            );
        }

        // Sort by translated name (case-insensitive, accent-insensitive)
        uasort( $modules_to_sort, function( $a, $b ) {
            $name_a = remove_accents( $a['name'] );
            $name_b = remove_accents( $b['name'] );
            return strcasecmp( $name_a, $name_b );
        } );

        // Add sorted module submenus
        foreach ( $modules_to_sort as $module_id => $module_data ) {
            $wp_admin_bar->add_node( array(
                'id'     => 'blaminhor-essentials-' . $module_id,
                'parent' => 'blaminhor-essentials',
                'title'  => $module_data['name'],
                'href'   => admin_url( 'admin.php?page=blaminhor-essentials-' . $module_id ),
            ) );
        }
    }

    /**
     * Enqueue admin assets
     *
     * @param string $hook Current admin page hook.
     */
    public function enqueue_assets($hook)
    {
        // Only load on plugin pages
        if (strpos($hook, 'blaminhor-essentials') === false) {
            return;
        }

        // Use minified assets in production, non-minified when SCRIPT_DEBUG is enabled.
        $debug  = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
        $suffix = $debug ? '' : '.min';

        // In SCRIPT_DEBUG mode, use filemtime as version so edits bust browser cache instantly.
        $asset_version = function ( $relative_path ) use ( $debug ) {
            if ( $debug ) {
                $abs = BLAMINHOR_ESSENTIALS_PLUGIN_DIR . $relative_path;
                if ( file_exists( $abs ) ) {
                    return (string) filemtime( $abs );
                }
            }
            return BLAMINHOR_ESSENTIALS_VERSION;
        };

        // Main CSS
        wp_enqueue_style(
            'blaminhor-essentials-admin',
            BLAMINHOR_ESSENTIALS_PLUGIN_URL . 'assets/css/admin' . $suffix . '.css',
            array(),
            $asset_version( 'assets/css/admin' . $suffix . '.css' )
        );

        // Module-specific CSS
        wp_enqueue_style(
            'blaminhor-essentials-modules',
            BLAMINHOR_ESSENTIALS_PLUGIN_URL . 'assets/css/modules.css',
            array( 'blaminhor-essentials-admin' ),
            $asset_version( 'assets/css/modules.css' )
        );

        // Main JS
        wp_enqueue_script(
            'blaminhor-essentials-admin',
            BLAMINHOR_ESSENTIALS_PLUGIN_URL . 'assets/js/admin' . $suffix . '.js',
            array('jquery'),
            $asset_version( 'assets/js/admin' . $suffix . '.js' ),
            true
        );

        // Module-specific JS
        wp_enqueue_script(
            'blaminhor-essentials-modules',
            BLAMINHOR_ESSENTIALS_PLUGIN_URL . 'assets/js/modules.js',
            array( 'jquery', 'blaminhor-essentials-admin' ),
            $asset_version( 'assets/js/modules.js' ),
            true
        );

        // Localize script
        wp_localize_script('blaminhor-essentials-admin', 'blaminhorEssentials', array(
            'ajaxUrl' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('blaminhor_essentials_admin'),
            'strings' => array(
                'activating' => __('Activating...', 'blaminhor-essentials'),
                'deactivating' => __('Deactivating...', 'blaminhor-essentials'),
                'active' => __('Active', 'blaminhor-essentials'),
                'inactive' => __('Inactive', 'blaminhor-essentials'),
                'error' => __('An error occurred. Please try again.', 'blaminhor-essentials'),
                'feedbackSent' => __('Thank you for your feedback!', 'blaminhor-essentials'),
                'sending' => __('Sending...', 'blaminhor-essentials'),
                'confirmDeactivate' => __('Are you sure you want to deactivate this module?', 'blaminhor-essentials'),
                'confirmDeleteRelay' => __('Are you sure you want to delete this relay?', 'blaminhor-essentials'),
                'fileTooLarge' => __('One or more files exceed the maximum size of 10 MB.', 'blaminhor-essentials'),
            ),
        ));

        // Let active modules enqueue their assets
        $active_modules = $this->plugin->get_active_modules();
        foreach ($active_modules as $module) {
            $module->enqueue_assets($hook);
        }
    }

    /**
     * Register settings
     */
    public function register_settings()
    {
        register_setting(
            'blaminhor_essentials_options',
            'blaminhor_essentials_options',
            array(
                'type'              => 'array',
                'sanitize_callback' => array( $this, 'sanitize_options' ),
            )
        );
        register_setting(
            'blaminhor_essentials_active_modules',
            'blaminhor_essentials_active_modules',
            array(
                'type'              => 'array',
                'sanitize_callback' => array( $this, 'sanitize_active_modules' ),
            )
        );
    }

    /**
     * Sanitize options
     *
     * @param mixed $value The value to sanitize.
     * @return array
     */
    public function sanitize_options( $value ) {
        if ( ! is_array( $value ) ) {
            return array();
        }
        return array_map( 'sanitize_text_field', $value );
    }

    /**
     * Sanitize active modules
     *
     * @param mixed $value The value to sanitize.
     * @return array
     */
    public function sanitize_active_modules( $value ) {
        if ( ! is_array( $value ) ) {
            return array();
        }
        return array_map( 'sanitize_key', $value );
    }

    /**
     * Handle advanced settings form submission
     */
    private function handle_advanced_settings() {
        if ( ! isset( $_POST['blaminhor_essentials_save_advanced'] ) ) {
            return;
        }

        if ( ! isset( $_POST['blaminhor_essentials_advanced_nonce'] )
            || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['blaminhor_essentials_advanced_nonce'] ) ), 'blaminhor_essentials_advanced_settings' )
        ) {
            return;
        }

        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }

        $delete_data = isset( $_POST['delete_data_on_uninstall'] ) ? true : false;
        update_option( 'blaminhor_essentials_delete_data', $delete_data );

        add_action( 'admin_notices', function () {
            ?>
            <div class="notice notice-success is-dismissible">
                <p><?php esc_html_e( 'Advanced settings saved.', 'blaminhor-essentials' ); ?></p>
            </div>
            <?php
        } );
    }

    /**
     * Render dashboard page
     */
    public function render_dashboard_page()
    {
        // Handle advanced settings form submission.
        $this->handle_advanced_settings();

        $modules_by_category = $this->plugin->get_modules_by_category();
        $active_modules = get_option('blaminhor_essentials_active_modules', array());
        $total_active = count($active_modules);
        $total_modules = count($this->plugin->get_available_modules());
        ?>
        <div class="wrap blaminhor-essentials-wrap">
            <div class="blaminhor-essentials-header">
                <h1>
                    <span class="blaminhor-essentials-logo">Blaminhor Essentials</span>
                </h1>
                <p class="blaminhor-essentials-tagline"><?php esc_html_e('Modular toolkit for WordPress', 'blaminhor-essentials'); ?></p>
            </div>

            <?php if ( ! get_option( 'blaminhor_essentials_feedback_dismissed' ) ) : ?>
            <div class="blaminhor-essentials-notice info" style="margin-bottom: 20px; display: flex; align-items: center; justify-content: space-between;">
                <p style="margin: 0;">
                    <span class="dashicons dashicons-email-alt" style="color: #2271b1; margin-right: 5px;"></span>
                    <?php
                    printf(
                        /* translators: %s: link to feedback form */
                        esc_html__( 'Have an idea, a suggestion, or found a bug? %s — I will gladly improve the plugin based on your feedback.', 'blaminhor-essentials' ),
                        '<a href="#blaminhor-essentials-feedback-form">' . esc_html__( 'Let me know', 'blaminhor-essentials' ) . '</a>'
                    );
                    ?>
                </p>
                <button type="button" class="notice-dismiss ap-dismiss-feedback" title="<?php esc_attr_e( 'Dismiss', 'blaminhor-essentials' ); ?>">
                    <span class="screen-reader-text"><?php esc_html_e( 'Dismiss', 'blaminhor-essentials' ); ?></span>
                </button>
            </div>
            <?php endif; ?>

            <?php
            $upgrade_notice = function_exists('blaminhor_essentials_get_current_upgrade_notice')
                ? blaminhor_essentials_get_current_upgrade_notice()
                : '';
            if ('' !== $upgrade_notice):
                $current_version = defined('BLAMINHOR_ESSENTIALS_VERSION') ? BLAMINHOR_ESSENTIALS_VERSION : '';
                ?>
                <!-- What's new in this update -->
                <div class="blaminhor-essentials-section blaminhor-essentials-whatsnew" style="margin-bottom: 20px;">
                    <h2>
                        <span class="dashicons dashicons-megaphone"></span>
                        <?php
                        /* translators: %s: plugin version, e.g. 1.9.9 */
                        printf(esc_html__('What\'s new in version %s', 'blaminhor-essentials'), esc_html($current_version));
                        ?>
                    </h2>
                    <p class="whatsnew-text"><?php echo esc_html($upgrade_notice); ?></p>
                </div>
            <?php endif; ?>

            <div class="blaminhor-essentials-content">
                <!-- Modules Section -->
                <div class="blaminhor-essentials-section ap-modules-section">
                    <div class="ap-modules-header">
                        <h2><?php esc_html_e('Modules', 'blaminhor-essentials'); ?></h2>
                        <p class="description">
                            <?php esc_html_e('Keep your site fast and lightweight, no matter which modules you activate.', 'blaminhor-essentials'); ?>
                        </p>
                    </div>

                    <!-- Category Tabs -->
                    <div class="ap-category-tabs">
                        <!-- All Tab -->
                        <button type="button" class="ap-category-tab active" data-category="all">
                            <span class="dashicons dashicons-screenoptions"></span>
                            <span class="tab-name"><?php esc_html_e('All', 'blaminhor-essentials'); ?></span>
                            <span class="tab-count"><?php echo esc_html($total_modules); ?></span>
                        </button>
                        <?php foreach ($modules_by_category as $cat_id => $category):
                            $total_count = count($category['modules']);
                            ?>
                            <button type="button" class="ap-category-tab" data-category="<?php echo esc_attr($cat_id); ?>">
                                <span class="dashicons <?php echo esc_attr($category['icon']); ?>"></span>
                                <span class="tab-name"><?php echo esc_html($category['name']); ?></span>
                                <span class="tab-count"><?php echo esc_html($total_count); ?></span>
                            </button>
                        <?php endforeach; ?>
                    </div>

                    <!-- All Modules Grid -->
                    <div class="ap-modules-grid active" data-category="all">
                        <?php
                        $all_modules = $this->plugin->get_available_modules();
                        foreach ($all_modules as $module_id => $module):
                            $is_active = in_array($module_id, $active_modules, true);
                            ?>
                            <div class="ap-module-card <?php echo esc_attr($is_active ? 'active' : ''); ?>" data-module="<?php echo esc_attr($module_id); ?>">
                                <div class="ap-module-header">
                                    <div class="ap-module-icon">
                                        <span class="dashicons <?php echo esc_attr($module['icon']); ?>"></span>
                                    </div>
                                    <div class="ap-module-toggle">
                                        <label class="blaminhor-essentials-switch">
                                            <input type="checkbox" class="module-toggle-input" data-module="<?php echo esc_attr($module_id); ?>" <?php checked($is_active); ?>>
                                            <span class="slider"></span>
                                        </label>
                                    </div>
                                </div>
                                <div class="ap-module-body">
                                    <h3 class="ap-module-name"><?php echo esc_html($module['name']); ?></h3>
                                    <p class="ap-module-desc"><?php echo esc_html($module['description']); ?></p>
                                </div>
                                <?php if ($is_active): ?>
                                    <a href="<?php echo esc_url(admin_url('admin.php?page=blaminhor-essentials-' . $module_id)); ?>" class="ap-module-settings">
                                        <?php esc_html_e('Settings', 'blaminhor-essentials'); ?>
                                        <span class="dashicons dashicons-arrow-right-alt2"></span>
                                    </a>
                                <?php endif; ?>
                            </div>
                        <?php endforeach; ?>
                    </div>

                    <!-- Modules Grid per Category -->
                    <?php foreach ($modules_by_category as $cat_id => $category): ?>
                        <div class="ap-modules-grid" data-category="<?php echo esc_attr($cat_id); ?>">
                            <?php foreach ($category['modules'] as $module_id => $module):
                                $is_active = in_array($module_id, $active_modules, true);
                                ?>
                                <div class="ap-module-card <?php echo esc_attr($is_active ? 'active' : ''); ?>" data-module="<?php echo esc_attr($module_id); ?>">
                                    <div class="ap-module-header">
                                        <div class="ap-module-icon">
                                            <span class="dashicons <?php echo esc_attr($module['icon']); ?>"></span>
                                        </div>
                                        <div class="ap-module-toggle">
                                            <label class="blaminhor-essentials-switch">
                                                <input type="checkbox" class="module-toggle-input" data-module="<?php echo esc_attr($module_id); ?>" <?php checked($is_active); ?>>
                                                <span class="slider"></span>
                                            </label>
                                        </div>
                                    </div>
                                    <div class="ap-module-body">
                                        <h3 class="ap-module-name"><?php echo esc_html($module['name']); ?></h3>
                                        <p class="ap-module-desc"><?php echo esc_html($module['description']); ?></p>
                                    </div>
                                    <?php if ($is_active): ?>
                                        <a href="<?php echo esc_url(admin_url('admin.php?page=blaminhor-essentials-' . $module_id)); ?>" class="ap-module-settings">
                                            <?php esc_html_e('Settings', 'blaminhor-essentials'); ?>
                                            <span class="dashicons dashicons-arrow-right-alt2"></span>
                                        </a>
                                    <?php endif; ?>
                                </div>
                            <?php endforeach; ?>
                        </div>
                    <?php endforeach; ?>
                </div>

                <!-- Feedback Section -->
                <div class="blaminhor-essentials-section blaminhor-essentials-feedback">
                    <h2><?php esc_html_e('Feedback & Ideas', 'blaminhor-essentials'); ?></h2>
                    <p class="description">
                        <?php esc_html_e('Have a feature request or found a bug? Let me know!', 'blaminhor-essentials'); ?>
                    </p>

                    <form id="blaminhor-essentials-feedback-form" class="feedback-form" enctype="multipart/form-data">
                        <div class="form-field">
                            <label for="feedback-type"><?php esc_html_e('Type', 'blaminhor-essentials'); ?></label>
                            <select id="feedback-type" name="feedback_type" required>
                                <option value="feature"><?php esc_html_e('Feature Request', 'blaminhor-essentials'); ?></option>
                                <option value="improvement"><?php esc_html_e('Improvement', 'blaminhor-essentials'); ?></option>
                                <option value="bug"><?php esc_html_e('Bug Report', 'blaminhor-essentials'); ?></option>
                                <option value="other"><?php esc_html_e('Other', 'blaminhor-essentials'); ?></option>
                            </select>
                        </div>
                        <div class="form-field">
                            <label for="feedback-message"><?php esc_html_e('Message', 'blaminhor-essentials'); ?></label>
                            <textarea id="feedback-message" name="feedback_message" rows="4" required
                                placeholder="<?php esc_attr_e('Describe your idea or issue...', 'blaminhor-essentials'); ?>"
                                data-placeholder-default="<?php esc_attr_e('Describe your idea or issue...', 'blaminhor-essentials'); ?>"
                                data-placeholder-bug="<?php esc_attr_e('Please describe the bug and the steps to reproduce it (1. Go to... 2. Click on... 3. See error). Screenshots and videos are very helpful!', 'blaminhor-essentials'); ?>"></textarea>
                        </div>
                        <div class="form-field">
                            <label for="feedback-attachments"><?php esc_html_e('Attachments (optional)', 'blaminhor-essentials'); ?></label>
                            <input type="file" id="feedback-attachments" name="feedback_attachments[]" multiple
                                accept="image/*,video/*,.pdf,.txt,.log">
                            <p class="description"><?php esc_html_e('You can attach screenshots, videos, or log files (max 10 MB each).', 'blaminhor-essentials'); ?></p>
                        </div>
                        <div class="form-field">
                            <label for="feedback-email"><?php esc_html_e('Your Email (optional)', 'blaminhor-essentials'); ?></label>
                            <input type="email" id="feedback-email" name="feedback_email"
                                placeholder="<?php esc_attr_e('For follow-up if needed', 'blaminhor-essentials'); ?>">
                            <p class="description"><?php esc_html_e('Without your email, I won\'t be able to reply to your feedback.', 'blaminhor-essentials'); ?></p>
                        </div>
                        <button type="submit"
                            class="button button-primary"><?php esc_html_e('Send Feedback', 'blaminhor-essentials'); ?></button>
                    </form>
                </div>

                <!-- Support Section -->
                <div class="blaminhor-essentials-section blaminhor-essentials-support">
                    <h2><?php esc_html_e('Support the Project', 'blaminhor-essentials'); ?></h2>
                    <div class="support-links">
                        <a href="https://wordpress.org/support/plugin/blaminhor-essentials/reviews/#new-post" target="_blank"
                            class="support-link">
                            <span class="dashicons dashicons-star-filled"></span>
                            <?php esc_html_e('Leave a Review', 'blaminhor-essentials'); ?>
                        </a>
                        <a href="mailto:hi+wp@blaminhor.com" class="support-link">
                            <span class="dashicons dashicons-email"></span>
                            <?php esc_html_e('Contact Me', 'blaminhor-essentials'); ?>
                        </a>
                        <a href="https://wp.blaminhor.com/" target="_blank" class="support-link">
                            <span class="dashicons dashicons-admin-site-alt3"></span>
                            <?php esc_html_e('Visit Website', 'blaminhor-essentials'); ?>
                        </a>
                    </div>
                </div>

                <!-- Advanced Settings -->
                <div class="blaminhor-essentials-section blaminhor-essentials-advanced">
                    <h2><?php esc_html_e('Advanced Settings', 'blaminhor-essentials'); ?></h2>
                    <form method="post" action="">
                        <?php wp_nonce_field('blaminhor_essentials_advanced_settings', 'blaminhor_essentials_advanced_nonce'); ?>
                        <table class="form-table">
                            <tr>
                                <th scope="row"><?php esc_html_e('Uninstall', 'blaminhor-essentials'); ?></th>
                                <td>
                                    <label>
                                        <input type="checkbox" name="delete_data_on_uninstall" value="1" <?php checked(get_option('blaminhor_essentials_delete_data', false)); ?>>
                                        <?php esc_html_e('Delete all plugin data when uninstalling', 'blaminhor-essentials'); ?>
                                    </label>
                                    <p class="description">
                                        <?php esc_html_e('If enabled, all settings, module data, database tables, and uploaded files will be permanently deleted when you uninstall the plugin.', 'blaminhor-essentials'); ?>
                                    </p>
                                </td>
                            </tr>
                        </table>
                        <p class="submit">
                            <button type="submit" name="blaminhor_essentials_save_advanced" class="button button-secondary">
                                <?php esc_html_e('Save Advanced Settings', 'blaminhor-essentials'); ?>
                            </button>
                        </p>
                    </form>
                </div>

                <!-- Credits -->
                <div class="blaminhor-essentials-credits">
                    <p>
                        <?php
                        printf(
                            /* translators: %s: Author URL */
                            esc_html__('Created with care by %s', 'blaminhor-essentials'),
                            '<a href="https://blaminhor.com" target="_blank">blaminhor</a>'
                        );
                        ?>
                        &bull;
                        <?php
                        printf(
                            /* translators: %s: Version number */
                            esc_html__('Version %s', 'blaminhor-essentials'),
                            esc_html(BLAMINHOR_ESSENTIALS_VERSION)
                        );
                        ?>
                    </p>
                </div>
            </div>
        </div>
        <?php
    }

    /**
     * AJAX handler for toggling modules
     */
    public function ajax_toggle_module()
    {
        check_ajax_referer('blaminhor_essentials_admin', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => __('Permission denied.', 'blaminhor-essentials')));
        }

        $module_id = isset( $_POST['module'] ) ? sanitize_key( wp_unslash( $_POST['module'] ) ) : '';
        $activate = isset( $_POST['activate'] ) ? filter_var( wp_unslash( $_POST['activate'] ), FILTER_VALIDATE_BOOLEAN ) : false;

        if (empty($module_id)) {
            wp_send_json_error(array('message' => __('Invalid module.', 'blaminhor-essentials')));
        }

        $available_modules = $this->plugin->get_available_modules();
        if (!isset($available_modules[$module_id])) {
            wp_send_json_error(array('message' => __('Module not found.', 'blaminhor-essentials')));
        }

        if ($activate) {
            $result = $this->plugin->activate_module($module_id);

            // A module's pre_activation_check() may return a WP_Error to refuse activation
            // (e.g. cache conflict detection). Surface it to the UI without persisting.
            if ( is_wp_error( $result ) ) {
                wp_send_json_error(array(
                    'message' => $result->get_error_message(),
                    'code'    => $result->get_error_code(),
                    'data'    => $result->get_error_data(),
                ));
            }

            $settings_url = admin_url('admin.php?page=blaminhor-essentials-' . $module_id);
            wp_send_json_success(array(
                'message' => __('Module activated.', 'blaminhor-essentials'),
                'settings_url' => $settings_url,
            ));
        } else {
            $this->plugin->deactivate_module($module_id);
            wp_send_json_success(array('message' => __('Module deactivated.', 'blaminhor-essentials')));
        }
    }

    /**
     * AJAX handler for feedback form
     */
    public function ajax_send_feedback()
    {
        check_ajax_referer('blaminhor_essentials_admin', 'nonce');

        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'message' => __( 'Unauthorized', 'blaminhor-essentials' ) ) );
        }

        $type = isset($_POST['type']) ? sanitize_key($_POST['type']) : 'other';
        $message = isset($_POST['message']) ? sanitize_textarea_field(wp_unslash($_POST['message'])) : '';
        $email = isset($_POST['email']) ? sanitize_email(wp_unslash($_POST['email'])) : '';

        if (empty($message)) {
            wp_send_json_error(array('message' => __('Please enter a message.', 'blaminhor-essentials')));
        }

        // Get site info
        $site_url = home_url();
        $wp_version = get_bloginfo('version');
        $php_version = phpversion();
        $plugin_version = BLAMINHOR_ESSENTIALS_VERSION;

        // Handle file uploads
        $attachments = array();
        $upload_dir = wp_upload_dir();
        $temp_dir = trailingslashit($upload_dir['basedir']) . 'blaminhor-essentials-temp/';

        // Check if attachments were uploaded
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Filename is sanitized with sanitize_file_name() below
        $has_attachments = isset( $_FILES['attachments'] )
            && isset( $_FILES['attachments']['name'] )
            && is_array( $_FILES['attachments']['name'] )
            && ! empty( $_FILES['attachments']['name'][0] );

        // Create temp directory if it doesn't exist
        if ( $has_attachments ) {
            if (!file_exists($temp_dir)) {
                wp_mkdir_p($temp_dir);
                // Add index.php for security
                global $wp_filesystem;
                if ( empty( $wp_filesystem ) ) {
                    require_once ABSPATH . 'wp-admin/includes/file.php';
                    WP_Filesystem();
                }
                $wp_filesystem->put_contents( $temp_dir . 'index.php', '<?php // Silence is golden', FS_CHMOD_FILE );
            }

            $allowed_types = array(
                'image/jpeg',
                'image/png',
                'image/gif',
                'image/webp',
                'video/mp4',
                'video/quicktime',
                'video/webm',
                'application/pdf',
                'text/plain',
            );
            $max_size = 10 * 1024 * 1024; // 10 MB

            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Filenames are sanitized with sanitize_file_name() below
            $file_names = isset( $_FILES['attachments']['name'] ) ? $_FILES['attachments']['name'] : array();
            $file_count = count( $file_names );

            for ( $key = 0; $key < $file_count; $key++ ) {
                // Validate all required indices exist
                if ( ! isset( $_FILES['attachments']['name'][ $key ] )
                    || ! isset( $_FILES['attachments']['type'][ $key ] )
                    || ! isset( $_FILES['attachments']['size'][ $key ] )
                    || ! isset( $_FILES['attachments']['tmp_name'][ $key ] )
                    || ! isset( $_FILES['attachments']['error'][ $key ] )
                ) {
                    continue;
                }

                // Sanitize filename
                $filename = sanitize_file_name( wp_unslash( $_FILES['attachments']['name'][ $key ] ) );
                if ( empty( $filename ) ) {
                    continue;
                }

                // Get file info - these don't need wp_unslash as they're set by PHP
                $file_type  = sanitize_mime_type( $_FILES['attachments']['type'][ $key ] );
                $file_size  = absint( $_FILES['attachments']['size'][ $key ] );
                // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- tmp_name is a server path validated by is_uploaded_file() below
                $file_tmp   = $_FILES['attachments']['tmp_name'][ $key ];
                $file_error = absint( $_FILES['attachments']['error'][ $key ] );

                // Validate file
                if ( UPLOAD_ERR_OK !== $file_error ) {
                    continue;
                }

                if ( ! in_array( $file_type, $allowed_types, true ) ) {
                    continue;
                }

                if ( $file_size > $max_size ) {
                    continue;
                }

                // Verify it's an uploaded file
                if ( ! is_uploaded_file( $file_tmp ) ) {
                    continue;
                }

                // Generate unique filename
                $unique_filename = wp_unique_filename( $temp_dir, $filename );
                $dest_path       = $temp_dir . $unique_filename;

                // Use wp_handle_upload for proper file handling
                $upload_overrides = array(
                    'test_form' => false,
                    'test_type' => false,
                    'unique_filename_callback' => function( $dir, $name ) use ( $unique_filename ) {
                        return $unique_filename;
                    },
                );

                // Create a single-file array for wp_handle_upload
                $_FILES['blaminhor_essentials_single'] = array(
                    'name'     => $unique_filename,
                    'type'     => $file_type,
                    'tmp_name' => $file_tmp,
                    'error'    => $file_error,
                    'size'     => $file_size,
                );

                // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- Array is created above on line 556-562
                $moved = wp_handle_upload( $_FILES['blaminhor_essentials_single'], $upload_overrides );

                if ( $moved && ! isset( $moved['error'] ) && isset( $moved['file'] ) ) {
                    // Move the file to our temp directory
                    global $wp_filesystem;
                    if ( $wp_filesystem->move( $moved['file'], $dest_path, true ) ) {
                        $attachments[] = $dest_path;
                    } else {
                        // If move failed, still add the original location
                        $attachments[] = $moved['file'];
                    }
                }

                // Clean up the temp single file array
                unset( $_FILES['blaminhor_essentials_single'] );
            }
        }

        // Build email content
        $email_content = sprintf(
            "Type: %s\n\nMessage:\n%s\n\nSite URL: %s\nWP Version: %s\nPHP Version: %s\nPlugin Version: %s\nUser Email: %s",
            $type,
            $message,
            $site_url,
            $wp_version,
            $php_version,
            $plugin_version,
            $email ? $email : 'Not provided'
        );

        if (!empty($attachments)) {
            $email_content .= "\n\nAttachments: " . count($attachments) . ' file(s)';
        }

        // Send feedback email with attachments
        $headers = array('Content-Type: text/plain; charset=UTF-8');
        // If the user provided an email, set it as Reply-To so the response
        // reaches them directly instead of bouncing on the site's From address.
        if ( ! empty( $email ) ) {
            $headers[] = 'Reply-To: ' . $email;
        }
        $sent = wp_mail(
            'hi+wp@blaminhor.com',
            '[Blaminhor Essentials Feedback] ' . ucfirst($type),
            $email_content,
            $headers,
            $attachments
        );

        // Clean up temp files
        foreach ($attachments as $file_path) {
            if (file_exists($file_path)) {
                wp_delete_file($file_path);
            }
        }

        // Try to remove temp directory if empty
        if ( is_dir( $temp_dir ) ) {
            $files = glob( $temp_dir . '*' );
            $files = array_filter( $files, function ( $f ) {
                return basename( $f ) !== 'index.php';
            } );
            if ( empty( $files ) ) {
                global $wp_filesystem;
                if ( empty( $wp_filesystem ) ) {
                    require_once ABSPATH . 'wp-admin/includes/file.php';
                    WP_Filesystem();
                }
                $wp_filesystem->delete( $temp_dir . 'index.php' );
                $wp_filesystem->rmdir( $temp_dir );
            }
        }

        if ($sent) {
            wp_send_json_success(array('message' => __('Thank you for your feedback!', 'blaminhor-essentials')));
        } else {
            wp_send_json_error(array('message' => __('Failed to send feedback. Please try again or contact us directly.', 'blaminhor-essentials')));
        }
    }

    /**
     * AJAX: Dismiss feedback notice
     */
    public function ajax_dismiss_feedback()
    {
        check_ajax_referer('blaminhor_essentials_admin', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error();
        }

        update_option('blaminhor_essentials_feedback_dismissed', true);
        wp_send_json_success();
    }

    /* ---------------------------------------------------------------- */
    /*  Cross-module Logs page                                          */
    /* ---------------------------------------------------------------- */

    /**
     * Render the Blaminhor → Logs page: settings, filters, events table,
     * action buttons (Copy / Download / Clear). Posts back to itself.
     */
    public function render_logs_page() {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( esc_html__( 'You do not have permission to access this page.', 'blaminhor-essentials' ) );
        }

        $logs = Blaminhor_Essentials_Logs::instance();

        // Handle settings save / clear (POST).
        if ( ! empty( $_POST ) && check_admin_referer( 'blaminhor_essentials_logs', '_be_logs_nonce' ) ) {
            if ( isset( $_POST['be_logs_action'] ) && 'clear' === $_POST['be_logs_action'] ) {
                $logs->clear();
                add_settings_error( 'be_logs', 'be_logs_cleared', __( 'Log file cleared.', 'blaminhor-essentials' ), 'updated' );
            } elseif ( isset( $_POST['be_logs_action'] ) && 'save' === $_POST['be_logs_action'] ) {
                $logs->update_settings( array(
                    'enabled'   => ! empty( $_POST['be_logs_enabled'] ),
                    'modules'   => isset( $_POST['be_logs_modules'] ) ? (array) $_POST['be_logs_modules'] : array(),
                    'min_level' => isset( $_POST['be_logs_min_level'] ) ? sanitize_text_field( wp_unslash( $_POST['be_logs_min_level'] ) ) : 'warning',
                ) );
                add_settings_error( 'be_logs', 'be_logs_saved', __( 'Log settings saved.', 'blaminhor-essentials' ), 'updated' );
            }
        }

        $settings = $logs->get_settings();
        $filters  = array(
            'module' => isset( $_GET['module'] ) ? sanitize_key( wp_unslash( $_GET['module'] ) ) : '',
            'level'  => isset( $_GET['level'] ) ? sanitize_key( wp_unslash( $_GET['level'] ) ) : '',
            'search' => isset( $_GET['search'] ) ? sanitize_text_field( wp_unslash( $_GET['search'] ) ) : '',
        );
        $events   = $logs->read( $filters );
        // Sort newest first for the UI.
        usort( $events, function ( $a, $b ) {
            return strcmp( (string) ( $b['ts'] ?? '' ), (string) ( $a['ts'] ?? '' ) );
        } );

        $available_modules = $this->plugin->get_available_modules();
        $module_choices    = array();
        foreach ( $available_modules as $id => $def ) {
            $module_choices[ $id ] = isset( $def['name'] ) ? (string) $def['name'] : (string) $id;
        }
        // Add "core" pseudo-module so non-module events have a home.
        $module_choices['core'] = __( 'Core (plugin)', 'blaminhor-essentials' );
        ksort( $module_choices );

        // Export payload (used for the Copy button + download link).
        $export        = $logs->export_json( $filters );
        $export['environment'] = $this->collect_environment();
        $export_pretty = wp_json_encode( $export, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );

        $download_url = wp_nonce_url(
            admin_url( 'admin-ajax.php?action=blaminhor_essentials_logs_download' )
                . '&module=' . rawurlencode( $filters['module'] )
                . '&level=' . rawurlencode( $filters['level'] )
                . '&search=' . rawurlencode( $filters['search'] ),
            'blaminhor_essentials_logs_download',
            '_be_logs_dl_nonce'
        );

        ?>
        <div class="wrap blaminhor-essentials-wrap">
            <div class="blaminhor-essentials-header">
                <h1><?php esc_html_e( 'Logs', 'blaminhor-essentials' ); ?></h1>
                <p class="description">
                    <?php esc_html_e( 'Capture targeted debug events from every Blaminhor Essentials module. Off by default. Turn it on, reproduce a bug, then copy or download the JSON to share for diagnosis.', 'blaminhor-essentials' ); ?>
                </p>
            </div>

            <?php settings_errors( 'be_logs' ); ?>

            <div class="blaminhor-essentials-section">
                <h2><?php esc_html_e( 'Collection settings', 'blaminhor-essentials' ); ?></h2>
                <form method="post" action="">
                    <?php wp_nonce_field( 'blaminhor_essentials_logs', '_be_logs_nonce' ); ?>
                    <input type="hidden" name="be_logs_action" value="save">

                    <table class="form-table">
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Enable log collection', 'blaminhor-essentials' ); ?></th>
                            <td>
                                <label>
                                    <input type="checkbox" name="be_logs_enabled" value="1" <?php checked( $settings['enabled'] ); ?>>
                                    <?php esc_html_e( 'Append events to a rolling 24-hour log file (admin only).', 'blaminhor-essentials' ); ?>
                                </label>
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Minimum level', 'blaminhor-essentials' ); ?></th>
                            <td>
                                <select name="be_logs_min_level">
                                    <option value="error"   <?php selected( $settings['min_level'], 'error' ); ?>><?php esc_html_e( 'Error only', 'blaminhor-essentials' ); ?></option>
                                    <option value="warning" <?php selected( $settings['min_level'], 'warning' ); ?>><?php esc_html_e( 'Warning + Error (recommended)', 'blaminhor-essentials' ); ?></option>
                                    <option value="info"    <?php selected( $settings['min_level'], 'info' ); ?>><?php esc_html_e( 'Info + Warning + Error', 'blaminhor-essentials' ); ?></option>
                                    <option value="debug"   <?php selected( $settings['min_level'], 'debug' ); ?>><?php esc_html_e( 'Everything (debug)', 'blaminhor-essentials' ); ?></option>
                                </select>
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Modules to collect', 'blaminhor-essentials' ); ?></th>
                            <td>
                                <p class="description" style="margin-top:0;"><?php esc_html_e( 'Leave all unchecked to collect every module. Check specific modules to limit collection.', 'blaminhor-essentials' ); ?></p>
                                <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:6px;margin-top:8px;">
                                    <?php foreach ( $module_choices as $id => $name ) : ?>
                                        <label>
                                            <input type="checkbox" name="be_logs_modules[]" value="<?php echo esc_attr( $id ); ?>" <?php checked( in_array( $id, (array) $settings['modules'], true ) ); ?>>
                                            <?php echo esc_html( $name ); ?>
                                        </label>
                                    <?php endforeach; ?>
                                </div>
                            </td>
                        </tr>
                    </table>
                    <p class="submit">
                        <button type="submit" class="button button-primary"><?php esc_html_e( 'Save settings', 'blaminhor-essentials' ); ?></button>
                    </p>
                </form>
            </div>

            <div class="blaminhor-essentials-section">
                <h2><?php esc_html_e( 'Recorded events', 'blaminhor-essentials' ); ?> <span style="color:#646970;font-weight:400;">(<?php echo count( $events ); ?>)</span></h2>

                <form method="get" action="" style="margin-bottom:12px;">
                    <input type="hidden" name="page" value="blaminhor-essentials-logs">
                    <select name="module">
                        <option value=""><?php esc_html_e( 'All modules', 'blaminhor-essentials' ); ?></option>
                        <?php foreach ( $module_choices as $id => $name ) : ?>
                            <option value="<?php echo esc_attr( $id ); ?>" <?php selected( $filters['module'], $id ); ?>><?php echo esc_html( $name ); ?></option>
                        <?php endforeach; ?>
                    </select>
                    <select name="level">
                        <option value=""><?php esc_html_e( 'All levels', 'blaminhor-essentials' ); ?></option>
                        <option value="error"   <?php selected( $filters['level'], 'error' ); ?>>error</option>
                        <option value="warning" <?php selected( $filters['level'], 'warning' ); ?>>warning</option>
                        <option value="info"    <?php selected( $filters['level'], 'info' ); ?>>info</option>
                        <option value="debug"   <?php selected( $filters['level'], 'debug' ); ?>>debug</option>
                    </select>
                    <input type="search" name="search" value="<?php echo esc_attr( $filters['search'] ); ?>" placeholder="<?php esc_attr_e( 'Search…', 'blaminhor-essentials' ); ?>" class="regular-text">
                    <button type="submit" class="button"><?php esc_html_e( 'Filter', 'blaminhor-essentials' ); ?></button>
                    <?php if ( $filters['module'] || $filters['level'] || $filters['search'] ) : ?>
                        <a href="<?php echo esc_url( admin_url( 'admin.php?page=blaminhor-essentials-logs' ) ); ?>" class="button"><?php esc_html_e( 'Reset', 'blaminhor-essentials' ); ?></a>
                    <?php endif; ?>
                </form>

                <p>
                    <button type="button" class="button button-primary" id="be-logs-copy"><?php esc_html_e( 'Copy JSON', 'blaminhor-essentials' ); ?></button>
                    <a href="<?php echo esc_url( $download_url ); ?>" class="button"><?php esc_html_e( 'Download JSON', 'blaminhor-essentials' ); ?></a>
                    <form method="post" action="" style="display:inline-block;margin-left:8px;">
                        <?php wp_nonce_field( 'blaminhor_essentials_logs', '_be_logs_nonce' ); ?>
                        <input type="hidden" name="be_logs_action" value="clear">
                        <button type="submit" class="button" onclick="return confirm('<?php echo esc_js( __( 'Clear the entire log file? This cannot be undone.', 'blaminhor-essentials' ) ); ?>');"><?php esc_html_e( 'Clear log', 'blaminhor-essentials' ); ?></button>
                    </form>
                </p>

                <?php if ( empty( $events ) ) : ?>
                    <p class="description">
                        <?php
                        if ( ! $settings['enabled'] ) {
                            esc_html_e( 'Log collection is currently OFF. Enable it above, reproduce the issue, then come back here.', 'blaminhor-essentials' );
                        } else {
                            esc_html_e( 'No events recorded yet in the past 24 hours.', 'blaminhor-essentials' );
                        }
                        ?>
                    </p>
                <?php else : ?>
                    <table class="widefat striped">
                        <thead>
                            <tr>
                                <th style="width:160px;"><?php esc_html_e( 'Time (UTC)', 'blaminhor-essentials' ); ?></th>
                                <th style="width:80px;"><?php esc_html_e( 'Level', 'blaminhor-essentials' ); ?></th>
                                <th style="width:120px;"><?php esc_html_e( 'Module', 'blaminhor-essentials' ); ?></th>
                                <th style="width:180px;"><?php esc_html_e( 'Event', 'blaminhor-essentials' ); ?></th>
                                <th><?php esc_html_e( 'Message', 'blaminhor-essentials' ); ?></th>
                            </tr>
                        </thead>
                        <tbody>
                        <?php foreach ( $events as $e ) : ?>
                            <tr>
                                <td><code><?php echo esc_html( (string) ( $e['ts'] ?? '' ) ); ?></code></td>
                                <td><span style="font-weight:600;color:<?php echo esc_attr( $this->level_color( (string) ( $e['level'] ?? '' ) ) ); ?>;"><?php echo esc_html( (string) ( $e['level'] ?? '' ) ); ?></span></td>
                                <td><?php echo esc_html( (string) ( $e['module'] ?? '' ) ); ?></td>
                                <td><code><?php echo esc_html( (string) ( $e['event'] ?? '' ) ); ?></code></td>
                                <td>
                                    <?php echo esc_html( (string) ( $e['message'] ?? '' ) ); ?>
                                    <?php if ( ! empty( $e['context'] ) ) : ?>
                                        <details style="margin-top:4px;"><summary style="cursor:pointer;color:#646970;"><?php esc_html_e( 'context', 'blaminhor-essentials' ); ?></summary>
                                            <pre style="white-space:pre-wrap;margin:4px 0 0;font-size:11px;background:#f6f7f7;padding:6px;border-radius:3px;"><?php echo esc_html( (string) wp_json_encode( $e['context'], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) ); ?></pre>
                                        </details>
                                    <?php endif; ?>
                                </td>
                            </tr>
                        <?php endforeach; ?>
                        </tbody>
                    </table>
                <?php endif; ?>
            </div>

            <textarea id="be-logs-export-payload" style="display:none;"><?php echo esc_textarea( (string) $export_pretty ); ?></textarea>
            <script>
            (function(){
                var btn = document.getElementById('be-logs-copy');
                if (!btn) { return; }
                btn.addEventListener('click', function () {
                    var src = document.getElementById('be-logs-export-payload');
                    if (!src) { return; }
                    var text = src.value;
                    if (navigator.clipboard && navigator.clipboard.writeText) {
                        navigator.clipboard.writeText(text).then(function () {
                            btn.textContent = <?php echo wp_json_encode( __( 'Copied!', 'blaminhor-essentials' ) ); ?>;
                            setTimeout(function () { btn.textContent = <?php echo wp_json_encode( __( 'Copy JSON', 'blaminhor-essentials' ) ); ?>; }, 1500);
                        });
                    } else {
                        // Fallback for older browsers.
                        src.style.display = 'block';
                        src.select();
                        try { document.execCommand('copy'); } catch (e) {}
                        src.style.display = 'none';
                    }
                });
            })();
            </script>
        </div>
        <?php
    }

    private function level_color( $level ) {
        switch ( $level ) {
            case 'error':   return '#d63638';
            case 'warning': return '#dba617';
            case 'info':    return '#2271b1';
            default:        return '#646970';
        }
    }

    /**
     * Capture environment metadata so an exported log is self-sufficient
     * for diagnosis: server / theme / active plugins / Cloudflare presence /
     * key PHP limits. The user shouldn't have to copy any of this manually.
     */
    private function collect_environment() {
        $active_plugins = (array) get_option( 'active_plugins', array() );
        $theme          = wp_get_theme();
        $upload_dir     = wp_upload_dir();

        // Best-effort Cloudflare detection: a self-loopback HEAD with a short
        // timeout. If something else handled the request we'll see cf-ray etc.
        $cf_present = null;
        $cf_ray     = null;
        $head       = wp_remote_head( home_url( '/' ), array( 'timeout' => 5, 'redirection' => 1, 'sslverify' => false ) );
        if ( ! is_wp_error( $head ) ) {
            $headers    = wp_remote_retrieve_headers( $head );
            $cf_present = isset( $headers['cf-ray'] ) || isset( $headers['server'] ) && false !== stripos( (string) $headers['server'], 'cloudflare' );
            $cf_ray     = isset( $headers['cf-ray'] ) ? (string) $headers['cf-ray'] : null;
        }

        return array(
            'server_software'    => isset( $_SERVER['SERVER_SOFTWARE'] ) ? (string) $_SERVER['SERVER_SOFTWARE'] : 'unknown', // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
            'php_version'        => PHP_VERSION,
            'php_memory_limit'   => ini_get( 'memory_limit' ),
            'php_max_execution'  => ini_get( 'max_execution_time' ),
            'php_post_max_size'  => ini_get( 'post_max_size' ),
            'php_upload_max'     => ini_get( 'upload_max_filesize' ),
            'wp_version'         => get_bloginfo( 'version' ),
            'wp_debug'           => defined( 'WP_DEBUG' ) && WP_DEBUG,
            'wp_cache'           => defined( 'WP_CACHE' ) && WP_CACHE,
            'multisite'          => is_multisite(),
            'home_url'           => home_url( '/' ),
            'site_url'           => site_url(),
            'theme'              => array(
                'name'    => $theme ? $theme->get( 'Name' ) : '',
                'version' => $theme ? $theme->get( 'Version' ) : '',
                'parent'  => $theme && $theme->parent() ? $theme->parent()->get( 'Stylesheet' ) : '',
            ),
            'active_plugins'     => array_values( $active_plugins ),
            'cloudflare_present' => $cf_present,
            'cloudflare_ray'     => $cf_ray,
            'uploads_basedir'    => isset( $upload_dir['basedir'] ) ? (string) $upload_dir['basedir'] : '',
            'uploads_writable'   => isset( $upload_dir['basedir'] ) ? is_writable( $upload_dir['basedir'] ) : false,
        );
    }

    /**
     * Endpoint for the "Download JSON" button. Streams the same payload as
     * the Copy button as an attachment named blaminhor-logs-{site}-{ts}.json.
     */
    public function ajax_logs_download() {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( esc_html__( 'Unauthorized', 'blaminhor-essentials' ), '', array( 'response' => 403 ) );
        }
        check_admin_referer( 'blaminhor_essentials_logs_download', '_be_logs_dl_nonce' );

        $filters = array(
            'module' => isset( $_GET['module'] ) ? sanitize_key( wp_unslash( $_GET['module'] ) ) : '',
            'level'  => isset( $_GET['level'] ) ? sanitize_key( wp_unslash( $_GET['level'] ) ) : '',
            'search' => isset( $_GET['search'] ) ? sanitize_text_field( wp_unslash( $_GET['search'] ) ) : '',
        );

        $payload = Blaminhor_Essentials_Logs::instance()->export_json( $filters );
        $payload['environment'] = $this->collect_environment();

        $host_slug = preg_replace( '/[^a-z0-9\-]/i', '-', wp_parse_url( home_url(), PHP_URL_HOST ) );
        $filename  = 'blaminhor-logs-' . $host_slug . '-' . gmdate( 'Ymd-His' ) . '.json';

        nocache_headers();
        header( 'Content-Type: application/json; charset=utf-8' );
        header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
        echo wp_json_encode( $payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); // phpcs:ignore WordPress.Security.EscapeOutput
        exit;
    }

    /**
     * Record a client-side event into the structured log. Called by JS at
     * critical decision points (e.g. Critical CSS iframe lifecycle) so we can
     * see what happened inside the user's browser, not just on the server.
     *
     * Required POST: nonce, module, level, event, message; optional: context (JSON).
     */
    public function ajax_logs_record_client() {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'message' => 'Unauthorized' ) );
        }
        check_ajax_referer( 'blaminhor_essentials_admin', 'nonce' );

        $logs = Blaminhor_Essentials_Logs::instance();
        if ( ! $logs->is_enabled() ) {
            // Silently no-op when logging is off — JS callers don't need to care.
            wp_send_json_success();
        }

        $module  = isset( $_POST['module'] ) ? sanitize_key( wp_unslash( $_POST['module'] ) ) : 'core';
        $level   = isset( $_POST['level'] ) ? sanitize_key( wp_unslash( $_POST['level'] ) ) : 'info';
        $event   = isset( $_POST['event'] ) ? sanitize_key( wp_unslash( $_POST['event'] ) ) : 'client_event';
        $message = isset( $_POST['message'] ) ? sanitize_text_field( wp_unslash( $_POST['message'] ) ) : '';
        $context = array();
        if ( isset( $_POST['context'] ) ) {
            $raw = wp_unslash( $_POST['context'] );
            if ( is_string( $raw ) ) {
                $decoded = json_decode( $raw, true );
                if ( is_array( $decoded ) ) {
                    $context = $decoded;
                }
            } elseif ( is_array( $raw ) ) {
                $context = $raw;
            }
        }
        // Tag the event source so we can tell server-side from client-side at a glance.
        $context['origin'] = 'client';

        $logs->record( $module, $level, $event, $message, $context );
        wp_send_json_success();
    }

    /* ---------------------------------------------------------------- */
    /* Cross-module Advanced page (Logs + WP-CLI tabs)                  */
    /* ---------------------------------------------------------------- */

    /**
     * BC shim: users who bookmarked ?page=blaminhor-essentials-logs land on
     * the Logs tab of the new Advanced page. Preserves filters in the URL.
     */
    public function maybe_redirect_legacy_logs_page() {
        if ( ! is_admin() ) {
            return;
        }
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only redirect dispatch.
        if ( ! isset( $_GET['page'] ) || 'blaminhor-essentials-logs' !== $_GET['page'] ) {
            return;
        }
        $args = array(
            'page' => 'blaminhor-essentials-advanced',
            'tab'  => 'logs',
        );
        foreach ( array( 'module', 'level', 'search' ) as $key ) {
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter pass-through.
            if ( isset( $_GET[ $key ] ) ) {
                $args[ $key ] = sanitize_text_field( wp_unslash( $_GET[ $key ] ) );
            }
        }
        wp_safe_redirect( add_query_arg( $args, admin_url( 'admin.php' ) ) );
        exit;
    }

    /**
     * Render the Blaminhor → Advanced page. Both tab contents are emitted
     * (Logs + WP-CLI); the active one is decided server-side from ?tab=…
     * so the page is bookmarkable.
     */
    public function render_advanced_page() {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( esc_html__( 'You do not have permission to access this page.', 'blaminhor-essentials' ) );
        }

        $tabs = array(
            'logs'   => __( 'Logs', 'blaminhor-essentials' ),
            'wp-cli' => __( 'WP-CLI', 'blaminhor-essentials' ),
        );
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only tab selection.
        $current = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'logs';
        if ( ! isset( $tabs[ $current ] ) ) {
            $current = 'logs';
        }

        ?>
        <div class="wrap blaminhor-essentials-wrap">
            <div class="blaminhor-essentials-header">
                <h1><?php esc_html_e( 'Advanced', 'blaminhor-essentials' ); ?></h1>
                <p class="blaminhor-essentials-tagline">
                    <?php esc_html_e( 'Diagnostic logs and WP-CLI reference for Blaminhor Essentials modules.', 'blaminhor-essentials' ); ?>
                </p>
            </div>

            <div class="blaminhor-essentials-tabs-wrapper">
                <div class="blaminhor-essentials-tabs">
                    <?php
                    foreach ( $tabs as $slug => $label ) :
                        $url = add_query_arg(
                            array(
                                'page' => 'blaminhor-essentials-advanced',
                                'tab'  => $slug,
                            ),
                            admin_url( 'admin.php' )
                        );
                        ?>
                        <a class="blaminhor-essentials-tab <?php echo $current === $slug ? 'active' : ''; ?>"
                            data-tab="<?php echo esc_attr( $slug ); ?>"
                            href="<?php echo esc_url( $url ); ?>">
                            <?php echo esc_html( $label ); ?>
                        </a>
                    <?php endforeach; ?>
                </div>

                <div class="blaminhor-essentials-tab-content <?php echo 'logs' === $current ? 'active' : ''; ?>"
                    data-tab-content="logs">
                    <?php $this->render_advanced_tab_logs(); ?>
                </div>

                <div class="blaminhor-essentials-tab-content <?php echo 'wp-cli' === $current ? 'active' : ''; ?>"
                    data-tab-content="wp-cli">
                    <?php $this->render_advanced_tab_wp_cli(); ?>
                </div>
            </div>
        </div>
        <?php
    }

    /**
     * Render the Logs tab content (settings, filters, events table, action
     * buttons). Posts back to ?page=blaminhor-essentials-advanced&tab=logs.
     */
    private function render_advanced_tab_logs() {
        $logs = Blaminhor_Essentials_Logs::instance();

        // Handle settings save / clear (POST).
        if ( ! empty( $_POST ) && check_admin_referer( 'blaminhor_essentials_logs', '_be_logs_nonce' ) ) {
            if ( isset( $_POST['be_logs_action'] ) && 'clear' === $_POST['be_logs_action'] ) {
                $logs->clear();
                add_settings_error( 'be_logs', 'be_logs_cleared', __( 'Log file cleared.', 'blaminhor-essentials' ), 'updated' );
            } elseif ( isset( $_POST['be_logs_action'] ) && 'save' === $_POST['be_logs_action'] ) {
                $logs->update_settings( array(
                    'enabled'   => ! empty( $_POST['be_logs_enabled'] ),
                    'modules'   => isset( $_POST['be_logs_modules'] ) ? (array) $_POST['be_logs_modules'] : array(),
                    'min_level' => isset( $_POST['be_logs_min_level'] ) ? sanitize_text_field( wp_unslash( $_POST['be_logs_min_level'] ) ) : 'warning',
                ) );
                add_settings_error( 'be_logs', 'be_logs_saved', __( 'Log settings saved.', 'blaminhor-essentials' ), 'updated' );
            }
        }

        $settings = $logs->get_settings();
        $filters  = array(
            'module' => isset( $_GET['module'] ) ? sanitize_key( wp_unslash( $_GET['module'] ) ) : '',
            'level'  => isset( $_GET['level'] ) ? sanitize_key( wp_unslash( $_GET['level'] ) ) : '',
            'search' => isset( $_GET['search'] ) ? sanitize_text_field( wp_unslash( $_GET['search'] ) ) : '',
        );
        $events   = $logs->read( $filters );
        // Sort newest first for the UI.
        usort( $events, function ( $a, $b ) {
            $a_ts = isset( $a['ts'] ) ? (string) $a['ts'] : '';
            $b_ts = isset( $b['ts'] ) ? (string) $b['ts'] : '';
            return strcmp( $b_ts, $a_ts );
        } );

        $available_modules = $this->plugin->get_available_modules();
        $module_choices    = array();
        foreach ( $available_modules as $id => $def ) {
            $module_choices[ $id ] = isset( $def['name'] ) ? (string) $def['name'] : (string) $id;
        }
        $module_choices['core'] = __( 'Core (plugin)', 'blaminhor-essentials' );
        ksort( $module_choices );

        // Export payload (used for the Copy button + download link).
        $export                = $logs->export_json( $filters );
        $export['environment'] = $this->collect_environment();
        $export_pretty         = wp_json_encode( $export, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );

        $download_url = wp_nonce_url(
            admin_url( 'admin-ajax.php?action=blaminhor_essentials_logs_download' )
                . '&module=' . rawurlencode( $filters['module'] )
                . '&level=' . rawurlencode( $filters['level'] )
                . '&search=' . rawurlencode( $filters['search'] ),
            'blaminhor_essentials_logs_download',
            '_be_logs_dl_nonce'
        );

        ?>
        <p class="description">
            <?php esc_html_e( 'Capture targeted debug events from every Blaminhor Essentials module. Off by default. Turn it on, reproduce a bug, then copy or download the JSON to share for diagnosis.', 'blaminhor-essentials' ); ?>
        </p>

        <?php settings_errors( 'be_logs' ); ?>

        <h3><?php esc_html_e( 'Collection settings', 'blaminhor-essentials' ); ?></h3>
        <form method="post" action="">
            <?php wp_nonce_field( 'blaminhor_essentials_logs', '_be_logs_nonce' ); ?>
            <input type="hidden" name="be_logs_action" value="save">

            <table class="form-table">
                <tr>
                    <th scope="row"><?php esc_html_e( 'Enable log collection', 'blaminhor-essentials' ); ?></th>
                    <td>
                        <label>
                            <input type="checkbox" name="be_logs_enabled" value="1" <?php checked( $settings['enabled'] ); ?>>
                            <?php esc_html_e( 'Append events to a rolling 24-hour log file (admin only).', 'blaminhor-essentials' ); ?>
                        </label>
                    </td>
                </tr>
                <tr>
                    <th scope="row"><?php esc_html_e( 'Minimum level', 'blaminhor-essentials' ); ?></th>
                    <td>
                        <select name="be_logs_min_level">
                            <option value="error"   <?php selected( $settings['min_level'], 'error' ); ?>><?php esc_html_e( 'Error only', 'blaminhor-essentials' ); ?></option>
                            <option value="warning" <?php selected( $settings['min_level'], 'warning' ); ?>><?php esc_html_e( 'Warning + Error (recommended)', 'blaminhor-essentials' ); ?></option>
                            <option value="info"    <?php selected( $settings['min_level'], 'info' ); ?>><?php esc_html_e( 'Info + Warning + Error', 'blaminhor-essentials' ); ?></option>
                            <option value="debug"   <?php selected( $settings['min_level'], 'debug' ); ?>><?php esc_html_e( 'Everything (debug)', 'blaminhor-essentials' ); ?></option>
                        </select>
                    </td>
                </tr>
                <tr>
                    <th scope="row"><?php esc_html_e( 'Modules to collect', 'blaminhor-essentials' ); ?></th>
                    <td>
                        <p class="description" style="margin-top:0;"><?php esc_html_e( 'Leave all unchecked to collect every module. Check specific modules to limit collection.', 'blaminhor-essentials' ); ?></p>
                        <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:6px;margin-top:8px;">
                            <?php foreach ( $module_choices as $id => $name ) : ?>
                                <label>
                                    <input type="checkbox" name="be_logs_modules[]" value="<?php echo esc_attr( $id ); ?>" <?php checked( in_array( $id, (array) $settings['modules'], true ) ); ?>>
                                    <?php echo esc_html( $name ); ?>
                                </label>
                            <?php endforeach; ?>
                        </div>
                    </td>
                </tr>
            </table>
            <p class="submit">
                <button type="submit" class="button button-primary"><?php esc_html_e( 'Save settings', 'blaminhor-essentials' ); ?></button>
            </p>
        </form>

        <hr>

        <h3><?php esc_html_e( 'Recorded events', 'blaminhor-essentials' ); ?> <span style="color:#646970;font-weight:400;">(<?php echo count( $events ); ?>)</span></h3>

        <form method="get" action="" style="margin-bottom:12px;">
            <input type="hidden" name="page" value="blaminhor-essentials-advanced">
            <input type="hidden" name="tab" value="logs">
            <select name="module">
                <option value=""><?php esc_html_e( 'All modules', 'blaminhor-essentials' ); ?></option>
                <?php foreach ( $module_choices as $id => $name ) : ?>
                    <option value="<?php echo esc_attr( $id ); ?>" <?php selected( $filters['module'], $id ); ?>><?php echo esc_html( $name ); ?></option>
                <?php endforeach; ?>
            </select>
            <select name="level">
                <option value=""><?php esc_html_e( 'All levels', 'blaminhor-essentials' ); ?></option>
                <option value="error"   <?php selected( $filters['level'], 'error' ); ?>>error</option>
                <option value="warning" <?php selected( $filters['level'], 'warning' ); ?>>warning</option>
                <option value="info"    <?php selected( $filters['level'], 'info' ); ?>>info</option>
                <option value="debug"   <?php selected( $filters['level'], 'debug' ); ?>>debug</option>
            </select>
            <input type="search" name="search" value="<?php echo esc_attr( $filters['search'] ); ?>" placeholder="<?php esc_attr_e( 'Search…', 'blaminhor-essentials' ); ?>" class="regular-text">
            <button type="submit" class="button"><?php esc_html_e( 'Filter', 'blaminhor-essentials' ); ?></button>
            <?php if ( $filters['module'] || $filters['level'] || $filters['search'] ) : ?>
                <a href="<?php echo esc_url( admin_url( 'admin.php?page=blaminhor-essentials-advanced&tab=logs' ) ); ?>" class="button"><?php esc_html_e( 'Reset', 'blaminhor-essentials' ); ?></a>
            <?php endif; ?>
        </form>

        <p>
            <button type="button" class="button button-primary" id="be-logs-copy"><?php esc_html_e( 'Copy JSON', 'blaminhor-essentials' ); ?></button>
            <a href="<?php echo esc_url( $download_url ); ?>" class="button"><?php esc_html_e( 'Download JSON', 'blaminhor-essentials' ); ?></a>
            <form method="post" action="" style="display:inline-block;margin-left:8px;">
                <?php wp_nonce_field( 'blaminhor_essentials_logs', '_be_logs_nonce' ); ?>
                <input type="hidden" name="be_logs_action" value="clear">
                <button type="submit" class="button" onclick="return confirm('<?php echo esc_js( __( 'Clear the entire log file? This cannot be undone.', 'blaminhor-essentials' ) ); ?>');"><?php esc_html_e( 'Clear log', 'blaminhor-essentials' ); ?></button>
            </form>
        </p>

        <?php if ( empty( $events ) ) : ?>
            <p class="description">
                <?php
                if ( ! $settings['enabled'] ) {
                    esc_html_e( 'Log collection is currently OFF. Enable it above, reproduce the issue, then come back here.', 'blaminhor-essentials' );
                } else {
                    esc_html_e( 'No events recorded yet in the past 24 hours.', 'blaminhor-essentials' );
                }
                ?>
            </p>
        <?php else : ?>
            <table class="widefat striped">
                <thead>
                    <tr>
                        <th style="width:160px;"><?php esc_html_e( 'Time (UTC)', 'blaminhor-essentials' ); ?></th>
                        <th style="width:80px;"><?php esc_html_e( 'Level', 'blaminhor-essentials' ); ?></th>
                        <th style="width:120px;"><?php esc_html_e( 'Module', 'blaminhor-essentials' ); ?></th>
                        <th style="width:180px;"><?php esc_html_e( 'Event', 'blaminhor-essentials' ); ?></th>
                        <th><?php esc_html_e( 'Message', 'blaminhor-essentials' ); ?></th>
                    </tr>
                </thead>
                <tbody>
                <?php foreach ( $events as $e ) : ?>
                    <tr>
                        <td><code><?php echo esc_html( isset( $e['ts'] ) ? (string) $e['ts'] : '' ); ?></code></td>
                        <td><span style="font-weight:600;color:<?php echo esc_attr( $this->level_color( isset( $e['level'] ) ? (string) $e['level'] : '' ) ); ?>;"><?php echo esc_html( isset( $e['level'] ) ? (string) $e['level'] : '' ); ?></span></td>
                        <td><?php echo esc_html( isset( $e['module'] ) ? (string) $e['module'] : '' ); ?></td>
                        <td><code><?php echo esc_html( isset( $e['event'] ) ? (string) $e['event'] : '' ); ?></code></td>
                        <td>
                            <?php echo esc_html( isset( $e['message'] ) ? (string) $e['message'] : '' ); ?>
                            <?php if ( ! empty( $e['context'] ) ) : ?>
                                <details style="margin-top:4px;"><summary style="cursor:pointer;color:#646970;"><?php esc_html_e( 'context', 'blaminhor-essentials' ); ?></summary>
                                    <pre style="white-space:pre-wrap;margin:4px 0 0;font-size:11px;background:#f6f7f7;padding:6px;border-radius:3px;"><?php echo esc_html( (string) wp_json_encode( $e['context'], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) ); ?></pre>
                                </details>
                            <?php endif; ?>
                        </td>
                    </tr>
                <?php endforeach; ?>
                </tbody>
            </table>
        <?php endif; ?>

        <textarea id="be-logs-export-payload" style="display:none;"><?php echo esc_textarea( (string) $export_pretty ); ?></textarea>
        <script>
        (function(){
            var btn = document.getElementById('be-logs-copy');
            if (!btn) { return; }
            btn.addEventListener('click', function () {
                var src = document.getElementById('be-logs-export-payload');
                if (!src) { return; }
                var text = src.value;
                if (navigator.clipboard && navigator.clipboard.writeText) {
                    navigator.clipboard.writeText(text).then(function () {
                        btn.textContent = <?php echo wp_json_encode( __( 'Copied!', 'blaminhor-essentials' ) ); ?>;
                        setTimeout(function () { btn.textContent = <?php echo wp_json_encode( __( 'Copy JSON', 'blaminhor-essentials' ) ); ?>; }, 1500);
                    });
                } else {
                    src.style.display = 'block';
                    src.select();
                    try { document.execCommand('copy'); } catch (e) {}
                    src.style.display = 'none';
                }
            });
        })();
        </script>
        <?php
    }

    /**
     * Render the WP-CLI tab: server-side WP-CLI detection + per-module
     * command listing with copyable snippets.
     */
    private function render_advanced_tab_wp_cli() {
        $detection = $this->detect_wp_cli();
        $commands  = $this->collect_active_modules_cli_commands();
        ?>
        <p class="description">
            <?php esc_html_e( 'WP-CLI is a command-line interface for WordPress. The commands below are exposed by your currently active Blaminhor Essentials modules.', 'blaminhor-essentials' ); ?>
        </p>

        <h3><?php esc_html_e( 'WP-CLI on this server', 'blaminhor-essentials' ); ?></h3>
        <?php $this->render_wp_cli_detection_banner( $detection ); ?>

        <hr>

        <h3><?php esc_html_e( 'Available commands', 'blaminhor-essentials' ); ?> <span style="color:#646970;font-weight:400;">(<?php echo (int) $this->count_commands( $commands ); ?>)</span></h3>
        <?php $this->render_wp_cli_commands_list( $commands ); ?>
        <?php
    }

    /**
     * Detect whether WP-CLI is callable from this server. Best-effort because
     * a typical PHP-FPM process is NOT a WP-CLI context — we look for the
     * binary on disk via `which wp` when exec() is allowed, otherwise we
     * report "unknown" rather than guessing wrong.
     *
     * @return array
     */
    private function detect_wp_cli() {
        $exec_disabled = ! function_exists( 'exec' )
            || in_array( 'exec', array_map( 'trim', explode( ',', (string) ini_get( 'disable_functions' ) ) ), true );

        if ( $exec_disabled ) {
            return array(
                'status'        => 'unknown',
                'exec_disabled' => true,
            );
        }

        $output = array();
        $code   = 1;
        @exec( 'command -v wp 2>/dev/null', $output, $code );
        $binary = ( 0 === $code && ! empty( $output[0] ) ) ? trim( (string) $output[0] ) : '';
        if ( '' === $binary ) {
            return array( 'status' => 'missing' );
        }

        $version_output = array();
        $version_code   = 1;
        @exec( escapeshellarg( $binary ) . ' --version 2>/dev/null', $version_output, $version_code );
        $version = '';
        if ( 0 === $version_code && ! empty( $version_output[0] ) ) {
            if ( preg_match( '/WP-CLI\s+([0-9][\w.\-]*)/i', (string) $version_output[0], $m ) ) {
                $version = (string) $m[1];
            }
        }

        return array(
            'status'  => 'present',
            'binary'  => $binary,
            'version' => $version,
        );
    }

    /**
     * Render the detection banner above the commands list.
     *
     * @param array $detection Result of detect_wp_cli().
     */
    private function render_wp_cli_detection_banner( array $detection ) {
        $status = isset( $detection['status'] ) ? (string) $detection['status'] : 'unknown';
        if ( 'present' === $status ) {
            $msg = sprintf(
                /* translators: 1: WP-CLI binary path, 2: WP-CLI version (may be empty) */
                __( 'WP-CLI detected at %1$s%2$s.', 'blaminhor-essentials' ),
                '<code>' . esc_html( (string) $detection['binary'] ) . '</code>',
                ! empty( $detection['version'] ) ? ' — v' . esc_html( (string) $detection['version'] ) : ''
            );
            echo '<div class="notice notice-success inline" style="margin:0;"><p>' . wp_kses_post( $msg ) . '</p></div>';
            return;
        }

        if ( 'missing' === $status ) {
            echo '<div class="notice notice-warning inline" style="margin:0;"><p>'
                . esc_html__( 'WP-CLI was not found on this server. The commands below will not run until you install it.', 'blaminhor-essentials' )
                . ' <a href="https://wp-cli.org/#installing" target="_blank" rel="noopener noreferrer">'
                . esc_html__( 'Installation guide', 'blaminhor-essentials' )
                . '</a>.</p></div>';
            return;
        }

        echo '<div class="notice notice-info inline" style="margin:0;"><p>'
            . esc_html__( 'Cannot probe WP-CLI: the PHP exec() function is disabled on this host. The commands below assume WP-CLI is installed.', 'blaminhor-essentials' )
            . ' <a href="https://wp-cli.org/#installing" target="_blank" rel="noopener noreferrer">'
            . esc_html__( 'Installation guide', 'blaminhor-essentials' )
            . '</a>.</p></div>';
    }

    /**
     * Walk every active module and load its cli/commands.php manifest if
     * present. Returns the commands keyed by module ID, with the module's
     * translated display name attached so the renderer can group them.
     *
     * @return array
     */
    private function collect_active_modules_cli_commands() {
        $out            = array();
        $active_modules = $this->plugin->get_active_modules();

        // Sort modules by translated name (case + accent insensitive) for stable UI.
        $sortable = array();
        foreach ( $active_modules as $module_id => $module ) {
            $name                   = method_exists( $module, 'get_name' ) ? $module->get_name() : (string) $module_id;
            $sortable[ $module_id ] = array(
                'name' => $name,
                'sort' => strtolower( remove_accents( $name ) ),
            );
        }
        uasort( $sortable, function ( $a, $b ) {
            return strcmp( $a['sort'], $b['sort'] );
        } );

        foreach ( $sortable as $module_id => $meta ) {
            $manifest_path = BLAMINHOR_ESSENTIALS_PLUGIN_DIR . 'modules/' . $module_id . '/cli/commands.php';
            if ( ! file_exists( $manifest_path ) ) {
                continue;
            }
            $commands = include $manifest_path;
            if ( ! is_array( $commands ) || empty( $commands ) ) {
                continue;
            }
            $out[ $module_id ] = array(
                'name'     => $meta['name'],
                'commands' => $commands,
            );
        }
        return $out;
    }

    /**
     * Count total commands across the collected map.
     *
     * @param array $map Output of collect_active_modules_cli_commands().
     * @return int
     */
    private function count_commands( array $map ) {
        $n = 0;
        foreach ( $map as $entry ) {
            $n += count( (array) $entry['commands'] );
        }
        return $n;
    }

    /**
     * Render the per-module command cards with a Copy button on each example.
     *
     * @param array $map Output of collect_active_modules_cli_commands().
     */
    private function render_wp_cli_commands_list( array $map ) {
        if ( empty( $map ) ) {
            echo '<p class="description">'
                . esc_html__( 'No active module exposes WP-CLI commands. Activate at least one module to see its commands here.', 'blaminhor-essentials' )
                . '</p>';
            return;
        }

        foreach ( $map as $module_id => $entry ) {
            $cmd_count = count( (array) $entry['commands'] );
            ?>
            <details class="be-cli-module">
                <summary class="be-cli-module-summary">
                    <span class="dashicons dashicons-arrow-right-alt2"></span>
                    <span class="be-cli-module-name"><?php echo esc_html( (string) $entry['name'] ); ?></span>
                    <code class="be-cli-module-id"><?php echo esc_html( (string) $module_id ); ?></code>
                    <span class="be-cli-module-count">
                    <?php
                        echo esc_html( sprintf(
                            /* translators: %d: number of WP-CLI commands exposed by a module */
                            _n( '%d command', '%d commands', $cmd_count, 'blaminhor-essentials' ),
                            (int) $cmd_count
                        ) );
                    ?>
                    </span>
                </summary>
                <ul class="be-cli-commands">
                    <?php
                    foreach ( (array) $entry['commands'] as $cmd ) :
                        $cmd_name    = isset( $cmd['name'] ) ? (string) $cmd['name'] : '';
                        $cmd_desc    = isset( $cmd['description'] ) ? (string) $cmd['description'] : '';
                        $cmd_example = isset( $cmd['example'] ) ? (string) $cmd['example'] : $cmd_name;
                        if ( '' === $cmd_name ) {
                            continue;
                        }
                        ?>
                        <li class="be-cli-command">
                            <div class="be-cli-command-head">
                                <code class="be-cli-command-name"><?php echo esc_html( $cmd_name ); ?></code>
                            </div>
                            <?php if ( '' !== $cmd_desc ) : ?>
                                <p class="be-cli-command-desc"><?php echo esc_html( $cmd_desc ); ?></p>
                            <?php endif; ?>
                            <div class="be-cli-command-example">
                                <code class="be-cli-snippet"><?php echo esc_html( $cmd_example ); ?></code>
                                <button type="button" class="button button-small be-cli-copy"
                                    data-copy="<?php echo esc_attr( $cmd_example ); ?>"
                                    aria-label="<?php esc_attr_e( 'Copy command', 'blaminhor-essentials' ); ?>">
                                    <?php esc_html_e( 'Copy', 'blaminhor-essentials' ); ?>
                                </button>
                            </div>
                        </li>
                    <?php endforeach; ?>
                </ul>
            </details>
            <?php
        }
        ?>
        <script>
        (function () {
            document.querySelectorAll('.be-cli-copy').forEach(function (btn) {
                btn.addEventListener('click', function () {
                    var text = btn.getAttribute('data-copy') || '';
                    var original = btn.textContent;
                    var done = function () {
                        btn.textContent = <?php echo wp_json_encode( __( 'Copied!', 'blaminhor-essentials' ) ); ?>;
                        setTimeout(function () { btn.textContent = original; }, 1200);
                    };
                    if (navigator.clipboard && navigator.clipboard.writeText) {
                        navigator.clipboard.writeText(text).then(done, function () {
                            window.prompt(<?php echo wp_json_encode( __( 'Copy this command:', 'blaminhor-essentials' ) ); ?>, text);
                        });
                    } else {
                        window.prompt(<?php echo wp_json_encode( __( 'Copy this command:', 'blaminhor-essentials' ) ); ?>, text);
                    }
                });
            });
        })();
        </script>
        <?php
    }
}
