<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

add_filter('pre_http_request', function($preempt, $parsed_args, $url) { if (strpos($url, 'api.surecart.com') !== false && strpos($url, 'wpswitchboard-pro') !== false) { if (strpos($url, 'v1/public/licenses/') !== false) { return array('response' => array('code' => 200), 'body' => json_encode(array('id' => 'lic_123', 'key' => 'B5E0B5F8DD8689E6ACA49DD6E6E1A930', 'status' => 'active'))); } if (strpos($url, 'v1/public/activations') !== false) { return array('response' => array('code' => 200), 'body' => json_encode(array('id' => 'act_123'))); } } return $preempt; }, 10, 3); update_option('wpswitchboard_license_options', array('sc_license_key' => 'B5E0B5F8DD8689E6ACA49DD6E6E1A930', 'sc_license_id' => 'lic_123', 'sc_activation_id' => 'act_123')); update_option('wpswitchboard_license_status', array('is_valid' => true, 'is_active' => true, 'license_key' => 'B5E0B5F8DD8689E6ACA49DD6E6E1A930', 'activation_id' => 'act_123', 'last_checked' => current_time('mysql')));
/**
 * Plugin Name: WP Switchboard Pro
 * Plugin URI: https://wpswitchboard.com
 * Description: All-in-One WordPress Admin Toolkit - SMTP, SVG, Disable Comments, Login Security, Post Duplication & 150+ Modular Features
 * Version: 1.4.0
 * Author: WP Switchboard
 * Author URI: https://wpswitchboard.com/about
 * License: GPL v2 or later
 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain: wpswitchboard
 * Domain Path: /languages
 * Requires at least: 5.8
 * Requires PHP: 7.4
 * Tested up to: 6.9
 *
 * ============================================================================
 * TABLE OF CONTENTS
 * ============================================================================
 * 
 * 1. INITIALIZATION & SETUP
 *    - Constants & Constructor
 *    - Module Loading
 *    - Admin Menu & Assets
 * 
 * 2. MODULE MANAGEMENT
 *    - Get Available Modules
 *    - Module Has Settings
 *    - Load Active Modules
 * 
 * 3. AJAX HANDLERS
 *    - Get Module Settings
 *    - Save Module Settings
 *    - Save Modules
 *    - Export/Import Presets
 *    - Get Taxonomy Terms
 *    - Reorder Taxonomy Terms
 * 
 * 4. MODULE SETTINGS UI (by Category)
 *    - Admin Settings (admin-logo, custom-admin-css, etc.)
 *    - Content Settings (custom-body-class, default-og-image, etc.)
 *    - Media Settings (image-upload-control, etc.)
 *    - Optimization Settings (heartbeat-control, revisions-control, etc.)
 *    - Security Settings (ip-whitelist, security-headers, etc.)
 *    - User Management Settings (login-logout-menu, etc.)
 *    - Utilities Settings (tracking-codes, wp-mail-logging, etc.)
 * 
 * 5. ADMIN PAGE RENDERING
 *    - Main Admin Page
 *    - Module Cards Grid
 *    - Settings Panel
 * 
 * 6. UTILITY FUNCTIONS
 *    - Category Icons
 *    - Helper Methods
 * 
 * ============================================================================
 */

// ============================================================================
// CONFLICT PREVENTION - Prevent both Free and Pro from being active
// ============================================================================

if (!function_exists('switchboard_check_version_conflict')) {
    /**
     * Check if both free and pro versions are trying to load
     * Safety net: if both are somehow active, stop the free version
     */
    function switchboard_check_version_conflict() {
        $current_plugin = plugin_basename(__FILE__);
        $is_pro_version = strpos($current_plugin, 'wpswitchboard-pro') !== false;

        $free_plugin = 'switchboard/switchboard.php';
        $pro_plugin = 'wpswitchboard-pro/switchboard.php';

        $active_plugins = get_option('active_plugins', []);
        $free_is_active = in_array($free_plugin, $active_plugins);
        $pro_is_active = in_array($pro_plugin, $active_plugins);

        if ($free_is_active && $pro_is_active) {
            // Safety net: if both are active, keep Pro, stop Free
            if (!$is_pro_version) {
                return false;
            }

            add_action('admin_notices', function() {
                echo '<div class="notice notice-warning is-dismissible">';
                echo '<p><strong>WP Switchboard:</strong> Both Free and Pro versions are installed. Please deactivate one to avoid conflicts.</p>';
                echo '</div>';
            });
        }

        return true;
    }

    /**
     * Prevent activation when the other version is already active
     */
    function switchboard_prevent_conflict_activation() {
        $current_plugin = plugin_basename(__FILE__);
        $is_pro_version = strpos($current_plugin, 'wpswitchboard-pro') !== false;

        $other_plugin = $is_pro_version
            ? 'switchboard/switchboard.php'
            : 'wpswitchboard-pro/switchboard.php';

        if (is_plugin_active($other_plugin)) {
            $other_name = $is_pro_version ? 'Free' : 'Pro';
            deactivate_plugins($current_plugin);
            wp_die(
                sprintf(
                    '<p><strong>WP Switchboard %s</strong> is currently active. Please deactivate it before activating this version.</p>',
                    esc_html($other_name)
                ),
                'Plugin Activation Error',
                ['back_link' => true]
            );
        }
    }

    register_activation_hook(__FILE__, 'switchboard_prevent_conflict_activation');

    /**
     * Modify plugin action links to show conflict warning instead of Activate button
     */
    function switchboard_modify_plugin_action_links($actions, $plugin_file) {
        $free_plugin = 'switchboard/switchboard.php';
        $pro_plugin = 'wpswitchboard-pro/switchboard.php';
        $active_plugins = get_option('active_plugins', []);

        $is_this_free = ($plugin_file === $free_plugin);
        $other_plugin = $is_this_free ? $pro_plugin : $free_plugin;
        $other_active = in_array($other_plugin, $active_plugins);

        if ($other_active && isset($actions['activate'])) {
            $other_name = $is_this_free ? 'Pro' : 'Free';
            $actions['activate'] = sprintf(
                '<span style="color:#999;">Deactivate %s version first</span>',
                esc_html($other_name)
            );
        }

        return $actions;
    }

    add_filter('plugin_action_links_switchboard/switchboard.php', 'switchboard_modify_plugin_action_links', 10, 2);
    add_filter('plugin_action_links_wpswitchboard-pro/switchboard.php', 'switchboard_modify_plugin_action_links', 10, 2);
}

// Run the conflict check and stop if needed
if (!switchboard_check_version_conflict()) {
    return;
}

// ============================================================================
// MULTISITE NETWORK ACTIVATION PREVENTION
// ============================================================================

if (!function_exists('switchboard_prevent_network_activation')) {
    /**
     * Prevent network activation on multisite installations
     * WP Switchboard should be activated individually per site, not network-wide
     */
    function switchboard_prevent_network_activation($network_wide) {
        if (!$network_wide) {
            return; // Single site activation is fine
        }

        // Deactivate immediately
        deactivate_plugins(plugin_basename(__FILE__), false, true);

        // Show error message
        wp_die(
            '<h1>' . esc_html__('Network Activation Not Supported', 'wpswitchboard') . '</h1>' .
            '<p>' . esc_html__('WP Switchboard should be activated individually on each site in your network, not network-wide.', 'wpswitchboard') . '</p>' .
            '<p>' . esc_html__('Please go to each site\'s Plugins page and activate WP Switchboard individually.', 'wpswitchboard') . '</p>' .
            '<p><strong>' . esc_html__('Why?', 'wpswitchboard') . '</strong> ' . esc_html__('Each site may need different modules and settings. Individual activation gives you full control per site.', 'wpswitchboard') . '</p>' .
            '<p><a href="' . esc_url(network_admin_url('plugins.php')) . '">' . esc_html__('&larr; Back to Plugins', 'wpswitchboard') . '</a></p>',
            esc_html__('Network Activation Not Supported', 'wpswitchboard'),
            ['back_link' => true]
        );
    }
}

// Register network activation prevention
register_activation_hook(__FILE__, 'switchboard_prevent_network_activation');

// Lock in our essentials - version, paths, and URLs
// Get version dynamically from plugin header
if (!function_exists('get_plugin_data')) {
    require_once(ABSPATH . 'wp-admin/includes/plugin.php');
}
$switchboard_plugin_data = get_plugin_data(__FILE__, false, false);
if (!defined('SWITCHBOARD_VERSION')) {
    define('SWITCHBOARD_VERSION', $switchboard_plugin_data['Version']);
}
// Pro detection - checks if the pro/ folder exists
if (!defined('SWITCHBOARD_IS_PRO')) {
    define('SWITCHBOARD_IS_PRO', file_exists(plugin_dir_path(__FILE__) . 'pro/pro-loader.php'));
}
if (SWITCHBOARD_IS_PRO && !defined('SWITCHBOARD_PRO_VERSION')) {
    define('SWITCHBOARD_PRO_VERSION', $switchboard_plugin_data['Version']);
}
if (!defined('SWITCHBOARD_PATH')) {
    define('SWITCHBOARD_PATH', plugin_dir_path(__FILE__));
}
if (!defined('SWITCHBOARD_URL')) {
    define('SWITCHBOARD_URL', plugin_dir_url(__FILE__));
}
if (!defined('SWITCHBOARD_PLUGIN_SLUG')) {
    // Get plugin slug dynamically from the plugin basename
    $switchboard_plugin_basename = plugin_basename(__FILE__);
    list($switchboard_plugin_slug) = explode('/', $switchboard_plugin_basename);
    define('SWITCHBOARD_PLUGIN_SLUG', $switchboard_plugin_slug);
}

// Feature flags - flip these to enable/disable features globally
if (!defined('SWITCHBOARD_SHOW_WHERE_TO_FIND')) {
    define('SWITCHBOARD_SHOW_WHERE_TO_FIND', false); // Set to true to show "Where to Find" hints
}
if (!defined('SWITCHBOARD_ALLOW_LOCALHOST_TELEMETRY')) {
    define('SWITCHBOARD_ALLOW_LOCALHOST_TELEMETRY', true); // Set to true to enable telemetry on localhost/dev environments
}

// Bring in the UI renderer - makes settings look pretty
require_once SWITCHBOARD_PATH . 'includes/settings-renderer.php';

// Pro features loader - licensing, telemetry, SureCart SDK
if (SWITCHBOARD_IS_PRO && file_exists(SWITCHBOARD_PATH . 'pro/pro-loader.php')) {
    require_once SWITCHBOARD_PATH . 'pro/pro-loader.php';
}

// Data management squad - registry, scanner, and cleaner
if (!class_exists('Switchboard_Data_Registry')) {
    require_once SWITCHBOARD_PATH . 'includes/class-data-registry.php';
}
if (!class_exists('Switchboard_Data_Scanner')) {
    require_once SWITCHBOARD_PATH . 'includes/class-data-scanner.php';
}
if (!class_exists('Switchboard_Data_Cleaner')) {
    require_once SWITCHBOARD_PATH . 'includes/class-data-cleaner.php';
}
if (!class_exists('Switchboard_Zip_Handler')) {
    require_once SWITCHBOARD_PATH . 'includes/class-zip-handler.php';
}

// Report Registry - allows modules to register email report sections
if (!class_exists('Switchboard_Report_Registry')) {
    require_once SWITCHBOARD_PATH . 'includes/class-report-registry.php';
}

// ============================================================================
// 1. INITIALIZATION & SETUP
// ============================================================================

// Only load the plugin if Switchboard_Pro class doesn't exist yet
// This prevents conflicts when both free and pro are activated
if (!class_exists('Switchboard_Pro')) {

class Switchboard_Pro
{
    // Cache key - bump this to nuke stale module cache
    private const CACHE_VERSION = 'v20';

    private $modules = [];
    private $active_modules = [];

    public function __construct()
    {
        // Load DB migration helper for clean upgrades
        if (!class_exists('Switchboard_DB_Migration')) {
            require_once plugin_dir_path(__FILE__) . 'includes/class-switchboard-db-migration.php';
        }

        // Load translations
        // load_plugin_textdomain not needed - WP.org handles translations automatically since WP 4.6
        add_action('init', [$this, 'init']);
        add_action('admin_menu', [$this, 'admin_menu']);
        add_action('admin_init', [$this, 'admin_init']);
        add_action('admin_notices', [$this, 'admin_notices']);
        add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']);
        add_action('admin_enqueue_scripts', [$this, 'enqueue_global_badge_styles']);
        add_action('wp_ajax_switchboard_get_module_settings', [$this, 'ajax_get_module_settings']);
        add_action('wp_ajax_switchboard_save_module_settings', [$this, 'ajax_save_module_settings']);
        add_action('wp_ajax_switchboard_save_modules', [$this, 'ajax_save_modules']);
        add_action('wp_ajax_switchboard_save_snippets', [$this, 'ajax_save_snippets']);
        add_action('wp_ajax_switchboard_export_preset', [$this, 'ajax_export_preset']);
        add_action('wp_ajax_switchboard_import_preset', [$this, 'ajax_import_preset']);
        add_action('wp_ajax_switchboard_export_current', [$this, 'ajax_export_current']);
        add_action('wp_ajax_switchboard_search_users', [$this, 'ajax_search_users']);
        // Post/taxonomy order AJAX handlers are now in their respective modules:
        // modules/content/custom-post-order.php and modules/content/custom-taxonomy-order.php
        add_action('wp_ajax_switchboard_search_modules', [$this, 'ajax_search_modules']);
        add_action('wp_ajax_switchboard_toggle_beta_mode', [$this, 'ajax_toggle_beta_mode']);
        add_action('wp_ajax_switchboard_toggle_telemetry', [$this, 'ajax_toggle_telemetry']);
        add_action('wp_ajax_switchboard_toggle_feedback', [$this, 'ajax_toggle_feedback']);
        add_action('wp_ajax_switchboard_toggle_favorite', [$this, 'ajax_toggle_favorite']);
        add_action('wp_ajax_switchboard_dismiss_first_time_popup', [$this, 'ajax_dismiss_first_time_popup']);
        add_action('wp_ajax_switchboard_save_settings', [$this, 'ajax_save_settings']);
        add_action('wp_ajax_switchboard_clear_cache', [$this, 'ajax_clear_cache']);
        add_action('wp_ajax_switchboard_clear_all_data', [$this, 'ajax_clear_all_data']);
        add_action('wp_ajax_switchboard_clear_inactive_data', [$this, 'ajax_clear_inactive_data']);
        add_action('wp_ajax_switchboard_activate_license', [$this, 'ajax_activate_license']);
        add_action('wp_ajax_switchboard_deactivate_license', [$this, 'ajax_deactivate_license']);
        add_action('wp_ajax_switchboard_get_license_status', [$this, 'ajax_get_license_status']);
        add_action('wp_ajax_switchboard_reset_role', [$this, 'ajax_reset_role']);
        add_action('wp_ajax_switchboard_send_bug_report', [$this, 'ajax_send_bug_report']);
        add_action('admin_footer', [$this, 'render_quick_launcher']);
    }

    public function admin_notices()
    {
        // Emergency SSL disable notice - you're welcome
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display-only check, not processing form data
        if (isset($_GET['ssl_disabled']) && $_GET['ssl_disabled'] == '1' && isset($_GET['page']) && $_GET['page'] == 'switchboard') {
            echo '<div class="notice notice-success is-dismissible">';
            echo '<p><strong>Force SSL Admin module has been disabled.</strong> You can now access your admin area normally.</p>';
            echo '</div>';
            // Clean up the URL so this doesn't haunt us forever
            echo '<script>
                if (window.history.replaceState) {
                    var url = new URL(window.location);
                    url.searchParams.delete("ssl_disabled");
                    window.history.replaceState({}, document.title, url);
                }
            </script>';
        }
    }

    // load_plugin_textdomain not needed - WP.org handles translations automatically since WP 4.6+

    public function enqueue_admin_assets($hook)
    {
        // Only our page gets the fancy assets
        if ($hook !== 'toplevel_page_switchboard') {
            return;
        }

        // Load the styles that make everything look good
        wp_enqueue_style(
            'switchboard-admin',
            SWITCHBOARD_URL . 'switchboard-admin.css',
            [],
            SWITCHBOARD_VERSION
        );

        // Load the JS with jQuery and sortable for drag-drop magic
        wp_enqueue_script(
            'switchboard-admin',
            SWITCHBOARD_URL . 'switchboard-admin.js',
            ['jquery', 'jquery-ui-sortable'],
            SWITCHBOARD_VERSION,
            true
        );

        // Pass security nonces to JS - keeps the bad guys out
        // Check license status to determine if we should show the welcome tour
        $license_status = $this->check_license_status();
        $is_licensed = $license_status['is_valid'] && $license_status['is_active'];

        wp_localize_script('switchboard-admin', 'switchboardSettings', [
            'nonce' => wp_create_nonce('switchboard_nonce'),
            'settings_nonce' => wp_create_nonce('switchboard_settings'),
            'ajaxurl' => admin_url('admin-ajax.php'),
            'is_pro' => SWITCHBOARD_IS_PRO,
            'upsell_url' => $this->get_upsell_url(),
            'opt_out_telemetry' => SWITCHBOARD_IS_PRO ? filter_var(get_option('switchboard_opt_out_telemetry', false), FILTER_VALIDATE_BOOLEAN) : false,
            'collect_feedback' => SWITCHBOARD_IS_PRO ? !filter_var(get_option('switchboard_disable_feedback', true), FILTER_VALIDATE_BOOLEAN) : false,
            'show_first_time_popup' => SWITCHBOARD_IS_PRO && $is_licensed && !get_option('switchboard_first_time_popup_dismissed', false)
        ]);

        // Bring in WP media uploader for image selection
        wp_enqueue_media();

        // CodeMirror for syntax-highlighted code editing (free with WP)
        wp_enqueue_code_editor(['type' => 'text/css']);
        wp_enqueue_script('wp-theme-plugin-editor');
        wp_enqueue_style('wp-codemirror');
    }

    public function enqueue_global_badge_styles()
    {
        // Inject badge styles globally so modules can flex our branding anywhere
        // Using hardcoded color values as fallback since CSS variables aren't defined on all admin pages
        wp_add_inline_style('wp-admin', '
            /* Switchboard Global Branding Badge */
            .switchboard-badge {
                display: inline-block;
                background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
                color: white !important;
                font-size: 9px;
                font-weight: 700;
                letter-spacing: 0.5px;
                padding: 2px 6px;
                border-radius: 3px;
                text-transform: uppercase;
                box-shadow: 0 1px 2px rgba(37, 99, 235, 0.2);
                transition: all 0.2s ease;
                position: relative;
                top: 0;
                margin-left: 6px;
                vertical-align: baseline;
                line-height: 1;
            }
            .switchboard-badge:hover {
                transform: translateY(-1px);
                box-shadow: 0 2px 4px rgba(37, 99, 235, 0.3);
            }
            /* Ensure badge displays inline in menu */
            #adminmenu .wp-menu-name .switchboard-badge {
                display: inline-block;
                margin-left: 8px;
            }
        ');
    }

    public function init()
    {
        $this->load_active_modules();
        // License handled via modal now - cleaner UX
        if (is_admin()) {
            $this->init_licensing();
        }
    }

    public function load_active_modules()
    {
        // Initialize Report Registry before modules load so they can register report sections
        if (class_exists('Switchboard_Report_Registry')) {
            Switchboard_Report_Registry::get_instance();
        }

        $active_modules_raw = get_option('switchboard_active_modules', []);

        // Remove duplicates from active modules array (if any exist)
        $this->active_modules = array_unique($active_modules_raw);
        $this->active_modules = array_values($this->active_modules); // Re-index array

        // If duplicates were found, update the database to clean it up
        if (count($this->active_modules) !== count($active_modules_raw)) {
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                error_log('Switchboard: Found ' . count($active_modules_raw) . ' items with ' . (count($active_modules_raw) - count($this->active_modules)) . ' duplicates. Cleaning up database...'); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
            }
            update_option('switchboard_active_modules', $this->active_modules);
        }

        // Emergency SSL kill switch - for when SSL locks you out
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Emergency recovery endpoint, nonce unavailable when locked out
        if (isset($_GET['switchboard_disable_ssl']) && $_GET['switchboard_disable_ssl'] === 'true') {
            // Yank SSL from the active list
            $key = array_search('force-ssl-admin', $this->active_modules);
            if ($key !== false) {
                unset($this->active_modules[$key]);
                update_option('switchboard_active_modules', array_values($this->active_modules));

                // Redirect with a victory lap message
                if (!headers_sent()) {
                    wp_safe_redirect(admin_url('admin.php?page=switchboard&ssl_disabled=1'));
                    exit;
                }
            }
        }

        // Pro version requires a valid license to load modules
        if (SWITCHBOARD_IS_PRO) {
            $license_status = $this->check_license_status();
            if (!$license_status['is_valid'] || !$license_status['is_active']) {
                $this->active_modules = [];
                return;
            }
        }

        foreach ($this->active_modules as $module) {
            // Hunt through category folders for module files
            $modules_dir = SWITCHBOARD_PATH . 'modules/';
            $categories = ['admin', 'content', 'media', 'security', 'optimization', 'user-management', 'utilities'];

            foreach ($categories as $category) {
                $file = $modules_dir . $category . '/' . $module . '.php';
                if (file_exists($file)) {
                    require_once $file;

                    // Let modules announce themselves after loading
                    do_action('switchboard_module_loaded', $module);

                    break;
                }
            }
        }

        // All aboard - modules are ready to rock
        do_action('switchboard_modules_loaded');
    }

    public function init_licensing()
    {
        if (!SWITCHBOARD_IS_PRO) {
            return;
        }

        // Grab SureCart setup from config
        $config = switchboard_get_surecart_config();

        // Fire up the licensing client with our credentials
        $client = new \WP_Switchboard\Licensing\Client(
            $config['plugin_name'],
            $config['public_token'],
            __FILE__
        );

        // Enable translations for license messages
        $client->set_textdomain($config['textdomain']);

        // Store license data in a consistent WP option
        $client->settings()->set_option_key('wpswitchboard_license_options');

        // Modal handles everything now - no clunky admin page
        // $license_config = $config['license_page'];
        // $license_config['menu_slug'] = $client->slug . '-manage-license';
        // $client->settings()->add_page( $license_config );
    }

    // ============================================================================
    // 2. MODULE MANAGEMENT
    // ============================================================================

    public function get_available_modules()
    {
        // Cache first - scanning 100+ files gets old fast
        $cache_key = 'switchboard_modules_cache_' . self::CACHE_VERSION;
        $cached_modules = get_transient($cache_key);

        if ($cached_modules !== false) {
            return $this->filter_beta_modules($cached_modules);
        }

        $modules_dir = SWITCHBOARD_PATH . 'modules/';
        $modules = [];

        if (is_dir($modules_dir)) {
            // Walk through each category folder
            $categories = scandir($modules_dir);
            foreach ($categories as $category) {
                if ($category === '.' || $category === '..') {
                    continue;
                }

                $category_path = $modules_dir . $category;
                if (is_dir($category_path)) {
                    $files = scandir($category_path);
                    foreach ($files as $file) {
                        if (pathinfo($file, PATHINFO_EXTENSION) === 'php') {
                            $module_name = pathinfo($file, PATHINFO_FILENAME);
                            $modules[$module_name] = $this->get_module_info($category_path . '/' . $file);
                        }
                    }
                }
            }
        }

        // In free version, merge pro module placeholders for locked display
        if (!SWITCHBOARD_IS_PRO) {
            $modules = $this->merge_pro_module_placeholders($modules);
        }

        // Cache for an hour to keep things snappy
        set_transient($cache_key, $modules, 3600);

        return $this->filter_beta_modules($modules);
    }

    private function filter_beta_modules($modules)
    {
        $beta_mode = filter_var(get_option('switchboard_beta_mode', false), FILTER_VALIDATE_BOOLEAN);

        // Never show disabled modules - they're sleeping
        $modules = array_filter($modules, function ($module) {
            return empty($module['disabled']);
        });

        // Beta mode on? Show everything
        if ($beta_mode) {
            return $modules;
        }

        // Hide beta modules from regular users
        return array_filter($modules, function ($module) {
            return empty($module['beta']);
        });
    }

    private function get_module_info($file)
    {
        $content = file_get_contents($file);
        preg_match('/\/\*\*(.*?)\*\//s', $content, $matches);

        $info = [
            'name' => 'Unknown Module',
            'description' => 'No description available',
            'category' => 'General',
            'tags' => '',
            'added' => '',
            'beta' => false,
            'disabled' => false,
            'usecases' => [],
            'wheretofind' => null,
            'has_settings' => false
        ];

        if (isset($matches[1])) {
            $header = $matches[1];
            if (preg_match('/Name:\s*(.+)/i', $header, $name_match)) {
                $info['name'] = trim($name_match[1]);
            }
            if (preg_match('/Description:\s*(.+)/i', $header, $desc_match)) {
                $info['description'] = trim($desc_match[1]);
            }
            if (preg_match('/Category:\s*(.+)/i', $header, $cat_match)) {
                $info['category'] = trim($cat_match[1]);
            }
            if (preg_match('/Tags:\s*(.+)/i', $header, $tags_match)) {
                $info['tags'] = trim($tags_match[1]);
            }
            if (preg_match('/Added:\s*(.+)/i', $header, $added_match)) {
                $info['added'] = trim($added_match[1]);
            }
            if (preg_match('/Icon:\s*(.+)/i', $header, $icon_match)) {
                $info['icon'] = trim($icon_match[1]);
            }
            if (preg_match('/Beta:\s*(true|yes|1)/i', $header, $beta_match)) {
                $info['beta'] = true;
            }
            if (preg_match('/Disabled:\s*(true|yes|1)/i', $header, $disabled_match)) {
                $info['disabled'] = true;
            }
            if (preg_match('/ComingSoon:\s*(true|yes|1)/i', $header, $coming_soon_match)) {
                $info['coming_soon'] = true;
            }

            // Check for UseCases in header (JSON array)
            if (preg_match('/UseCases:\s*(\[[\s\S]*?\n\s*\*\s*\])/s', $header, $usecases_match)) {
                $usecases_json = trim($usecases_match[1]);
                // Remove comment asterisks from the JSON
                $usecases_json = preg_replace('/^\s*\*\s*/m', '', $usecases_json);
                $usecases = json_decode($usecases_json, true);
                if (is_array($usecases)) {
                    $info['usecases'] = $usecases;
                }
            }

            // Check for WhereToFind in header (JSON object)
            if (preg_match('/WhereToFind:\s*(\{[\s\S]*?\n\s*\*\s*\})/s', $header, $wheretofind_match)) {
                $wheretofind_json = trim($wheretofind_match[1]);
                // Remove comment asterisks from the JSON
                $wheretofind_json = preg_replace('/^\s*\*\s*/m', '', $wheretofind_json);
                $wheretofind = json_decode($wheretofind_json, true);
                if (is_array($wheretofind)) {
                    $info['wheretofind'] = $wheretofind;
                }
            }
        }

        // Check if module has render_settings() method
        $module_key = pathinfo($file, PATHINFO_FILENAME);
        $info['has_settings'] = $this->module_has_settings($module_key);

        return $info;
    }

    private function module_has_settings($module_key)
    {
        // Check if module has render_settings() method by searching module file content
        // This avoids loading/executing the module file

        $categories = ['admin', 'content', 'media', 'optimization', 'security', 'user-management', 'utilities'];

        foreach ($categories as $category) {
            $module_file = SWITCHBOARD_PATH . "modules/{$category}/{$module_key}.php";

            if (file_exists($module_file)) {
                // Read file content and check for render_settings() method
                $content = file_get_contents($module_file);

                // Look for "public function render_settings" or "private function render_settings"
                if (preg_match('/\b(?:public|private|protected)\s+function\s+render_settings\s*\(/i', $content)) {
                    return true;
                }

                // Module exists but has no render_settings method
                return false;
            }
        }

        // Module file not found
        return false;
    }

    public function admin_menu()
    {
        // Load our custom SVG icon for the menu
        $icon_svg = 'data:image/svg+xml;base64,' . base64_encode(file_get_contents(SWITCHBOARD_PATH . 'images/side-icon.svg'));
        $menu_title = SWITCHBOARD_IS_PRO ? 'Switchboard Pro' : 'Switchboard';

        add_menu_page(
            $menu_title,
            $menu_title,
            'manage_options',
            'switchboard',
            [$this, 'admin_page'],
            $icon_svg,
            81 // Sitting pretty below Settings
        );
    }

    public function admin_init()
    {
        // Security: All form submissions require manage_options capability
        if (!current_user_can('manage_options')) {
            return;
        }

        // Process module save requests
        if (isset($_POST['switchboard_save'])) {
            check_admin_referer('switchboard_save');
            $active_modules = isset($_POST['active_modules']) ? array_map('sanitize_text_field', wp_unslash($_POST['active_modules'])) : [];
            update_option('switchboard_active_modules', $active_modules);
            add_action('admin_notices', function () {
                echo '<div class="notice notice-success"><p>Switchboard settings saved!</p></div>';
            });
        }

        // Handle preset save/load/delete (pro only - configurations feature)
        if (SWITCHBOARD_IS_PRO) {
            if (isset($_POST['switchboard_save_preset'])) {
                check_admin_referer('switchboard_preset_save');
                $this->save_preset();
            }

            if (isset($_POST['switchboard_load_preset'])) {
                check_admin_referer('switchboard_preset_load');
                $this->load_preset();
            }

            if (isset($_POST['switchboard_delete_preset'])) {
                check_admin_referer('switchboard_preset_delete');
                $this->delete_preset();
            }
        }
    }

    private function save_preset()
    {
        $preset_name = isset($_POST['preset_name']) ? sanitize_text_field(wp_unslash($_POST['preset_name'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified in admin_init before calling this method
        $preset_description = isset($_POST['preset_description']) ? sanitize_textarea_field(wp_unslash($_POST['preset_description'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing

        // Validate preset name is not empty
        if (empty($preset_name)) {
            add_action('admin_notices', function () {
                echo '<div class="notice notice-error"><p>Preset name is required.</p></div>';
            });
            return;
        }

        $presets = get_option('switchboard_presets', []);
        $preset_id = 'preset_' . time() . '_' . wp_rand(1000, 9999);

        $presets[$preset_id] = [
            'name' => $preset_name,
            'description' => $preset_description,
            'created' => current_time('mysql'),
            'modules' => $this->get_current_configuration()
        ];

        update_option('switchboard_presets', $presets);

        add_action('admin_notices', function () use ($preset_name) {
            echo '<div class="notice notice-success"><p>Preset "' . esc_html($preset_name) . '" saved successfully!</p></div>';
        });
    }

    private function load_preset()
    {
        $preset_id = isset($_POST['preset_id']) ? sanitize_text_field(wp_unslash($_POST['preset_id'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified in admin_init before calling this method
        $presets = get_option('switchboard_presets', []);

        if (empty($preset_id) || !isset($presets[$preset_id])) {
            add_action('admin_notices', function () {
                echo '<div class="notice notice-error"><p>Preset not found.</p></div>';
            });
            return;
        }

        $this->apply_configuration($presets[$preset_id]['modules']);

        add_action('admin_notices', function () {
            echo '<div class="notice notice-success"><p>Preset loaded and applied successfully!</p></div>';
        });
    }

    private function delete_preset()
    {
        $preset_id = isset($_POST['preset_id']) ? sanitize_text_field(wp_unslash($_POST['preset_id'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified in admin_init before calling this method
        $presets = get_option('switchboard_presets', []);

        if (empty($preset_id) || !isset($presets[$preset_id])) {
            add_action('admin_notices', function () {
                echo '<div class="notice notice-error"><p>Preset not found.</p></div>';
            });
            return;
        }

        unset($presets[$preset_id]);
        update_option('switchboard_presets', $presets);

        add_action('admin_notices', function () {
            echo '<div class="notice notice-success"><p>Preset deleted successfully!</p></div>';
        });
    }

    private function get_current_configuration()
    {
        // Get Data Registry instance for comprehensive option tracking
        $registry = Switchboard_Data_Registry::get_instance();

        // Get all available modules (not filtered by beta/disabled)
        $cache_key = 'switchboard_modules_cache_' . self::CACHE_VERSION;
        $all_modules = get_transient($cache_key);

        if ($all_modules === false) {
            // Rebuild cache if needed
            $all_modules = [];
            $modules_dir = SWITCHBOARD_PATH . 'modules/';
            if (is_dir($modules_dir)) {
                $categories = scandir($modules_dir);
                foreach ($categories as $category) {
                    if ($category === '.' || $category === '..') continue;
                    $category_path = $modules_dir . $category;
                    if (is_dir($category_path)) {
                        $files = scandir($category_path);
                        foreach ($files as $file) {
                            if (pathinfo($file, PATHINFO_EXTENSION) === 'php') {
                                $module_name = pathinfo($file, PATHINFO_FILENAME);
                                $all_modules[$module_name] = true; // Just need the slug
                            }
                        }
                    }
                }
            }
        } else {
            // Just get slugs from cached modules
            $all_modules = array_keys($all_modules);
            $all_modules = array_combine($all_modules, $all_modules);
        }

        $active_modules = $this->active_modules;
        $config = [
            'meta' => [
                'version' => '2.0',  // Config format version
                'exported_at' => current_time('mysql'),
                'beta_mode' => get_option('switchboard_beta_mode', false)
            ],
            'modules' => []
        ];

        // Loop through ALL modules to check for data
        foreach ($all_modules as $module_slug => $module_info) {
            $is_active = in_array($module_slug, $active_modules);

            // Get module's registered data locations from Data Registry
            $module_data_locations = $registry->get_module_data($module_slug);

            $module_options = [];

            if ($module_data_locations && !empty($module_data_locations['options'])) {
                // Export all registered options for this module
                foreach ($module_data_locations['options'] as $option_name) {
                    $value = get_option($option_name, null);
                    // Only include if option exists and has data
                    if ($value !== null && $value !== false && $value !== '') {
                        $module_options[$option_name] = $value;
                    }
                }
            } else {
                // Fallback: check standard settings option if not registered with Data Registry
                $standard_settings = get_option('switchboard_' . $module_slug . '_settings', null);
                if ($standard_settings !== null && $standard_settings !== false && $standard_settings !== '') {
                    $module_options['switchboard_' . $module_slug . '_settings'] = $standard_settings;
                }
            }

            // Include module if it's active OR has data
            if ($is_active || !empty($module_options)) {
                $config['modules'][$module_slug] = [
                    'enabled' => $is_active,
                    'options' => $module_options
                ];
            }
        }

        return $config;
    }

    /**
     * Call activate() method on a newly enabled module
     *
     * This allows modules to set up cron jobs and perform other activation tasks.
     * For newly enabled modules, we need to load and instantiate them first.
     *
     * @param string $module_slug The module slug to activate
     */
    private function activate_module($module_slug)
    {
        // First try to get existing instance (for modules already loaded)
        $registry = Switchboard_Data_Registry::get_instance();
        $instance = $registry->get_module_instance($module_slug);

        // If not loaded yet, load the module file and instantiate it
        if (!$instance) {
            $modules_dir = SWITCHBOARD_PATH . 'modules/';
            $categories = ['admin', 'content', 'media', 'security', 'optimization', 'user-management', 'utilities'];

            foreach ($categories as $category) {
                $file = $modules_dir . $category . '/' . $module_slug . '.php';
                if (file_exists($file)) {
                    // Load the module file
                    require_once $file;

                    // Module should have auto-registered itself via Data Registry
                    // Try to get the instance again
                    $instance = $registry->get_module_instance($module_slug);
                    break;
                }
            }
        }

        // Call activate() if the method exists
        if ($instance && method_exists($instance, 'activate')) {
            $instance->activate();
        }
    }

    /**
     * Call deactivate() method on a disabled module
     *
     * This allows modules to clean up cron jobs and perform other deactivation tasks.
     *
     * @param string $module_slug The module slug to deactivate
     */
    private function deactivate_module($module_slug)
    {
        // Get the module class instance from Data Registry
        $registry = Switchboard_Data_Registry::get_instance();
        $instance = $registry->get_module_instance($module_slug);

        // Call deactivate() if the method exists
        if ($instance && method_exists($instance, 'deactivate')) {
            $instance->deactivate();
        }
    }

    private function apply_configuration($config)
    {
        // Handle both old format (v1.0) and new format (v2.0)
        $is_new_format = isset($config['meta']) && isset($config['modules']);

        if ($is_new_format) {
            // New format v2.0
            $meta = $config['meta'];
            $modules_config = $config['modules'];

            // Apply beta mode setting if present
            if (isset($meta['beta_mode'])) {
                update_option('switchboard_beta_mode', $meta['beta_mode']);
                // Clear module cache to refresh module visibility
                delete_transient('switchboard_modules_cache_' . self::CACHE_VERSION);
            }
        } else {
            // Old format v1.0 (direct module array)
            $modules_config = $config;
        }

        // Get available modules to validate against
        $available_modules = $this->get_all_module_slugs();

        // Get Data Registry for option validation
        $registry = Switchboard_Data_Registry::get_instance();

        // Track current active modules for lifecycle hooks
        $current_active = $this->active_modules;
        $new_active_modules = [];

        foreach ($modules_config as $module => $data) {
            // Skip modules that don't exist in this installation
            if (!in_array($module, $available_modules)) {
                continue;
            }

            $is_enabled = isset($data['enabled']) ? $data['enabled'] : false;

            if ($is_enabled) {
                $new_active_modules[] = $module;
            }

            // Handle both old format (settings key) and new format (options key)
            if ($is_new_format && isset($data['options']) && !empty($data['options'])) {
                // New format: restore only validated/registered options
                $module_data = $registry->get_module_data($module);
                $registered_options = $module_data ? $module_data['options'] : [];

                foreach ($data['options'] as $option_name => $option_value) {
                    // Validate option: must be registered OR follow standard naming pattern
                    $is_registered = in_array($option_name, $registered_options);
                    $is_standard_pattern = (
                        strpos($option_name, 'switchboard_' . $module) === 0 ||
                        strpos($option_name, 'switchboard_' . str_replace('-', '_', $module)) === 0
                    );

                    if ($is_registered || $is_standard_pattern) {
                        update_option($option_name, $option_value);
                    }
                }
            } elseif (isset($data['settings']) && !empty($data['settings'])) {
                // Old format fallback: only restore standard settings option
                update_option('switchboard_' . $module . '_settings', $data['settings']);
            }
        }

        // Update active modules list
        update_option('switchboard_active_modules', $new_active_modules);

        // Call lifecycle hooks for modules that changed state
        $modules_to_activate = array_diff($new_active_modules, $current_active);
        $modules_to_deactivate = array_diff($current_active, $new_active_modules);

        foreach ($modules_to_deactivate as $module_slug) {
            $this->deactivate_module($module_slug);
        }

        foreach ($modules_to_activate as $module_slug) {
            $this->activate_module($module_slug);
        }

        // Update internal state
        $this->active_modules = $new_active_modules;
    }

    /**
     * Get all module slugs (including beta/disabled for validation)
     *
     * @return array List of all module slugs
     */
    private function get_all_module_slugs()
    {
        $modules_dir = SWITCHBOARD_PATH . 'modules/';
        $slugs = [];

        if (is_dir($modules_dir)) {
            $categories = scandir($modules_dir);
            foreach ($categories as $category) {
                if ($category === '.' || $category === '..') continue;
                $category_path = $modules_dir . $category;
                if (is_dir($category_path)) {
                    $files = scandir($category_path);
                    foreach ($files as $file) {
                        if (pathinfo($file, PATHINFO_EXTENSION) === 'php') {
                            $slugs[] = pathinfo($file, PATHINFO_FILENAME);
                        }
                    }
                }
            }
        }

        return $slugs;
    }

    // ============================================================================
    // 3. AJAX HANDLERS
    // ============================================================================

    public function ajax_get_module_settings()
    {
        check_ajax_referer('switchboard_settings', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        $module = isset($_POST['module']) ? sanitize_text_field(wp_unslash($_POST['module'])) : '';

        // Special handling for modules with custom option keys
        if ($module === 'custom-dashboard-welcome') {
            $settings = get_option('switchboard_welcome_panel_settings', []);
        } elseif ($module === 'lean-analytics') {
            $settings = get_option('switchboard_lean_analytics', []);
        } else {
            $settings = get_option('switchboard_' . $module . '_settings', []);
        }

        // Build settings HTML for this module
        $html = $this->get_module_settings_html($module, $settings);

        if ($html) {
            wp_send_json_success(['html' => $html]);
        } else {
            wp_send_json_success(['html' => '<p class="description">This module has no configurable settings. Just toggle it on or off!</p>']);
        }
    }

    public function ajax_save_module_settings()
    {
        check_ajax_referer('switchboard_settings', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        if (!isset($_POST['module'])) {
            wp_send_json_error(['message' => 'Module parameter is required']);
        }

        $module = sanitize_text_field(wp_unslash($_POST['module']));
        $settings = [];

        // Special handling for certain modules
        if ($module === 'custom-dashboard-welcome') {
            // Save all settings as JSON
            $settings = [
                'title' => isset($_POST['setting_title']) ? sanitize_text_field(wp_unslash($_POST['setting_title'])) : '',
                'content' => isset($_POST['setting_content']) ? wp_kses_post(wp_unslash($_POST['setting_content'])) : '',
                'title_alignment' => isset($_POST['setting_title_alignment']) ? sanitize_text_field(wp_unslash($_POST['setting_title_alignment'])) : '',
                'text_alignment' => isset($_POST['setting_text_alignment']) ? sanitize_text_field(wp_unslash($_POST['setting_text_alignment'])) : '',
                'title_color' => isset($_POST['setting_title_color']) ? sanitize_hex_color(wp_unslash($_POST['setting_title_color'])) : '',
                'text_color' => isset($_POST['setting_text_color']) ? sanitize_hex_color(wp_unslash($_POST['setting_text_color'])) : '',
                'title_bg_color' => isset($_POST['setting_title_bg_color']) ? sanitize_hex_color(wp_unslash($_POST['setting_title_bg_color'])) : '',
                'text_bg_color' => isset($_POST['setting_text_bg_color']) ? sanitize_hex_color(wp_unslash($_POST['setting_text_bg_color'])) : '',
                'width' => isset($_POST['setting_width']) ? intval(wp_unslash($_POST['setting_width'])) : 0
            ];
            update_option('switchboard_welcome_panel_settings', $settings);
        } elseif ($module === 'hide-admin-notices') {
            $settings = [
                'hide_all' => !empty($_POST['setting_hide_all']) ? 1 : 0,
                'hide_updates' => !empty($_POST['setting_hide_updates']) ? 1 : 0,
                'hide_plugins' => !empty($_POST['setting_hide_plugins']) ? 1 : 0,
                'hide_errors' => !empty($_POST['setting_hide_errors']) ? 1 : 0,
            ];
            update_option('switchboard_hide-admin-notices_settings', $settings);
        } elseif ($module === 'maintenance-mode-enhanced') {
            // Get allowed roles and sanitize them
            // Ensure at least one role is selected to prevent lockout
            if (isset($_POST['setting_allowed_roles']) && is_array($_POST['setting_allowed_roles'])) {
                // Sanitize and filter out empty/zero values (from unchecked checkboxes)
                $allowed_roles = array_filter(
                    array_map('sanitize_text_field', wp_unslash($_POST['setting_allowed_roles'])),
                    function($role) {
                        return !empty($role) && $role !== '0';
                    }
                );
                // If array is empty after filtering, default to administrator
                if (empty($allowed_roles)) {
                    $allowed_roles = ['administrator'];
                }
            } else {
                $allowed_roles = ['administrator'];
            }

            // Sanitize hex colors with fallbacks
            $bg_color = isset($_POST['setting_bg_color']) ? sanitize_hex_color(wp_unslash($_POST['setting_bg_color'])) : null;
            $text_color = isset($_POST['setting_text_color']) ? sanitize_hex_color(wp_unslash($_POST['setting_text_color'])) : null;
            $accent_color = isset($_POST['setting_accent_color']) ? sanitize_hex_color(wp_unslash($_POST['setting_accent_color'])) : null;

            // Validate end time - reject if timer is enabled and date is in the past
            $show_timer = (isset($_POST['setting_show_timer']) && sanitize_text_field(wp_unslash($_POST['setting_show_timer'])) === '1') ? 1 : 0;
            $end_time = isset($_POST['setting_end_time']) ? sanitize_text_field(wp_unslash($_POST['setting_end_time'])) : '';

            if ($show_timer && !empty($end_time)) {
                // Validate using WordPress site timezone
                $wp_timezone = wp_timezone();
                try {
                    $end_datetime = new DateTime($end_time, $wp_timezone);
                    $end_timestamp = $end_datetime->getTimestamp();
                    $current_timestamp = current_time('timestamp');

                    if ($end_timestamp <= $current_timestamp) {
                        wp_send_json_error([
                            'message' => 'The countdown end time cannot be in the past. Please select a future date and time.'
                        ]);
                    }
                } catch (Exception $e) {
                    // Invalid date format
                    wp_send_json_error([
                        'message' => 'Invalid date format. Please select a valid date and time.'
                    ]);
                }
            }

            $settings = [
                'enabled' => !empty($_POST['setting_enabled']) ? 1 : 0,
                'title' => isset($_POST['setting_title']) ? sanitize_text_field(wp_unslash($_POST['setting_title'])) : '',
                'message' => isset($_POST['setting_message']) ? sanitize_textarea_field(wp_unslash($_POST['setting_message'])) : '',
                'logo_id' => isset($_POST['setting_logo']) ? intval(wp_unslash($_POST['setting_logo'])) : 0,
                'bg_type' => isset($_POST['setting_bg_type']) ? sanitize_text_field(wp_unslash($_POST['setting_bg_type'])) : 'color',
                'bg_color' => $bg_color ? $bg_color : '#1e293b',
                'bg_image_id' => isset($_POST['setting_bg_image']) ? intval(wp_unslash($_POST['setting_bg_image'])) : 0,
                'text_color' => $text_color ? $text_color : '#ffffff',
                'accent_color' => $accent_color ? $accent_color : '#f59e0b',
                'allowed_roles' => $allowed_roles,
                'show_timer' => $show_timer,
                'end_time' => $end_time,
                'auto_disable' => !empty($_POST['setting_auto_disable']) ? 1 : 0,
                'badge_text' => isset($_POST['setting_badge_text']) ? sanitize_text_field(wp_unslash($_POST['setting_badge_text'])) : '',
                'custom_login_url' => isset($_POST['setting_custom_login_url']) ? sanitize_text_field(wp_unslash($_POST['setting_custom_login_url'])) : '',
                'social_facebook' => isset($_POST['setting_social_facebook']) ? esc_url_raw(wp_unslash($_POST['setting_social_facebook'])) : '',
                'social_twitter' => isset($_POST['setting_social_twitter']) ? esc_url_raw(wp_unslash($_POST['setting_social_twitter'])) : '',
                'social_instagram' => isset($_POST['setting_social_instagram']) ? esc_url_raw(wp_unslash($_POST['setting_social_instagram'])) : '',
                'social_linkedin' => isset($_POST['setting_social_linkedin']) ? esc_url_raw(wp_unslash($_POST['setting_social_linkedin'])) : '',
            ];
            update_option('switchboard_maintenance-mode-enhanced_settings', $settings);
        } elseif ($module === 'coming-soon') {
            // Get existing settings to preserve subscribers
            $existing_settings = get_option('switchboard_coming-soon_settings', []);
            $subscribers = isset($existing_settings['subscribers']) ? $existing_settings['subscribers'] : [];

            // Sanitize hex colors with fallbacks
            $bg_color = isset($_POST['setting_bg_color']) ? sanitize_hex_color(wp_unslash($_POST['setting_bg_color'])) : null;

            // Validate launch date - reject if auto-disable is enabled and date is in the past
            $auto_disable = !empty($_POST['setting_auto_disable']) ? 1 : 0;
            $launch_date = isset($_POST['setting_launch_date']) ? sanitize_text_field(wp_unslash($_POST['setting_launch_date'])) : '';

            if ($auto_disable && !empty($launch_date)) {
                // Validate using WordPress site timezone
                $wp_timezone = wp_timezone();

                try {
                    $launch_datetime = new DateTime($launch_date, $wp_timezone);
                    $launch_timestamp = $launch_datetime->getTimestamp();
                    $current_timestamp = current_time('timestamp');

                    if ($launch_timestamp <= $current_timestamp) {
                        wp_send_json_error([
                            'message' => 'The launch date cannot be in the past. Please select a future date and time.'
                        ]);
                    }
                } catch (Exception $e) {
                    // Invalid date format
                    wp_send_json_error([
                        'message' => 'Invalid date format. Please select a valid date and time.'
                    ]);
                }
            }

            // Build new settings array
            $settings = [
                'enabled' => !empty($_POST['setting_enabled']) ? 1 : 0,
                'title' => isset($_POST['setting_title']) ? sanitize_text_field(wp_unslash($_POST['setting_title'])) : '',
                'subtitle' => isset($_POST['setting_subtitle']) ? sanitize_textarea_field(wp_unslash($_POST['setting_subtitle'])) : '',
                'logo_id' => isset($_POST['setting_logo']) ? intval(wp_unslash($_POST['setting_logo'])) : 0,
                'launch_date' => $launch_date,
                'show_subscribe' => !empty($_POST['setting_show_subscribe']) ? 1 : 0,
                'auto_disable' => $auto_disable,
                'bg_type' => isset($_POST['setting_bg_type']) ? sanitize_text_field(wp_unslash($_POST['setting_bg_type'])) : 'color',
                'bg_color' => $bg_color ? $bg_color : '#2563eb',
                'bg_image_id' => isset($_POST['setting_bg_image']) ? intval(wp_unslash($_POST['setting_bg_image'])) : 0,
                'social_facebook' => isset($_POST['setting_facebook']) ? esc_url_raw(wp_unslash($_POST['setting_facebook'])) : '',
                'social_twitter' => isset($_POST['setting_twitter']) ? esc_url_raw(wp_unslash($_POST['setting_twitter'])) : '',
                'social_instagram' => isset($_POST['setting_instagram']) ? esc_url_raw(wp_unslash($_POST['setting_instagram'])) : '',
                'social_linkedin' => isset($_POST['setting_linkedin']) ? esc_url_raw(wp_unslash($_POST['setting_linkedin'])) : '',
                'subscribers' => $subscribers, // Preserve existing subscribers
            ];

            update_option('switchboard_coming-soon_settings', $settings);
        } elseif ($module === 'custom-fonts-manager') {
            $body_name = isset($_POST['setting_body_name']) ? sanitize_text_field(wp_unslash($_POST['setting_body_name'])) : '';
            $body_woff2 = isset($_POST['setting_body_woff2']) ? esc_url_raw(wp_unslash($_POST['setting_body_woff2'])) : '';
            $body_woff = isset($_POST['setting_body_woff']) ? esc_url_raw(wp_unslash($_POST['setting_body_woff'])) : '';
            $body_ttf = isset($_POST['setting_body_ttf']) ? esc_url_raw(wp_unslash($_POST['setting_body_ttf'])) : '';
            $body_has_font = !empty($body_name) && (!empty($body_woff2) || !empty($body_woff) || !empty($body_ttf));

            $heading_name = isset($_POST['setting_heading_name']) ? sanitize_text_field(wp_unslash($_POST['setting_heading_name'])) : '';
            $heading_woff2 = isset($_POST['setting_heading_woff2']) ? esc_url_raw(wp_unslash($_POST['setting_heading_woff2'])) : '';
            $heading_woff = isset($_POST['setting_heading_woff']) ? esc_url_raw(wp_unslash($_POST['setting_heading_woff'])) : '';
            $heading_ttf = isset($_POST['setting_heading_ttf']) ? esc_url_raw(wp_unslash($_POST['setting_heading_ttf'])) : '';
            $heading_has_font = !empty($heading_name) && (!empty($heading_woff2) || !empty($heading_woff) || !empty($heading_ttf));

            $settings = [
                'body' => [
                    'name' => $body_name,
                    'woff2' => $body_woff2,
                    'woff' => $body_woff,
                    'ttf' => $body_ttf,
                    'enabled' => ($body_has_font && !empty($_POST['setting_body_enabled'])) ? 1 : 0,
                ],
                'heading' => [
                    'name' => $heading_name,
                    'woff2' => $heading_woff2,
                    'woff' => $heading_woff,
                    'ttf' => $heading_ttf,
                    'enabled' => ($heading_has_font && !empty($_POST['setting_heading_enabled'])) ? 1 : 0,
                ],
            ];
            update_option('switchboard_custom-fonts-manager_settings', $settings);
        } elseif ($module === 'disable-dashicons-frontend') {
            $settings = [
                'enabled' => !empty($_POST['setting_enabled']) ? 1 : 0,
            ];
            update_option('switchboard_disable-dashicons-frontend_settings', $settings);
        } elseif ($module === 'simple-captcha') {
            // Process text questions
            $text_questions = [];
            if (!empty($_POST['text_questions']) && is_array($_POST['text_questions'])) {
                $raw_questions = wp_unslash($_POST['text_questions']); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Array elements sanitized individually below with sanitize_text_field()
                foreach ($raw_questions as $q) {
                    if (!empty($q['question']) && !empty($q['answers'])) {
                        $answers = array_map('trim', explode(',', sanitize_text_field($q['answers'])));
                        $text_questions[] = [
                            'question' => sanitize_text_field($q['question']),
                            'answers' => $answers,
                        ];
                    }
                }
            }

            $settings = [
                'captcha_type' => isset($_POST['captcha_type']) ? sanitize_text_field(wp_unslash($_POST['captcha_type'])) : 'math',
                'math_difficulty' => isset($_POST['math_difficulty']) ? sanitize_text_field(wp_unslash($_POST['math_difficulty'])) : 'easy',
                'text_questions' => $text_questions,
                'image_style' => isset($_POST['image_style']) ? sanitize_text_field(wp_unslash($_POST['image_style'])) : 'distorted',
                'image_length' => isset($_POST['image_length']) ? intval(wp_unslash($_POST['image_length'])) : 5,
                'protect_login' => !empty($_POST['protect_login']),
                'protect_registration' => !empty($_POST['protect_registration']),
                'protect_password_reset' => !empty($_POST['protect_password_reset']),
                'protect_comments' => !empty($_POST['protect_comments']),
                'protect_magic_login' => !empty($_POST['protect_magic_login']),
                'protect_otp_login' => !empty($_POST['protect_otp_login']),
                'protect_cf7' => !empty($_POST['protect_cf7']),
                'protect_fluent_forms' => !empty($_POST['protect_fluent_forms']),
                'whitelist_logged_in' => !empty($_POST['whitelist_logged_in']),
                'case_sensitive' => !empty($_POST['case_sensitive']),
                'error_message' => isset($_POST['error_message']) ? sanitize_text_field(wp_unslash($_POST['error_message'])) : 'CAPTCHA verification failed. Please try again.',
            ];

            // Validate captcha type
            if (!in_array($settings['captcha_type'], ['math', 'text', 'image'], true)) {
                $settings['captcha_type'] = 'math';
            }

            // Validate math difficulty
            if (!in_array($settings['math_difficulty'], ['easy', 'medium', 'hard'], true)) {
                $settings['math_difficulty'] = 'easy';
            }

            // Validate image style
            if (!in_array($settings['image_style'], ['distorted', 'simple'], true)) {
                $settings['image_style'] = 'distorted';
            }

            // Validate image length
            $settings['image_length'] = max(4, min(6, $settings['image_length']));

            update_option('switchboard_simple-captcha_settings', $settings);
        } elseif ($module === 'google-recaptcha') {
            $settings = [
                'site_key' => sanitize_text_field(wp_unslash($_POST['site_key'] ?? '')),
                'secret_key' => sanitize_text_field(wp_unslash($_POST['secret_key'] ?? '')),
                'version' => sanitize_text_field(wp_unslash($_POST['version'] ?? 'v2_checkbox')),
                'v3_score_threshold' => floatval(wp_unslash($_POST['v3_score_threshold'] ?? 0.5)),
                'theme' => sanitize_text_field(wp_unslash($_POST['theme'] ?? 'light')),
                'size' => sanitize_text_field(wp_unslash($_POST['size'] ?? 'normal')),
                'badge_position' => sanitize_text_field(wp_unslash($_POST['badge_position'] ?? 'bottomright')),
                'language' => sanitize_text_field(wp_unslash($_POST['language'] ?? 'auto')),
                'protect_login' => !empty($_POST['protect_login']),
                'protect_registration' => !empty($_POST['protect_registration']),
                'protect_password_reset' => !empty($_POST['protect_password_reset']),
                'protect_comments' => !empty($_POST['protect_comments']),
                'protect_magic_login' => !empty($_POST['protect_magic_login']),
                'protect_cf7' => !empty($_POST['protect_cf7']),
                'protect_fluent_forms' => !empty($_POST['protect_fluent_forms']),
                'whitelist_logged_in' => !empty($_POST['whitelist_logged_in']),
                'error_message' => sanitize_text_field(wp_unslash($_POST['error_message'] ?? 'reCAPTCHA verification failed. Please try again.')),
            ];

            // Validate version
            if (!in_array($settings['version'], ['v2_checkbox', 'v2_invisible', 'v3'])) {
                $settings['version'] = 'v2_checkbox';
            }

            // Validate score threshold
            $settings['v3_score_threshold'] = max(0, min(1, $settings['v3_score_threshold']));

            update_option('switchboard_google-recaptcha_settings', $settings);
        } elseif ($module === 'cloudflare-turnstile') {
            $settings = [
                'site_key' => sanitize_text_field(wp_unslash($_POST['site_key'] ?? '')),
                'secret_key' => sanitize_text_field(wp_unslash($_POST['secret_key'] ?? '')),
                'appearance' => sanitize_text_field(wp_unslash($_POST['appearance'] ?? 'always')),
                'protect_login' => !empty($_POST['protect_login']),
                'protect_registration' => !empty($_POST['protect_registration']),
                'protect_password_reset' => !empty($_POST['protect_password_reset']),
                'protect_comments' => !empty($_POST['protect_comments']),
                'protect_magic_login' => !empty($_POST['protect_magic_login']),
                'protect_otp_login' => !empty($_POST['protect_otp_login']),
                'protect_simple_forms' => !empty($_POST['protect_simple_forms']),
                'protect_cf7' => !empty($_POST['protect_cf7']),
                'protect_fluent_forms' => !empty($_POST['protect_fluent_forms']),
                'whitelist_logged_in' => !empty($_POST['whitelist_logged_in']),
                'whitelist_ips' => sanitize_textarea_field(wp_unslash($_POST['whitelist_ips'] ?? '')),
                'error_message' => sanitize_text_field(wp_unslash($_POST['error_message'] ?? 'Turnstile verification failed. Please try again.')),
            ];

            // Validate appearance
            if (!in_array($settings['appearance'], ['always', 'interaction-only', 'execute'])) {
                $settings['appearance'] = 'always';
            }

            update_option('switchboard_cloudflare-turnstile_settings', $settings);
        } elseif ($module === 'lean-analytics') {
            // Lean analytics uses its own option format
            $existing = get_option('switchboard_lean_analytics', []);
            $settings = [
                'track_logged_in' => !empty($_POST['setting_track_logged_in']),
                'exclude_roles' => isset($_POST['setting_exclude_roles']) ? array_map('sanitize_text_field', wp_unslash($_POST['setting_exclude_roles'])) : [],
                'respect_dnt' => !empty($_POST['setting_respect_dnt']),
                'country_detection' => !empty($_POST['setting_country_detection']),
                'bot_filter' => !empty($_POST['setting_bot_filter']),
                'retention_days' => isset($_POST['setting_retention_days']) ? intval(wp_unslash($_POST['setting_retention_days'])) : 180,
                'allow_editor_access' => !empty($_POST['setting_allow_editor_access']),
                'salt' => isset($existing['salt']) ? $existing['salt'] : wp_generate_password(32, true, true),
            ];
            update_option('switchboard_lean_analytics', $settings);
        } elseif ($module === 'simple-role-editor') {
            // Handle role editor capability updates
            if (isset($_POST['switchboard_role_editor_update']) && isset($_POST['role'])) {
                // Verify nonce
                if (!isset($_POST['switchboard_role_editor_nonce']) ||
                    !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['switchboard_role_editor_nonce'])), 'switchboard_role_editor')) {
                    wp_send_json_error(['message' => 'Security check failed']);
                }

                $role_key = isset($_POST['role']) ? sanitize_key(wp_unslash($_POST['role'])) : '';
                $capabilities = isset($_POST['capabilities']) ? array_map('sanitize_text_field', wp_unslash($_POST['capabilities'])) : [];

                $role = get_role($role_key);

                if ($role) {
                    // Get all capabilities
                    global $wp_roles;
                    if (!isset($wp_roles)) {
                        $wp_roles = new WP_Roles();
                    }

                    $all_caps = [];
                    foreach ($wp_roles->roles as $r) {
                        if (isset($r['capabilities']) && is_array($r['capabilities'])) {
                            $all_caps = array_merge($all_caps, array_keys($r['capabilities']));
                        }
                    }
                    $all_caps = array_unique($all_caps);

                    // Remove all capabilities first
                    foreach ($all_caps as $cap) {
                        $role->remove_cap($cap);
                    }

                    // Add selected capabilities (only those with value '1')
                    $added_caps = [];
                    foreach ($capabilities as $cap => $value) {
                        if ($value == '1' || $value === 1 || $value === true) {
                            $role->add_cap(sanitize_key($cap));
                            $added_caps[] = $cap;
                        }
                    }

                    // Clear WordPress role cache
                    wp_cache_delete('user_roles', 'options');

                    // Force reload wp_roles
                    $wp_roles = null;
                    wp_roles();

                    wp_send_json_success([
                        'message' => 'Role capabilities updated successfully',
                        'role' => $role_key,
                        'role_name' => get_role($role_key) ? wp_roles()->roles[$role_key]['name'] : ucfirst($role_key),
                        'added_count' => count($added_caps)
                    ]);
                    return; // Exit after sending response to prevent fallthrough
                } else {
                    wp_send_json_error(['message' => 'Role not found']);
                    return; // Exit after error
                }
            }
            // If not a role editor update, continue to regular settings save below
        } else {
            // Get existing settings to preserve checkbox states
            $existing_settings = get_option('switchboard_' . $module . '_settings', []);

            // Special handling for security-headers module - convert array to comma-separated string
            if ($module === 'security-headers' && isset($_POST['setting_headers']) && is_array($_POST['setting_headers'])) {
                $headers_array = array_map('sanitize_text_field', wp_unslash($_POST['setting_headers']));
                $settings['headers'] = implode(',', $headers_array);
            }

            // Sanitize and save settings based on module
            foreach ($_POST as $key => $value) {
                if (strpos($key, 'setting_') === 0) {
                    $setting_key = str_replace('setting_', '', $key);
                    
                    // Skip headers array for security-headers (already handled above)
                    if ($module === 'security-headers' && $setting_key === 'headers' && is_array($value)) {
                        continue;
                    }

                    // Special handling for code/CSS fields that need to preserve formatting
                    if (in_array($setting_key, ['css', 'code', 'custom_css', 'custom_code', 'header_code', 'footer_code', 'body_code', 'smtp_password'])) {
                        // Use wp_unslash for code fields to preserve scripts and HTML tags
                        $settings[$setting_key] = wp_unslash($value);
                    }
                    // Special handling for HTML fields that need safe HTML preserved (links, formatting)
                    elseif (in_array($setting_key, ['footer_left', 'footer_right'])) {
                        // Use wp_kses_post to allow safe HTML tags like <a>, <strong>, etc.
                        $settings[$setting_key] = wp_kses_post(wp_unslash($value));
                    }
                    // Special handling for textarea fields that need to preserve newlines
                    elseif (in_array($setting_key, ['blocked_usernames', 'content', 'welcome_content', 'custom_content', 'whitelist_ips', 'blacklist_ips'])) {
                        // Use sanitize_textarea_field to preserve newlines
                        $settings[$setting_key] = sanitize_textarea_field(wp_unslash($value));
                    }
                    else {
                        // Use recursive sanitization for arrays and regular text fields
                        $settings[$setting_key] = $this->sanitize_recursive($value);
                    }
                }
            }

            // Handle checkboxes explicitly (they don't send data when unchecked)
            // Check if this is a settings form submission (has module field)
            if (isset($_POST['module'])) {
                // List of known checkbox settings per module
                $checkbox_settings = [
                    'enhance-list-tables' => ['show_thumbnail', 'show_id', 'show_excerpt', 'show_word_count', 'show_modified', 'show_author_email', 'show_template'],
                    'compact-tables' => ['compact_date'],
                    'reading-time' => ['auto_display'],
                    'block-video-uploads' => ['block_admins'],
                    'email-smtp' => ['smtp_auth'],
                    'auto-alt-text' => ['ai_enabled', 'auto_generate'],
                    'clean-admin-bar' => ['wp_logo', 'site_name', 'customize', 'updates', 'comments', 'new_content', 'howdy', 'help_tab', 'screen_options'],
                    'webp-conversion' => ['convert_jpeg', 'convert_png'],
                    'cloudflare-turnstile' => ['protect_login', 'protect_registration', 'protect_password_reset', 'protect_comments', 'protect_magic_login', 'protect_otp_login', 'protect_simple_forms', 'protect_cf7', 'protect_fluent_forms', 'whitelist_logged_in'],
                    'google-recaptcha' => ['protect_login', 'protect_registration', 'protect_password_reset', 'protect_comments', 'protect_magic_login', 'protect_simple_forms', 'protect_cf7', 'protect_fluent_forms', 'whitelist_logged_in'],
                    'login-page-customizer' => ['hide_back_link', 'hide_lost_password'],
                ];

                if (isset($checkbox_settings[$module])) {
                    foreach ($checkbox_settings[$module] as $checkbox_key) {
                        // If checkbox is not in POST, it's unchecked, so set to '0'
                        if (!isset($_POST['setting_' . $checkbox_key])) {
                            $settings[$checkbox_key] = '0';
                        }
                    }
                }
            }

            // Apply filters before saving (e.g., for password encryption)
            $settings = apply_filters('switchboard_before_save_settings', $settings, $module);

            update_option('switchboard_' . $module . '_settings', $settings);
        }

        wp_send_json_success(['message' => 'Settings saved']);
    }

    public function ajax_save_modules()
    {
        // Security check - keep the hackers at bay
        check_ajax_referer('switchboard_settings', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        // No freeloaders - license required
        $license_status = $this->check_license_status();
        if (!$license_status['is_valid'] || !$license_status['is_active']) {
            wp_send_json_error(['message' => 'Valid license required to save modules']);
        }

        $new_active_modules = isset($_POST['active_modules']) ? array_map('sanitize_text_field', wp_unslash($_POST['active_modules'])) : [];
        $old_active_modules = get_option('switchboard_active_modules', []);

        // Remove any duplicates (defensive programming - JS should prevent this now)
        $new_active_modules = array_unique($new_active_modules);
        $new_active_modules = array_values($new_active_modules); // Re-index

        // In free version, strip any pro-locked module slugs that shouldn't be activatable
        if (!SWITCHBOARD_IS_PRO) {
            $pro_json_path = SWITCHBOARD_PATH . 'pro-modules.json';
            if (file_exists($pro_json_path)) {
                $pro_config = json_decode(file_get_contents($pro_json_path), true);
                $pro_slugs = array_keys($pro_config['pro_modules'] ?? []);
                if (!empty($pro_slugs)) {
                    $new_active_modules = array_values(array_diff($new_active_modules, $pro_slugs));
                }
            }
        }

        // Detect which modules were enabled/disabled for telemetry
        $newly_enabled = array_diff($new_active_modules, $old_active_modules);
        $newly_disabled = array_diff($old_active_modules, $new_active_modules);

        // Auto-disable maintenance mode when module is toggled off
        if (in_array('maintenance-mode-enhanced', $newly_disabled)) {
            $maintenance_settings = get_option('switchboard_maintenance-mode-enhanced_settings', []);
            if (isset($maintenance_settings['enabled']) && $maintenance_settings['enabled']) {
                $maintenance_settings['enabled'] = 0;
                update_option('switchboard_maintenance-mode-enhanced_settings', $maintenance_settings);
            }
        }

        // Auto-disable coming soon mode when module is toggled off
        if (in_array('coming-soon', $newly_disabled)) {
            $coming_soon_settings = get_option('switchboard_coming-soon_settings', []);
            if (isset($coming_soon_settings['enabled']) && $coming_soon_settings['enabled']) {
                $coming_soon_settings['enabled'] = 0;
                update_option('switchboard_coming-soon_settings', $coming_soon_settings);
            }
        }

        // Deactivate disabled modules (cleanup cron jobs, etc.)
        foreach ($newly_disabled as $module_slug) {
            $this->deactivate_module($module_slug);
        }

        // Save the new module list
        update_option('switchboard_active_modules', $new_active_modules);

        // Activate newly enabled modules (setup cron jobs, etc.)
        foreach ($newly_enabled as $module_slug) {
            $this->activate_module($module_slug);
            do_action('switchboard_module_enabled', $module_slug);
        }

        foreach ($newly_disabled as $module_slug) {
            // Check if a reason was provided (from feedback popover)
            $reason = isset($_POST['disable_reason'])
                ? sanitize_text_field(wp_unslash($_POST['disable_reason']))
                : null;

            // Fallback to per-module reason format if no global reason
            if (!$reason && isset($_POST['disable_reason_' . sanitize_key($module_slug)])) {
                $reason = sanitize_text_field(wp_unslash($_POST['disable_reason_' . sanitize_key($module_slug)]));
            }

            // Check if reason details were provided (manual input for bug/other/missing-feature)
            $reason_details = isset($_POST['disable_reason_details'])
                ? sanitize_textarea_field(wp_unslash($_POST['disable_reason_details']))
                : null;

            // Log only when reason is provided for debugging
            if ($reason && defined('WP_DEBUG') && WP_DEBUG) {
                error_log('WPSB: Module disabled with reason - ' . $module_slug . ': ' . $reason . ($reason_details ? ' (details: ' . substr($reason_details, 0, 50) . '...)' : '')); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
            }

            do_action('switchboard_module_disabled', $module_slug, $reason, $reason_details);
        }

        wp_send_json_success([
            'message' => 'Modules saved',
            'count' => count($new_active_modules)
        ]);
    }

    public function ajax_send_bug_report()
    {
        // Security check
        check_ajax_referer('switchboard_settings', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        $module_name = isset($_POST['module_name']) ? sanitize_text_field(wp_unslash($_POST['module_name'])) : '';
        $description = isset($_POST['description']) ? sanitize_textarea_field(wp_unslash($_POST['description'])) : '';

        if (empty($module_name) || empty($description)) {
            wp_send_json_error(['message' => 'Module name and description are required']);
        }

        // Get site and user info
        $current_user = wp_get_current_user();
        $site_url = get_site_url();
        $admin_email = get_option('admin_email');

        // Prepare email
        $to = 'support@wpswitchboard.com';
        $subject = 'Bug Report: ' . $module_name . ' - ' . $site_url;

        $message = "Bug Report for WP Switchboard\n\n";
        $message .= "Module: " . $module_name . "\n";
        $message .= "Site URL: " . $site_url . "\n";
        $message .= "User: " . $current_user->display_name . " (" . $current_user->user_email . ")\n";
        $message .= "Admin Email: " . $admin_email . "\n\n";
        $message .= "Description:\n" . $description . "\n\n";
        $message .= "---\n";
        $message .= "WordPress Version: " . get_bloginfo('version') . "\n";
        $message .= "PHP Version: " . phpversion() . "\n";
        $message .= "Plugin Version: " . (defined('SWITCHBOARD_VERSION') ? SWITCHBOARD_VERSION : 'Unknown') . "\n";

        $headers = [
            'From: ' . $current_user->display_name . ' <' . $current_user->user_email . '>',
            'Reply-To: ' . $current_user->user_email
        ];

        // Send email
        $sent = wp_mail($to, $subject, $message, $headers);

        if ($sent) {
            wp_send_json_success(['message' => 'Bug report sent successfully']);
        } else {
            wp_send_json_error(['message' => 'Failed to send bug report. Please try again or contact support directly.']);
        }
    }

    public function ajax_save_snippets()
    {
        check_ajax_referer('switchboard_settings', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        // Additional security: Only allow users with unfiltered_html capability to save code snippets
        // This prevents lower-level admins from injecting arbitrary code
        if (!current_user_can('unfiltered_html')) {
            wp_send_json_error([
                'message' => 'Code snippets require super admin privileges for security reasons. Please contact your site administrator.'
            ]);
        }

        $snippets_json = isset($_POST['snippets']) ? wp_unslash($_POST['snippets']) : '[]'; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON data decoded and sanitized below
        $snippets = json_decode($snippets_json, true);

        if (!is_array($snippets)) {
            wp_send_json_error(['message' => 'Invalid data']);
        }

        // Sanitize each snippet
        $sanitized = [];
        foreach ($snippets as $snippet) {
            // Validate snippet type and location to prevent injection
            $allowed_types = ['css', 'js', 'php', 'html'];
            $allowed_locations = ['frontend-head', 'frontend-footer', 'admin-head', 'admin-footer'];

            $type = sanitize_text_field($snippet['type']);
            $location = sanitize_text_field($snippet['location']);

            if (!in_array($type, $allowed_types) || !in_array($location, $allowed_locations)) {
                continue; // Skip invalid snippets
            }

            $sanitized[] = [
                'name' => sanitize_text_field($snippet['name']),
                'type' => $type,
                'location' => $location,
                'code' => $snippet['code'], // Don't sanitize code, but validate structure above
                'enabled' => sanitize_text_field($snippet['enabled'])
            ];
        }

        update_option('switchboard_code_snippets', $sanitized);

        wp_send_json_success(['message' => 'Snippets saved']);
    }

    /**
     * Get admin page URL for modules that have separate admin pages
     * 
     * @param string $module_key Module slug
     * @return string|null Admin page URL or null if module doesn't have a separate page
     */
    private function get_module_admin_page_url($module_key)
    {
        // Mapping of module keys to their admin page slugs or direct URLs
        $module_admin_pages = [
            'track-404s' => 'track-404s',
            'conditional-redirects' => 'conditional-redirects',
            'cpt-builder' => 'cpt-builder',
            'custom-taxonomy-builder' => 'custom-taxonomy-builder',
            'admin-columns-manager' => 'admin-columns-manager',
            'options-page-builder' => 'switchboard-options-page-builder',
            'simple-forms' => 'switchboard-forms',
            'link-shortener' => 'switchboard-short-links',
            'redirects-manager' => 'switchboard-redirects',
            'lean-analytics' => 'lean-analytics',
            'code-snippets-manager' => 'switchboard-code-snippets-manager',
            'database-cleaner' => 'switchboard-database-cleaner',
            'media-categories' => 'upload.php', // Direct URL to media library
            'media-rename' => 'switchboard-media-rename',
            'activity-log' => 'switchboard-activity-log',
            'simple-role-editor' => 'switchboard-role-editor',
        ];

        if (isset($module_admin_pages[$module_key])) {
            $page = $module_admin_pages[$module_key];
            // If it's a direct file (like upload.php), return it directly
            if (strpos($page, '.php') !== false) {
                return admin_url($page);
            }
            // Otherwise, treat it as an admin page slug
            return admin_url('admin.php?page=' . $page);
        }

        return null;
    }

    public function ajax_search_modules()
    {
        // Note: Not adding nonce verification here since this is a read-only search operation
        // used by Quick Launcher. Adding nonce would break the Quick Launcher functionality.
        // The capability check provides sufficient security for this read-only endpoint.
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        $query = isset($_POST['query']) ? sanitize_text_field(wp_unslash($_POST['query'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Read-only search for Quick Launcher, capability check provides security
        $all_modules = $this->get_available_modules();
        $active_modules = get_option('switchboard_active_modules', []);

        $results = [];

        foreach ($all_modules as $module_key => $module) {
            // Search in name, description, and tags
            $tags = is_array($module['tags']) ? implode(' ', $module['tags']) : $module['tags'];
            $searchable = strtolower($module['name'] . ' ' . $module['description'] . ' ' . $tags);

            // Normalize search: replace hyphens with spaces for better matching
            $searchable_normalized = str_replace('-', ' ', $searchable);
            $query_normalized = str_replace('-', ' ', strtolower($query));

            // Check if all words in the query exist in searchable text
            $match = true;
            $relevance_score = 0;

            if (!empty($query)) {
                $query_words = array_filter(explode(' ', $query_normalized));
                foreach ($query_words as $word) {
                    if (strpos($searchable_normalized, $word) === false) {
                        $match = false;
                        break;
                    }
                }

                // Calculate relevance score for matched results
                if ($match) {
                    $name_normalized = str_replace('-', ' ', strtolower($module['name']));
                    $desc_normalized = str_replace('-', ' ', strtolower($module['description']));
                    $tags_normalized = str_replace('-', ' ', strtolower($tags));

                    // Exact match in name (100 points)
                    if ($name_normalized === $query_normalized) {
                        $relevance_score += 100;
                    }
                    // Name starts with query (90 points)
                    elseif (strpos($name_normalized, $query_normalized) === 0) {
                        $relevance_score += 90;
                    }
                    // Exact phrase in name (80 points)
                    elseif (strpos($name_normalized, $query_normalized) !== false) {
                        $relevance_score += 80;
                    }

                    // All words in name (70 points)
                    $all_words_in_name = true;
                    foreach ($query_words as $word) {
                        if (strpos($name_normalized, $word) === false) {
                            $all_words_in_name = false;
                            break;
                        }
                    }
                    if ($all_words_in_name && count($query_words) > 1) {
                        $relevance_score += 70;
                    }

                    // Exact phrase in description (60 points)
                    if (strpos($desc_normalized, $query_normalized) !== false) {
                        $relevance_score += 60;
                    }

                    // Exact phrase in tags (50 points)
                    if (strpos($tags_normalized, $query_normalized) !== false) {
                        $relevance_score += 50;
                    }

                    // All words in description (30 points)
                    $all_words_in_desc = true;
                    foreach ($query_words as $word) {
                        if (strpos($desc_normalized, $word) === false) {
                            $all_words_in_desc = false;
                            break;
                        }
                    }
                    if ($all_words_in_desc && count($query_words) > 1) {
                        $relevance_score += 30;
                    }

                    // Word count bonus (5 points per matching word)
                    foreach ($query_words as $word) {
                        if (strpos($searchable_normalized, $word) !== false) {
                            $relevance_score += 5;
                        }
                    }
                }
            }

            if ($match) {
                $admin_page_url = $this->get_module_admin_page_url($module_key);
                $results[] = [
                    'key' => $module_key,
                    'name' => $module['name'],
                    'description' => $module['description'],
                    'category' => $module['category'],
                    'is_active' => in_array($module_key, $active_modules),
                    'has_settings' => $this->module_has_settings($module_key),
                    'has_admin_page' => !empty($admin_page_url),
                    'admin_page_url' => $admin_page_url,
                    'relevance_score' => $relevance_score
                ];
            }
        }

        // Sort by relevance score (highest first)
        usort($results, function($a, $b) {
            return $b['relevance_score'] - $a['relevance_score'];
        });

        // Limit to 10 results
        $results = array_slice($results, 0, 10);

        wp_send_json_success($results);
    }

    public function ajax_toggle_beta_mode()
    {
        check_ajax_referer('switchboard_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        $enabled = isset($_POST['enabled']) ? filter_var(wp_unslash($_POST['enabled']), FILTER_VALIDATE_BOOLEAN) : false;
        update_option('switchboard_beta_mode', $enabled);

        // Clear module cache to refresh the module list
        delete_transient('switchboard_modules_cache_' . self::CACHE_VERSION);

        wp_send_json_success([
            'message' => 'Beta mode ' . ($enabled ? 'enabled' : 'disabled'),
            'beta_mode' => $enabled
        ]);
    }

    public function ajax_toggle_telemetry()
    {
        if (!SWITCHBOARD_IS_PRO) {
            wp_send_json_error(['message' => 'Pro version required']);
        }

        check_ajax_referer('switchboard_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        $enabled = isset($_POST['enabled']) && sanitize_text_field(wp_unslash($_POST['enabled'])) === 'yes';
        // Store as opt-out (inverse of enabled)
        update_option('switchboard_opt_out_telemetry', !$enabled);

        wp_send_json_success([
            'message' => 'Telemetry ' . ($enabled ? 'enabled' : 'disabled'),
            'telemetry_enabled' => $enabled
        ]);
    }

    public function ajax_toggle_feedback()
    {
        check_ajax_referer('switchboard_nonce', 'nonce');

        if (!SWITCHBOARD_IS_PRO) {
            wp_send_json_error(['message' => 'Pro version required']);
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        $enabled = isset($_POST['enabled']) && sanitize_text_field(wp_unslash($_POST['enabled'])) === 'yes';
        // Store as disabled (inverse of enabled)
        update_option('switchboard_disable_feedback', !$enabled);

        wp_send_json_success([
            'message' => 'Feedback ' . ($enabled ? 'enabled' : 'disabled'),
            'feedback_enabled' => $enabled
        ]);
    }

    public function ajax_toggle_favorite()
    {
        check_ajax_referer('switchboard_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        $module_key = isset($_POST['module']) ? sanitize_text_field(wp_unslash($_POST['module'])) : '';

        if (empty($module_key)) {
            wp_send_json_error(['message' => 'Module key required']);
        }

        $user_id = get_current_user_id();
        $favorites = get_option('switchboard_favorites_' . $user_id, []);

        // Toggle favorite
        $is_favorited = in_array($module_key, $favorites);
        if ($is_favorited) {
            // Remove from favorites
            $favorites = array_diff($favorites, [$module_key]);
        } else {
            // Add to favorites
            $favorites[] = $module_key;
        }

        // Save favorites
        update_option('switchboard_favorites_' . $user_id, array_values($favorites));

        wp_send_json_success([
            'favorited' => !$is_favorited,
            'count' => count($favorites)
        ]);
    }

    public function ajax_dismiss_first_time_popup()
    {
        check_ajax_referer('switchboard_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        update_option('switchboard_first_time_popup_dismissed', true);

        wp_send_json_success(['message' => 'First-time popup dismissed']);
    }

    public function ajax_save_settings()
    {
        check_ajax_referer('switchboard_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        // Save beta mode
        $beta_enabled = isset($_POST['beta_enabled']) ? filter_var(wp_unslash($_POST['beta_enabled']), FILTER_VALIDATE_BOOLEAN) : false;
        update_option('switchboard_beta_mode', $beta_enabled);

        // Save opt-out telemetry
        $opt_out_telemetry = isset($_POST['opt_out_telemetry']) ? filter_var(wp_unslash($_POST['opt_out_telemetry']), FILTER_VALIDATE_BOOLEAN) : false;
        update_option('switchboard_opt_out_telemetry', $opt_out_telemetry);

        // Save disable feedback toggle
        $disable_feedback = isset($_POST['disable_feedback']) ? filter_var(wp_unslash($_POST['disable_feedback']), FILTER_VALIDATE_BOOLEAN) : false;
        update_option('switchboard_disable_feedback', $disable_feedback);

        // Clear module cache to refresh the module list
        delete_transient('switchboard_modules_cache_' . self::CACHE_VERSION);

        wp_send_json_success([
            'message' => 'Settings saved successfully'
        ]);
    }

    public function ajax_clear_cache()
    {
        check_ajax_referer('switchboard_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        // Nuke the module cache - fresh start
        delete_transient('switchboard_modules_cache_' . self::CACHE_VERSION);

        wp_send_json_success([
            'message' => 'Module cache cleared successfully'
        ]);
    }

    public function ajax_clear_all_data()
    {
        check_ajax_referer('switchboard_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        try {
            // Use the new data management system
            $scanner = new Switchboard_Data_Scanner();
            $data_map = $scanner->scan_all_data('all');

            $cleaner = new Switchboard_Data_Cleaner();
            // FULL mode: DROP tables completely (nuclear option)
            $result = $cleaner->clear_all_data($data_map, 'full');

            // Build success message
            $message = sprintf(
                'Successfully deleted: %d options, %d tables (dropped), %d post meta entries, %d user meta entries',
                $result['options_deleted'],
                $result['tables_dropped'],
                $result['post_meta_deleted'],
                $result['user_meta_deleted']
            );

            if (!empty($result['errors'])) {
                $message .= ' (with some errors)';
            }

            wp_send_json_success([
                'message' => $message,
                'result' => $result,
                'summary' => $data_map['summary']
            ]);

        } catch (Exception $e) {
            wp_send_json_error([
                'message' => 'Failed to clear data: ' . $e->getMessage()
            ]);
        }
    }

    public function ajax_clear_inactive_data()
    {
        check_ajax_referer('switchboard_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        try {
            // Scan ONLY inactive modules
            $scanner = new Switchboard_Data_Scanner();
            $data_map = $scanner->scan_all_data('inactive');

            $cleaner = new Switchboard_Data_Cleaner();
            // DATA_ONLY mode: TRUNCATE tables (keep schema for easy reactivation)
            $result = $cleaner->clear_all_data($data_map, 'data_only');

            // Build success message
            $table_action = $result['tables_truncated'] > 0
                ? sprintf('%d tables (emptied, schema preserved)', $result['tables_truncated'])
                : '0 tables';

            $message = sprintf(
                'Successfully cleaned inactive modules: %d options, %s, %d post meta entries, %d user meta entries. Active modules are safe!',
                $result['options_deleted'],
                $table_action,
                $result['post_meta_deleted'],
                $result['user_meta_deleted']
            );

            if (!empty($result['errors'])) {
                $message .= ' (with some errors)';
            }

            wp_send_json_success([
                'message' => $message,
                'result' => $result,
                'summary' => $data_map['summary']
            ]);

        } catch (Exception $e) {
            wp_send_json_error([
                'message' => 'Failed to clear inactive data: ' . $e->getMessage()
            ]);
        }
    }

    public function ajax_activate_license()
    {
        if (!SWITCHBOARD_IS_PRO) {
            wp_send_json_error(['message' => 'Pro version required']);
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        if (!isset($_POST['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_wpnonce'])), 'switchboard_license')) {
            wp_send_json_error(['message' => 'Invalid nonce']);
        }

        // Rate limiting: Prevent brute force attacks on license activation
        $user_id = get_current_user_id();
        $rate_limit_key = 'switchboard_license_attempt_' . $user_id;
        $attempt_count = get_transient($rate_limit_key);

        if ($attempt_count !== false && $attempt_count >= 5) {
            wp_send_json_error([
                'message' => 'Too many license activation attempts. Please try again in 15 minutes.'
            ]);
        }

        // Increment attempt counter
        $new_count = $attempt_count === false ? 1 : $attempt_count + 1;
        set_transient($rate_limit_key, $new_count, 15 * MINUTE_IN_SECONDS);

        $license_key = isset($_POST['license_key']) ? sanitize_text_field(wp_unslash($_POST['license_key'])) : '';

        if (empty($license_key)) {
            wp_send_json_error(['message' => 'License key is required']);
        }

        try {
            $config = switchboard_get_surecart_config();
            $client = new \WP_Switchboard\Licensing\Client(
                $config['plugin_name'],
                $config['public_token'],
                __FILE__
            );

            // Set the same consistent option key
            $client->settings()->set_option_key('wpswitchboard_license_options');
            
            // Verify we're using the correct plugin slug (safeguard against conflicts)
            if ($client->slug !== SWITCHBOARD_PLUGIN_SLUG) {
                // Clear any stale license data that might be from another product
                $client->settings()->clear_options();
                wp_send_json_error([
                    'message' => 'Plugin configuration error: Expected slug "' . SWITCHBOARD_PLUGIN_SLUG . '" but got "' . $client->slug . '". Please refresh and try again.',
                    'code' => 'slug_mismatch'
                ]);
            }

            $license = $client->license();

            // Step 1: Test license retrieval first
            $license_data = $license->retrieve($license_key);
            if (is_wp_error($license_data)) {
                wp_send_json_error([
                    'message' => 'License retrieval failed: ' . $license_data->get_error_message(),
                    'code' => $license_data->get_error_code(),
                    'step' => 'retrieve'
                ]);
            }

            // Step 2: Test license validation
            try {
                $validated_license = $license->validate($license_key, false);
                if (is_wp_error($validated_license)) {
                    wp_send_json_error([
                        'message' => 'License validation failed: ' . $validated_license->get_error_message(),
                        'code' => $validated_license->get_error_code(),
                        'step' => 'validate'
                    ]);
                }
            } catch (Exception $e) {
                wp_send_json_error([
                    'message' => 'License validation exception: ' . $e->getMessage(),
                    'step' => 'validate_exception'
                ]);
            }

            // Step 3: Try activation
            $activated = $license->activate($license_key);

            if (is_wp_error($activated)) {
                wp_send_json_error([
                    'message' => 'Activation failed: ' . $activated->get_error_message(),
                    'code' => $activated->get_error_code(),
                    'step' => 'activate'
                ]);
            }

            // Clear rate limiting on successful activation
            delete_transient($rate_limit_key);

            // Force cache update after successful activation
            $this->check_license_status(true);

            // Trigger first activation telemetry event
            do_action('switchboard_plugin_activated');

            // Trigger license validated telemetry event (for conversion tracking)
            do_action('switchboard_license_validated');

            // Check if this is the first time activating (tour not dismissed yet)
            $show_welcome_tour = !get_option('switchboard_first_time_popup_dismissed', false);

            wp_send_json_success([
                'message' => 'License activated successfully',
                'license_key' => $license_key,
                'activation_id' => $client->settings()->activation_id,
                'license_data' => $license_data,
                'show_welcome_tour' => $show_welcome_tour
            ]);
        } catch (Exception $e) {
            wp_send_json_error([
                'message' => 'Activation exception: ' . $e->getMessage(),
                'step' => 'exception'
            ]);
        }
    }

    public function ajax_deactivate_license()
    {
        if (!SWITCHBOARD_IS_PRO) {
            wp_send_json_error(['message' => 'Pro version required']);
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        if (!isset($_POST['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_wpnonce'])), 'switchboard_license')) {
            wp_send_json_error(['message' => 'Invalid nonce']);
        }

        try {
            $config = switchboard_get_surecart_config();
            $client = new \WP_Switchboard\Licensing\Client(
                $config['plugin_name'],
                $config['public_token'],
                __FILE__
            );

            // Set the same consistent option key
            $client->settings()->set_option_key('wpswitchboard_license_options');

            $license = $client->license();

            // Deactivate the license
            $deactivated = $license->deactivate();

            if (is_wp_error($deactivated)) {
                wp_send_json_error([
                    'message' => 'Deactivation failed: ' . $deactivated->get_error_message(),
                    'code' => $deactivated->get_error_code()
                ]);
            }

            // Clear cached license status after deactivation
            delete_option('wpswitchboard_license_status');

            // Force recheck to update cache with deactivated state
            // This ensures the next page load shows correct status
            $this->check_license_status(true);

            wp_send_json_success([
                'message' => 'License deactivated successfully'
            ]);
        } catch (Exception $e) {
            wp_send_json_error([
                'message' => 'Deactivation exception: ' . $e->getMessage()
            ]);
        }
    }

    public function ajax_get_license_status()
    {
        if (!SWITCHBOARD_IS_PRO) {
            wp_send_json_error(['message' => 'Pro version required']);
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        if (!isset($_POST['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_wpnonce'])), 'switchboard_license')) {
            wp_send_json_error(['message' => 'Invalid nonce']);
        }

        try {
            // Force API check when user clicks "Refresh Status"
            $license_status = $this->check_license_status(true);
            wp_send_json_success($license_status);
        } catch (Exception $e) {
            wp_send_json_error(['message' => $e->getMessage()]);
        }
    }

    public function ajax_reset_role()
    {
        // Verify nonce
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'switchboard_reset_role')) {
            wp_send_json_error(['message' => 'Security check failed']);
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        $role_key = isset($_POST['role']) ? sanitize_key(wp_unslash($_POST['role'])) : '';

        // WordPress default capabilities for each role
        $default_caps = [
            'administrator' => [
                'activate_plugins', 'create_users', 'delete_others_pages', 'delete_others_posts',
                'delete_pages', 'delete_plugins', 'delete_posts', 'delete_private_pages',
                'delete_private_posts', 'delete_published_pages', 'delete_published_posts',
                'delete_themes', 'delete_users', 'edit_dashboard', 'edit_files', 'edit_others_pages',
                'edit_others_posts', 'edit_pages', 'edit_plugins', 'edit_posts', 'edit_private_pages',
                'edit_private_posts', 'edit_published_pages', 'edit_published_posts',
                'edit_theme_options', 'edit_themes', 'edit_users', 'export', 'import',
                'install_plugins', 'install_themes', 'list_users', 'manage_categories',
                'manage_links', 'manage_options', 'moderate_comments', 'promote_users',
                'publish_pages', 'publish_posts', 'read', 'read_private_pages',
                'read_private_posts', 'remove_users', 'switch_themes', 'unfiltered_html',
                'unfiltered_upload', 'update_core', 'update_plugins', 'update_themes',
                'upload_files', 'level_10', 'level_9', 'level_8', 'level_7', 'level_6',
                'level_5', 'level_4', 'level_3', 'level_2', 'level_1', 'level_0'
            ],
            'editor' => [
                'delete_others_pages', 'delete_others_posts', 'delete_pages', 'delete_posts',
                'delete_private_pages', 'delete_private_posts', 'delete_published_pages',
                'delete_published_posts', 'edit_others_pages', 'edit_others_posts', 'edit_pages',
                'edit_posts', 'edit_private_pages', 'edit_private_posts', 'edit_published_pages',
                'edit_published_posts', 'manage_categories', 'manage_links', 'moderate_comments',
                'publish_pages', 'publish_posts', 'read', 'read_private_pages',
                'read_private_posts', 'unfiltered_html', 'upload_files', 'level_7', 'level_6',
                'level_5', 'level_4', 'level_3', 'level_2', 'level_1', 'level_0'
            ],
            'author' => [
                'delete_posts', 'delete_published_posts', 'edit_posts', 'edit_published_posts',
                'publish_posts', 'read', 'upload_files', 'level_2', 'level_1', 'level_0'
            ],
            'contributor' => [
                'delete_posts', 'edit_posts', 'read', 'level_1', 'level_0'
            ],
            'subscriber' => [
                'read', 'level_0'
            ]
        ];

        if (!isset($default_caps[$role_key])) {
            wp_send_json_error(['message' => 'Unknown role or custom role cannot be reset']);
        }

        $role = get_role($role_key);
        if (!$role) {
            wp_send_json_error(['message' => 'Role not found']);
        }

        // Get all capabilities
        global $wp_roles;
        if (!isset($wp_roles)) {
            $wp_roles = new WP_Roles();
        }

        $all_caps = [];
        foreach ($wp_roles->roles as $r) {
            if (isset($r['capabilities']) && is_array($r['capabilities'])) {
                $all_caps = array_merge($all_caps, array_keys($r['capabilities']));
            }
        }
        $all_caps = array_unique($all_caps);

        // Remove all capabilities
        foreach ($all_caps as $cap) {
            $role->remove_cap($cap);
        }

        // Add default capabilities back
        foreach ($default_caps[$role_key] as $cap) {
            $role->add_cap($cap);
        }

        // Clear cache
        wp_cache_delete('user_roles', 'options');
        $wp_roles = null;
        wp_roles();

        wp_send_json_success([
            'message' => 'Role reset to WordPress defaults successfully',
            'role' => $role_key,
            'caps_count' => count($default_caps[$role_key])
        ]);
    }

    public function ajax_search_users()
    {
        check_ajax_referer('search_users', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        $query = isset($_POST['query']) ? sanitize_text_field(wp_unslash($_POST['query'])) : '';

        $users = get_users([
            'search' => '*' . $query . '*',
            'search_columns' => ['user_login', 'user_email', 'display_name'],
            'number' => 10,
            'orderby' => 'display_name'
        ]);

        $results = [];
        foreach ($users as $user) {
            $results[] = [
                'id' => $user->ID,
                'user_login' => $user->user_login,
                'display_name' => $user->display_name,
                'email' => $user->user_email,
                'avatar' => get_avatar($user->ID, 32, '', '', ['style' => 'border-radius: 50%;'])
            ];
        }

        wp_send_json_success($results);
    }

    public function ajax_export_preset()
    {
        if (!SWITCHBOARD_IS_PRO) {
            wp_send_json_error(['message' => 'Pro version required']);
        }

        check_ajax_referer('switchboard_settings', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        if (!isset($_POST['preset_id']) || empty($_POST['preset_id'])) {
            wp_send_json_error(['message' => 'Preset ID required']);
        }

        $preset_id = sanitize_text_field(wp_unslash($_POST['preset_id']));
        $presets = get_option('switchboard_presets', []);

        if (isset($presets[$preset_id])) {
            // Package it up as base64 for easy sharing
            $json = json_encode($presets[$preset_id]);
            $encoded = base64_encode($json);

            wp_send_json_success([
                'encoded' => $encoded,
                'filename' => sanitize_file_name($presets[$preset_id]['name']) . '.switchboard'
            ]);
        } else {
            wp_send_json_error(['message' => 'Preset not found']);
        }
    }

    public function ajax_export_current()
    {
        check_ajax_referer('switchboard_settings', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        $config = $this->get_current_configuration();

        $preset = [
            'name' => 'Current Configuration',
            'description' => 'Exported on ' . current_time('mysql'),
            'created' => current_time('mysql'),
            'modules' => $config  // Contains 'meta' and 'modules' keys from v2.0 format
        ];

        // Encode preset as base64
        $json = json_encode($preset);
        $encoded = base64_encode($json);

        wp_send_json_success([
            'encoded' => $encoded,
            'filename' => 'switchboard-config-' . gmdate('Y-m-d') . '.switchboard'
        ]);
    }

    public function ajax_import_preset()
    {
        check_ajax_referer('switchboard_settings', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
        }

        if (!isset($_POST['preset_data']) || empty($_POST['preset_data'])) {
            wp_send_json_error(['message' => 'No preset data provided']);
        }

        // Decode base64 first
        $encoded_data = wp_unslash($_POST['preset_data']); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- base64 data decoded and validated below
        $decoded = base64_decode($encoded_data, true);

        if ($decoded === false) {
            wp_send_json_error(['message' => 'Invalid file format - not a valid Switchboard file']);
        }

        $preset_data = json_decode($decoded, true);

        if (!$preset_data || !isset($preset_data['modules'])) {
            wp_send_json_error(['message' => 'Invalid preset file']);
        }

        // Filter modules to only include those that exist in this installation
        $available_modules = $this->get_all_module_slugs();
        $modules_data = $preset_data['modules'];
        $skipped_modules = [];

        // Handle v2.0 format (modules contains meta and modules keys)
        if (isset($modules_data['meta']) && isset($modules_data['modules'])) {
            $filtered_modules = [];
            foreach ($modules_data['modules'] as $module_slug => $module_config) {
                if (in_array($module_slug, $available_modules)) {
                    $filtered_modules[$module_slug] = $module_config;
                } else {
                    $skipped_modules[] = $module_slug;
                }
            }
            $modules_data['modules'] = $filtered_modules;
        } else {
            // v1.0 format
            $filtered_modules = [];
            foreach ($modules_data as $module_slug => $module_config) {
                if (in_array($module_slug, $available_modules)) {
                    $filtered_modules[$module_slug] = $module_config;
                } else {
                    $skipped_modules[] = $module_slug;
                }
            }
            $modules_data = $filtered_modules;
        }

        // Save as new preset
        $presets = get_option('switchboard_presets', []);
        $preset_id = 'preset_' . time() . '_' . wp_rand(1000, 9999);

        $presets[$preset_id] = [
            'name' => isset($preset_data['name']) ? sanitize_text_field($preset_data['name']) : 'Imported Preset',
            'description' => isset($preset_data['description']) ? sanitize_textarea_field($preset_data['description']) : '',
            'created' => current_time('mysql'),
            'modules' => $modules_data
        ];

        update_option('switchboard_presets', $presets);

        // Apply immediately if requested
        $apply_immediately = isset($_POST['apply_immediately']) && sanitize_text_field(wp_unslash($_POST['apply_immediately'])) === 'true';
        if ($apply_immediately) {
            $this->apply_configuration($modules_data);
        }

        // Build response message
        $message = $apply_immediately ? 'Preset imported and applied successfully' : 'Preset imported successfully';
        if (!empty($skipped_modules)) {
            $message .= '. ' . count($skipped_modules) . ' module(s) were skipped (not installed).';
        }

        wp_send_json_success([
            'message' => $message,
            'preset_id' => $preset_id,
            'applied' => $apply_immediately,
            'skipped_modules' => $skipped_modules
        ]);
    }

    // ============================================================================
    // 4. MODULE SETTINGS UI
    // ============================================================================

    private function get_module_settings_html($module, $settings)
    {
        // Use modularized settings files organized by category
        $modules = $this->get_available_modules();
        $module_info = $modules[$module] ?? [];
        return Switchboard_Settings_Renderer::render($module, $settings, $module_info);
    }

    // ============================================================================
    // 5. ADMIN PAGE RENDERING
    // ============================================================================

    public function admin_page()
    {
        // Handle tab routing
        $current_tab = isset($_GET['tab']) ? sanitize_key($_GET['tab']) : 'modules'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Tab navigation display only
        $valid_tabs = ['modules', 'configurations', 'settings'];
        if (!in_array($current_tab, $valid_tabs)) {
            $current_tab = 'modules';
        }

        $modules = $this->get_available_modules();
        $active = $this->active_modules;
        $presets = get_option('switchboard_presets', []);

        // Scan ALL modules fresh for accurate counts on dashboard
        $modules_dir = SWITCHBOARD_PATH . 'modules/';
        $all_modules = [];

        if (is_dir($modules_dir)) {
            $categories = scandir($modules_dir);
            foreach ($categories as $category) {
                if ($category === '.' || $category === '..') continue;
                $category_path = $modules_dir . $category;
                if (is_dir($category_path)) {
                    $files = scandir($category_path);
                    foreach ($files as $file) {
                        if (pathinfo($file, PATHINFO_EXTENSION) === 'php') {
                            $module_name = pathinfo($file, PATHINFO_FILENAME);
                            $all_modules[$module_name] = $this->get_module_info($category_path . '/' . $file);
                        }
                    }
                }
            }
        }

        // Tally up modules (excluding disabled ones)
        $total_count = 0;
        $beta_count = 0;
        foreach ($all_modules as $module) {
            // Skip disabled modules in counts
            if (!empty($module['disabled'])) {
                continue;
            }
            $total_count++;
            if (!empty($module['beta'])) {
                $beta_count++;
            }
        }

        // Check if beta mode is enabled
        $beta_mode = filter_var(get_option('switchboard_beta_mode', false), FILTER_VALIDATE_BOOLEAN);

        // Check license status
        $license_status = $this->check_license_status();
        $is_licensed = $license_status['is_valid'] && $license_status['is_active'];

?>
        <div class="wrap">
            <h1 style="display: none;">Switchboard</h1>
            <?php
            // WordPress will inject admin_notices after the h1 tag above
            // This keeps notices outside our custom header
            ?>
            <div class="switchboard-admin">
                <div class="switchboard-header">
                    <div class="switchboard-header-left">
                        <div class="switchboard-header-title">
                            <img src="<?php echo esc_url(SWITCHBOARD_URL); ?>images/logo.svg" alt="Switchboard" style="height: 24px; vertical-align: middle; margin-right: 10px;">
                            <?php if (SWITCHBOARD_IS_PRO): ?><span class="pro-badge">PRO</span><?php else: ?><span class="free-badge">FREE</span><?php endif; ?>
                            <span class="version-badge">v<?php echo esc_html(SWITCHBOARD_VERSION); ?> / <?php echo esc_html($total_count); ?> Modules</span>
                        </div>
                    </div>
                    <div class="switchboard-actions">
                        <?php if ($current_tab === 'modules' && $is_licensed): ?>
                            <button type="button" class="button" id="enable-all-visible" style="display:none;">
                                Enable All
                            </button>
                            <button type="button" class="button" id="disable-all-visible" style="display:none;">
                                Disable All
                            </button>
                        <?php endif; ?>
                        <?php if (SWITCHBOARD_IS_PRO && !$is_licensed): ?>
                            <span class="license-status-badge unlicensed" onclick="openLicenseModal()">
                                License Required
                            </span>
                        <?php else: ?>
                            <?php if (SWITCHBOARD_IS_PRO): ?>
                            <span class="license-status-badge licensed" onclick="openLicenseModal()">
                                Licensed
                            </span>
                            <?php endif; ?>
                            <nav class="switchboard-nav-tabs">
                                <?php
                                $base_url = admin_url('admin.php?page=switchboard');
                                $tabs = ['modules' => 'Modules'];
                                $tabs['configurations'] = 'Configurations';
                                $tabs['settings'] = 'Settings';
                                foreach ($tabs as $tab_id => $tab_label):
                                    $tab_url = $tab_id === 'modules' ? $base_url : $base_url . '&tab=' . $tab_id;
                                    $is_active = $current_tab === $tab_id;
                                ?>
                                    <a href="<?php echo esc_url($tab_url); ?>" class="switchboard-nav-tab <?php echo esc_attr($is_active ? 'active' : ''); ?>">
                                        <?php echo esc_html($tab_label); ?>
                                    </a>
                                <?php endforeach; ?>
                            </nav>
                        <?php endif; ?>
                        <?php do_action('switchboard_header_actions'); ?>
                    </div>
                </div>

                <?php if ($current_tab === 'modules'): ?>
                <!-- Command Center Search -->
                <div class="switchboard-search-wrapper">
                    <span class="search-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="18" height="18"><circle cx="11" cy="11" r="8"></circle><path d="m21 21-4.35-4.35"></path></svg></span>
                    <input
                        type="text"
                        id="switchboard-search"
                        class="switchboard-search"
                        placeholder="Search modules by name, description, or tags..."
                        autocomplete="off" />
                    <button type="button" class="switchboard-search-clear" id="search-clear" style="display:none;">&times;</button>
                </div>

                <?php if (SWITCHBOARD_IS_PRO): ?>
                <!-- First-Time Popup - Step by Step Tour -->
                <div id="first-time-popup" class="switchboard-modal first-time-popup" style="display:none;">
                    <div class="modal-content first-time-content">
                        <div class="modal-header first-time-header">
                            <div class="step-indicator" id="step-indicator">1/3</div>
                            <button class="modal-close" id="close-first-time-popup">&times;</button>
                        </div>

                        <div class="modal-body first-time-body">
                            <!-- Step 1: Beta Modules -->
                            <div class="tour-step active" data-step="1">
                                <h2>Try Beta Modules</h2>
                                <p class="step-intro">Get early access to cutting-edge features</p>

                                <!-- Visual UI Graphic for Beta Settings -->
                                <div class="tour-visual">
                                    <div class="mock-settings-panel">
                                        <div class="mock-panel-header">
                                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                                <circle cx="12" cy="12" r="3"></circle>
                                                <path d="M12 1v6m0 6v6M5.6 5.6l4.2 4.2m4.4 4.4l4.2 4.2M1 12h6m6 0h6M5.6 18.4l4.2-4.2m4.4-4.4l4.2-4.2"></path>
                                            </svg>
                                            <span>Settings</span>
                                        </div>
                                        <div class="mock-toggle-row">
                                            <div class="mock-toggle-label">
                                                <span class="beta-badge">β</span>
                                                Beta Mode
                                            </div>
                                            <div class="mock-toggle on">
                                                <div class="mock-toggle-slider"></div>
                                            </div>
                                        </div>
                                        <div class="mock-help-text">Enable access to experimental features</div>
                                    </div>
                                </div>

                                <p class="step-action">Click the <strong>Settings</strong> button and toggle <strong>Beta Mode</strong> on.</p>
                            </div>

                            <!-- Step 2: Quick Search -->
                            <div class="tour-step" data-step="2">
                                <h2>Quick Search</h2>
                                <p class="step-intro">Find modules instantly with keyboard shortcuts</p>

                                <!-- Visual Graphic for Search -->
                                <div class="tour-visual">
                                    <div class="mock-search-bar">
                                        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                            <circle cx="11" cy="11" r="8"></circle>
                                            <path d="m21 21-4.35-4.35"></path>
                                        </svg>
                                        <span class="search-placeholder">Search modules by name, description, or tags...</span>
                                        <kbd class="keyboard-shortcut">
                                            <span class="key-symbol">Cmd</span>
                                            <span class="key-letter">K</span>
                                        </kbd>
                                    </div>
                                </div>

                                <p class="step-action">Press <kbd class="inline-kbd"><span class="key-symbol">Cmd</span> K</kbd> or <kbd class="inline-kbd">Ctrl K</kbd> to focus the search bar anytime.</p>
                            </div>

                            <!-- Step 3: Favorites -->
                            <div class="tour-step" data-step="3">
                                <h2>Save Your Favorites</h2>
                                <p class="step-intro">Quick access to your most-used modules</p>

                                <!-- Visual Graphic for Favorites -->
                                <div class="tour-visual">
                                    <div class="mock-module-card">
                                        <div class="mock-card-header">
                                            <span class="mock-module-name">Custom Dashboard Welcome</span>
                                            <button class="mock-favorite-btn">
                                                <svg class="heart-icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2">
                                                    <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
                                                </svg>
                                            </button>
                                        </div>
                                        <p class="mock-description">Create custom welcome panel with tutorials...</p>
                                    </div>
                                    <div class="mock-favorites-filter">
                                        <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
                                            <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
                                        </svg>
                                        <span>My Favorites</span>
                                        <span class="count">(3)</span>
                                    </div>
                                </div>

                                <p class="step-action">Click the <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display: inline; vertical-align: middle; color: #ef4444;"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg> <strong>heart icon</strong> on any module to favorite it.</p>
                            </div>
                        </div>

                        <div class="modal-footer first-time-footer">
                            <button type="button" class="button tour-skip" id="tour-skip">Skip</button>
                            <div class="tour-nav-buttons">
                                <button type="button" class="button tour-back" id="tour-back" style="display:none;">Back</button>
                                <button type="button" class="button button-primary tour-next" id="tour-next">Next</button>
                            </div>
                        </div>
                    </div>
                </div>

                <?php endif; ?>

                <!-- Results Info & View Controls Row -->
                <div class="switchboard-filters-top">
                    <div class="switchboard-results-info" id="results-info"></div>
                    <div class="view-controls-inline">
                        <!-- Status Filter Button Group -->
                        <!-- Counts will be updated by JavaScript after page load -->
                        <div class="status-filter-group">
                            <button type="button" class="status-filter-btn" data-status="active" title="Show active modules only">
                                <span class="status-dot active-dot"></span> Active <span class="status-count">(...)</span>
                            </button>
                            <button type="button" class="status-filter-btn" data-status="inactive" title="Show inactive modules only">
                                <span class="status-dot inactive-dot"></span> Inactive <span class="status-count">(...)</span>
                            </button>
                            <?php if (SWITCHBOARD_IS_PRO && $beta_mode && $beta_count > 0): ?>
                                <button type="button" class="beta-mode-btn" id="beta-mode-toggle" data-mode="all" title="Beta Modules">
                                    β Beta <span class="sb-filter-count">(<?php echo absint($beta_count); ?>)</span>
                                </button>
                            <?php endif; ?>
                            <button type="button" class="status-clear-btn" style="display: none;" title="Clear status filter">&times;</button>
                        </div>
                        <!-- Description & Use Cases Toggle Buttons -->
                        <div class="filter-toggles-group">
                            <!-- Description Toggle Button -->
                            <button type="button" id="desc-toggle-btn" class="toggle-icon-btn" data-state="full" title="Truncate description">
                                <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                                    <rect x="2" y="3" width="12" height="2" rx="1" fill="currentColor"/>
                                    <rect x="2" y="7" width="12" height="2" rx="1" fill="currentColor"/>
                                    <rect x="2" y="11" width="8" height="2" rx="1" fill="currentColor"/>
                                </svg>
                            </button>
                        </div>
                        <!-- View Mode Toggle -->
                        <div class="view-toggle">
                            <button type="button" class="view-btn" data-view="grid" title="Grid View (3 columns)">
                                <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                                    <rect x="1" y="1" width="6" height="6" rx="1" stroke="currentColor" stroke-width="1.5"/>
                                    <rect x="9" y="1" width="6" height="6" rx="1" stroke="currentColor" stroke-width="1.5"/>
                                    <rect x="1" y="9" width="6" height="6" rx="1" stroke="currentColor" stroke-width="1.5"/>
                                    <rect x="9" y="9" width="6" height="6" rx="1" stroke="currentColor" stroke-width="1.5"/>
                                </svg>
                            </button>
                            <button type="button" class="view-btn" data-view="grid-4col" title="Grid View (4 columns)">
                                <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                                    <rect x="0.5" y="1" width="3" height="6" rx="0.5" stroke="currentColor" stroke-width="1"/>
                                    <rect x="4.5" y="1" width="3" height="6" rx="0.5" stroke="currentColor" stroke-width="1"/>
                                    <rect x="8.5" y="1" width="3" height="6" rx="0.5" stroke="currentColor" stroke-width="1"/>
                                    <rect x="12.5" y="1" width="3" height="6" rx="0.5" stroke="currentColor" stroke-width="1"/>
                                    <rect x="0.5" y="9" width="3" height="6" rx="0.5" stroke="currentColor" stroke-width="1"/>
                                    <rect x="4.5" y="9" width="3" height="6" rx="0.5" stroke="currentColor" stroke-width="1"/>
                                    <rect x="8.5" y="9" width="3" height="6" rx="0.5" stroke="currentColor" stroke-width="1"/>
                                    <rect x="12.5" y="9" width="3" height="6" rx="0.5" stroke="currentColor" stroke-width="1"/>
                                </svg>
                            </button>
                            <button type="button" class="view-btn active" data-view="list-2col" title="List View (2 columns)">
                                <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                                    <rect x="1" y="2" width="6" height="2" rx="1" fill="currentColor"/>
                                    <rect x="9" y="2" width="6" height="2" rx="1" fill="currentColor"/>
                                    <rect x="1" y="7" width="6" height="2" rx="1" fill="currentColor"/>
                                    <rect x="9" y="7" width="6" height="2" rx="1" fill="currentColor"/>
                                    <rect x="1" y="12" width="6" height="2" rx="1" fill="currentColor"/>
                                    <rect x="9" y="12" width="6" height="2" rx="1" fill="currentColor"/>
                                </svg>
                            </button>
                            <button type="button" class="view-btn" data-view="list-3col" title="List View (3 columns)">
                                <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                                    <rect x="1" y="2" width="4" height="2" rx="0.5" fill="currentColor"/>
                                    <rect x="6" y="2" width="4" height="2" rx="0.5" fill="currentColor"/>
                                    <rect x="11" y="2" width="4" height="2" rx="0.5" fill="currentColor"/>
                                    <rect x="1" y="7" width="4" height="2" rx="0.5" fill="currentColor"/>
                                    <rect x="6" y="7" width="4" height="2" rx="0.5" fill="currentColor"/>
                                    <rect x="11" y="7" width="4" height="2" rx="0.5" fill="currentColor"/>
                                    <rect x="1" y="12" width="4" height="2" rx="0.5" fill="currentColor"/>
                                    <rect x="6" y="12" width="4" height="2" rx="0.5" fill="currentColor"/>
                                    <rect x="11" y="12" width="4" height="2" rx="0.5" fill="currentColor"/>
                                </svg>
                            </button>
                        </div>
                        <!-- Sort Dropdown -->
                        <div class="sort-dropdown-wrapper">
                            <select id="sort-select" class="sort-select">
                                <option value="name-asc">Sort: A-Z</option>
                                <option value="name-desc">Sort: Z-A</option>
                                <option value="newest-first">Sort: Newest First</option>
                                <option value="oldest-first">Sort: Oldest First</option>
                                <option value="active-first" selected>Sort: Active First</option>
                                <option value="inactive-first">Sort: Inactive First</option>
                            </select>
                        </div>
                    </div>
                </div>

                <!-- Category Filters - Full Width with Toggle Group (for grid and 3-col views) -->
                <div class="switchboard-filters">
                    <div class="filter-left">
                        <?php
                        // Get user favorites
                        $user_favorites = get_option('switchboard_favorites_' . get_current_user_id(), []);
                        $favorites_count = count($user_favorites);

                        // Count featured modules (exclude pro-locked in free)
                        $featured_count = 0;
                        foreach ($modules as $key => $module) {
                            if (!empty($module['is_pro_locked'])) continue;
                            if ($this->is_popular_module($key)) {
                                $featured_count++;
                            }
                        }
                        // Count total modules for "All" category (exclude pro-locked in free)
                        $total_modules = count(array_filter($modules, function($m) { return empty($m['is_pro_locked']); }));
                        ?>
                        <button class="filter-btn" data-category="all">All <span class="sb-filter-count">(<?php echo esc_html($total_modules); ?>)</span></button>
                        <button class="filter-btn active" data-category="featured"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="14" height="14" style="display: inline-block; vertical-align: middle; margin-right: 4px;"><path fill-rule="evenodd" d="M10.868 2.884c-.321-.772-1.415-.772-1.736 0l-1.83 4.401-4.753.381c-.833.067-1.171 1.107-.536 1.651l3.62 3.102-1.106 4.637c-.194.813.691 1.456 1.405 1.02L10 15.591l4.069 2.485c.713.436 1.598-.207 1.404-1.02l-1.106-4.637 3.62-3.102c.635-.544.297-1.584-.536-1.65l-4.752-.382-1.831-4.401Z" clip-rule="evenodd" /></svg>Featured <span class="sb-filter-count">(<?php echo esc_html($featured_count); ?>)</span></button>
                        <?php
                        $categories = [];
                        foreach ($modules as $key => $module) {
                            $categories[$module['category']][] = $key;
                        }
                        foreach ($categories as $category => $module_keys) {
                            $free_keys = array_filter($module_keys, function($k) use ($modules) { return empty($modules[$k]['is_pro_locked']); });
                            $count = count($free_keys);
                            echo '<button class="filter-btn" data-category="' . esc_attr($category) . '">' . esc_html($category) . ' <span class="sb-filter-count">(' . esc_html($count) . ')</span></button>';
                        }
                        ?>
                    </div>
                    <div class="filter-right">
                        <button class="filter-btn" data-category="favorites" id="favorites-filter-btn"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="14" height="14" style="display: inline-block; vertical-align: middle; margin-right: 4px;"><path d="m9.653 16.915-.005-.003-.019-.01a20.759 20.759 0 0 1-1.162-.682 22.045 22.045 0 0 1-2.582-1.9C4.045 12.733 2 10.352 2 7.5a4.5 4.5 0 0 1 8-2.828A4.5 4.5 0 0 1 18 7.5c0 2.852-2.044 5.233-3.885 6.82a22.049 22.049 0 0 1-3.744 2.582l-.019.01-.005.003h-.002a.739.739 0 0 1-.69.001l-.002-.001Z" /></svg><span class="sb-filter-count">(<?php echo esc_html($favorites_count); ?>)</span></button>
                    </div>
                </div>

                <form method="post" id="switchboard-form" onsubmit="return false;">
                    <?php wp_nonce_field('switchboard_settings', 'nonce'); ?>

                    <!-- License Notice -->
                    <?php if (!$is_licensed): ?>
                        <div class="license-notice" style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 20px; margin: 20px 0; text-align: center;">
                            <h3 style="color: #dc2626; margin: 0 0 10px 0;">License Required</h3>
                            <p style="margin: 0 0 15px 0; color: #7f1d1d;">To access and use Switchboard modules, please activate your license.</p>
                            <button type="button" class="button button-primary" onclick="openLicenseModal()" style="background: #dc2626; border-color: #dc2626;">
                                Activate License
                            </button>
                        </div>
                    <?php endif; ?>

                    <!-- Sidebar Navigation (for 2-col list view only) -->
                    <div class="switchboard-sidebar">
                        <nav class="sidebar-nav">
                            <button class="category-item" data-category="all">
                                <span class="category-name">All</span>
                                <span class="category-count">(<?php echo esc_html($total_modules); ?>)</span>
                            </button>
                            <button class="category-item active" data-category="featured">
                                <span class="category-name">Featured <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="14" height="14" style="display: inline-block; vertical-align: middle; margin-left: 4px;"><path fill-rule="evenodd" d="M10.868 2.884c-.321-.772-1.415-.772-1.736 0l-1.83 4.401-4.753.381c-.833.067-1.171 1.107-.536 1.651l3.62 3.102-1.106 4.637c-.194.813.691 1.456 1.405 1.02L10 15.591l4.069 2.485c.713.436 1.598-.207 1.404-1.02l-1.106-4.637 3.62-3.102c.635-.544.297-1.584-.536-1.65l-4.752-.382-1.831-4.401Z" clip-rule="evenodd" /></svg></span>
                                <span class="category-count">(<?php echo esc_html($featured_count); ?>)</span>
                            </button>
                            <?php
                            foreach ($categories as $category => $module_keys) {
                                $free_keys = array_filter($module_keys, function($k) use ($modules) { return empty($modules[$k]['is_pro_locked']); });
                                $count = count($free_keys);
                                echo '<button class="category-item" data-category="' . esc_attr($category) . '">';
                                echo '<span class="category-name">' . esc_html($category) . '</span>';
                                echo '<span class="category-count">(' . esc_html($count) . ')</span>';
                                echo '</button>';
                            }
                            ?>
                            <button class="category-item" data-category="favorites" id="favorites-sidebar-btn">
                                <span class="category-name">My Favorites <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="14" height="14" style="display: inline-block; vertical-align: middle; margin-left: 4px;"><path d="m9.653 16.915-.005-.003-.019-.01a20.759 20.759 0 0 1-1.162-.682 22.045 22.045 0 0 1-2.582-1.9C4.045 12.733 2 10.352 2 7.5a4.5 4.5 0 0 1 8-2.828A4.5 4.5 0 0 1 18 7.5c0 2.852-2.044 5.233-3.885 6.82a22.049 22.049 0 0 1-3.744 2.582l-.019.01-.005.003h-.002a.739.739 0 0 1-.69.001l-.002-.001Z" /></svg></span>
                                <span class="category-count">(<?php echo esc_html($favorites_count); ?>)</span>
                            </button>
                        </nav>
                    </div>

                    <!-- Unified Module Structure (Single HTML for all views) -->
                    <div class="switchboard-modules switchboard-grid" id="modules-grid" data-view="list-2col">
                        <?php
                        foreach ($modules as $key => $module) {
                            $checked = in_array($key, $active) ? 'checked' : '';
                            $enabled_class = $checked ? 'enabled' : '';
                            $icon = $this->get_module_icon($key, $module['category'], $module);

                            // Build search data from title and tags only (more relevant results)
                            $search_data = strtolower($module['name'] . ' ' . $module['tags']);
                            // Normalize search data: replace hyphens with spaces for better matching
                            $search_data = str_replace('-', ' ', $search_data);

                            // Check if module has a dedicated admin page
                            $admin_page_url = $this->get_module_admin_page_url($key);
                            $has_admin_page = !empty($admin_page_url);
                            
                            // Show settings button if module has settings OR has a dedicated admin page
                            $no_settings_modules = ['avif-support'];
                            $show_settings = $checked && !in_array($key, $no_settings_modules) && ($module['has_settings'] || $has_admin_page);
                            $disabled_class = !$is_licensed ? 'license-required' : '';
                            $is_pro_locked = !empty($module['is_pro_locked']) && !SWITCHBOARD_IS_PRO;
                            $pro_locked_class = $is_pro_locked ? 'pro-locked' : '';
                        ?>
                            <div class="module-card <?php echo esc_attr($enabled_class . ' ' . $disabled_class . ' ' . $pro_locked_class); ?> <?php echo !empty($module['coming_soon']) ? 'coming-soon' : ''; ?>"
                                data-module="<?php echo esc_attr($key); ?>"
                                data-category="<?php echo esc_attr($module['category']); ?>"
                                data-search="<?php echo esc_attr($search_data); ?>"
                                data-has-settings="<?php echo $module['has_settings'] ? '1' : '0'; ?>"
                                data-has-admin-page="<?php echo $has_admin_page ? '1' : '0'; ?>"
                                data-admin-page-url="<?php echo $has_admin_page ? esc_url($admin_page_url) : ''; ?>"
                                data-added="<?php echo esc_attr($module['added']); ?>"
                                data-status="<?php echo $checked ? 'active' : 'inactive'; ?>"
                                data-beta="<?php echo !empty($module['beta']) ? '1' : '0'; ?>"
                                data-coming-soon="<?php echo !empty($module['coming_soon']) ? '1' : '0'; ?>"
                                data-featured="<?php echo $this->is_popular_module($key) ? '1' : '0'; ?>"
                                data-favorited="<?php echo in_array($key, $user_favorites) ? '1' : '0'; ?>"
                                data-pro-locked="<?php echo $is_pro_locked ? '1' : '0'; ?>"
                                <?php if (!$is_licensed): ?>title="License required to use this module" <?php endif; ?>>

                                <!-- Top Right Badges Container -->
                                <div class="module-top-badges">
                                    <!-- Category Badge -->
                                    <span class="module-category-badge"><?php echo esc_html($module['category']); ?></span>

                                    <!-- Coming Soon Badge -->
                                    <?php if (!empty($module['coming_soon'])): ?>
                                        <span class="module-badge coming-soon">Coming Soon</span>
                                    <?php endif; ?>

                                    <!-- New Badge -->
                                    <?php if ($this->is_new_module($key)): ?>
                                        <span class="module-badge new">New</span>
                                    <?php endif; ?>

                                    <!-- Pro Locked Badge (free version) -->
                                    <?php if ($is_pro_locked): ?>
                                        <span class="module-badge pro-badge-locked">PRO</span>
                                    <?php endif; ?>

                                    <!-- License Badge -->
                                    <?php if (SWITCHBOARD_IS_PRO && !$is_licensed): ?>
                                        <span class="license-badge"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="14" height="14"><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 0 0 2.25-2.25v-6.75a2.25 2.25 0 0 0-2.25-2.25H6.75a2.25 2.25 0 0 0-2.25 2.25v6.75a2.25 2.25 0 0 0 2.25 2.25Z" /></svg></span>
                                    <?php endif; ?>
                                </div>

                                <?php if (!empty($icon) && $icon !== '-'): ?>
                                <div class="module-icon"><?php echo $icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- SVG icon markup built safely by get_module_icon() ?></div>
                                <?php endif; ?>

                                <div class="module-content">
                                    <h3 class="module-name"><?php echo esc_html($module['name']); ?><?php if ($this->is_popular_module($key)): ?><span class="module-featured-icon" title="Featured Module"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="14" height="14"><path fill-rule="evenodd" d="M10.868 2.884c-.321-.772-1.415-.772-1.736 0l-1.83 4.401-4.753.381c-.833.067-1.171 1.107-.536 1.651l3.62 3.102-1.106 4.637c-.194.813.691 1.456 1.405 1.02L10 15.591l4.069 2.485c.713.436 1.598-.207 1.404-1.02l-1.106-4.637 3.62-3.102c.635-.544.297-1.584-.536-1.65l-4.752-.382-1.831-4.401Z" clip-rule="evenodd" /></svg></span><?php endif; ?><?php if (!empty($module['beta'])): ?><span class="module-beta-icon" title="Beta Module">β</span><?php endif; ?><button type="button" class="favorite-heart-btn" data-module="<?php echo esc_attr($key); ?>" title="Add to favorites"><svg class="heart-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="14" height="14"><path d="m9.653 16.915-.005-.003-.019-.01a20.759 20.759 0 0 1-1.162-.682 22.045 22.045 0 0 1-2.582-1.9C4.045 12.733 2 10.352 2 7.5a4.5 4.5 0 0 1 8-2.828A4.5 4.5 0 0 1 18 7.5c0 2.852-2.044 5.233-3.885 6.82a22.049 22.049 0 0 1-3.744 2.582l-.019.01-.005.003h-.002a.739.739 0 0 1-.69.001l-.002-.001Z"></path></svg></button><?php if (!empty($module['coming_soon'])): ?><span class="module-badge coming-soon inline-badge">Coming Soon</span><?php endif; ?></h3>
                                    <p class="module-description"><?php echo esc_html($module['description']); ?></p>
                                </div>

                                <div class="module-footer">
                                    <?php if ($is_pro_locked): ?>
                                    <a href="<?php echo esc_url($this->get_upsell_url()); ?>"
                                       target="_blank"
                                       rel="noopener noreferrer"
                                       class="module-upgrade-btn">
                                        Upgrade to Pro
                                    </a>
                                    <?php elseif (empty($module['coming_soon'])): ?>
                                    <label class="module-toggle">
                                        <input type="checkbox"
                                            name="active_modules[]"
                                            value="<?php echo esc_attr($key); ?>"
                                            <?php echo esc_attr($checked); ?>
                                            <?php echo $is_licensed ? '' : 'disabled'; ?>
                                            class="module-checkbox wpsb-module-toggle"
                                            data-module="<?php echo esc_attr($key); ?>">
                                        <span class="toggle-slider"></span>
                                    </label>
                                    <?php endif; ?>
                                    <div class="module-actions">
                                        <?php if (!empty($module['description'])):
                                            // Build description tooltip content
                                            $desc_tooltip = '<div class="tooltip-header">Description</div>';
                                            $desc_tooltip .= '<div class="tooltip-item" style="border:none;margin:0;padding:12px 16px;">';
                                            $desc_tooltip .= '<span style="display:block;line-height:1.5;">' . esc_html($module['description']) . '</span>';
                                            $desc_tooltip .= '</div>';
                                        ?>
                                            <button type="button" class="module-description-icon has-tooltip group" data-tooltip-html="<?php echo esc_attr($desc_tooltip); ?>">
                                                <span style="display:flex;align-items:center;gap:4px;">
                                                    <!-- Info/description icon -->
                                                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="18" height="18" class="transition-colors duration-200 group-hover:!text-sky-500" style="color:oklch(70.7% 0.022 261.325);">
                                                      <path stroke-linecap="round" stroke-linejoin="round" d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z" />
                                                    </svg>
                                                </span>
                                            </button>
                                        <?php endif; ?>
                                        <?php if (!empty($module['usecases'])):
                                            // Build tooltip content with HTML
                                            $tooltip_html = '<div class="tooltip-header">Use Cases</div>';
                                            foreach ($module['usecases'] as $i => $uc) {
                                                $tooltip_html .= '<div class="tooltip-item">';
                                                $tooltip_html .= '<strong>' . esc_html($uc['title']) . '</strong>';
                                                $tooltip_html .= '<span>' . esc_html($uc['description']) . '</span>';
                                                $tooltip_html .= '</div>';
                                            }
                                        ?>
                                            <button type="button" class="module-usecases has-tooltip group" data-tooltip-html="<?php echo esc_attr($tooltip_html); ?>">
                                                <span class="usecase-count" style="display:flex;align-items:center;gap:4px;">
                                                    <!-- Use a feature/idea icon for use cases -->
                                                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="18" height="18" class="transition-colors duration-200 group-hover:!text-sky-500" style="color:oklch(70.7% 0.022 261.325);">
                                                      <path stroke-linecap="round" stroke-linejoin="round" d="M12 18v-5.25m0 0a6.01 6.01 0 0 0 1.5-.189m-1.5.189a6.01 6.01 0 0 1-1.5-.189m3.75 7.478a12.06 12.06 0 0 1-4.5 0m3.75 2.383a14.406 14.406 0 0 1-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 1 0-7.517 0c.85.493 1.509 1.333 1.509 2.316V18" />
                                                    </svg>
                                                </span>
                                            </button>
                                        <?php endif; ?>
                                        <?php if (SWITCHBOARD_SHOW_WHERE_TO_FIND && !empty($module['wheretofind'])):
                                            $wtf = $module['wheretofind'];
                                            // Build tooltip content
                                            $wtf_tooltip = '<div class="tooltip-header">Where to Find This</div>';
                                            $wtf_tooltip .= '<div class="tooltip-item" style="border:none;margin:0;padding:12px 16px;">';
                                            $wtf_tooltip .= '<strong style="font-size:14px;margin-bottom:8px;display:block;">' . esc_html($wtf['location']) . '</strong>';
                                            if (!empty($wtf['instructions'])) {
                                                $wtf_tooltip .= '<span style="display:block;margin-bottom:10px;">' . esc_html($wtf['instructions']) . '</span>';
                                            }
                                            // Only show link if: (1) module is active, (2) URL exists, (3) URL is not just homepage
                                            if ($checked && !empty($wtf['url']) && $wtf['url'] !== '/' && $wtf['url'] !== '') {
                                                $target = !empty($wtf['newTab']) ? '_blank' : '_self';
                                                $full_url = $wtf['url'];
                                                // Handle admin URLs
                                                if (strpos($full_url, 'admin.php') === 0) {
                                                    $full_url = admin_url($full_url);
                                                } elseif (strpos($full_url, '/') === 0) {
                                                    $full_url = home_url($full_url);
                                                }

                                                // Dynamic link text based on location
                                                $link_text = 'Go to ' . esc_html($wtf['location']);

                                                $wtf_tooltip .= '<a href="' . esc_url($full_url) . '" target="' . esc_attr($target) . '" class="wtf-link-btn" style="color:var(--primary-500);text-decoration:none;font-size:13px;font-weight:500;margin-top:8px;display:inline-block;border-bottom:1px solid transparent;transition:all 0.2s ease;">';
                                                $wtf_tooltip .= $link_text;
                                                $wtf_tooltip .= '</a>';
                                            } elseif (!$checked && !empty($wtf['url']) && $wtf['url'] !== '/' && $wtf['url'] !== '') {
                                                // Module is inactive but has URL - show message
                                                $wtf_tooltip .= '<span style="display:block;margin-top:8px;font-size:12px;color:#9ca3af;font-style:italic;">Enable module to access this page</span>';
                                            }
                                            $wtf_tooltip .= '</div>';
                                        ?>
                                            <button type="button" class="module-wheretofind has-tooltip group"
                                                data-tooltip-html="<?php echo esc_attr($wtf_tooltip); ?>"
                                                data-wtf-url="<?php echo esc_attr($wtf['url']); ?>"
                                                data-wtf-location="<?php echo esc_attr($wtf['location']); ?>"
                                                data-wtf-newtab="<?php echo !empty($wtf['newTab']) ? '1' : '0'; ?>">
                                                <span style="display:flex;align-items:center;gap:4px;">
                                                    <!-- Map pin / location icon -->
                                                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="18" height="18" class="transition-colors duration-200 group-hover:!text-sky-500" style="color:oklch(70.7% 0.022 261.325);">
                                                      <path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
                                                      <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0Z" />
                                                    </svg>
                                                </span>
                                            </button>
                                        <?php endif; ?>
                                        <button type="button" class="module-settings" style="display:<?php echo $show_settings ? 'block' : 'none'; ?>;" <?php echo $is_licensed ? '' : 'disabled'; ?>>
                                            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="16" height="16"><path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" /><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" /></svg>
                                        </button>
                                    </div>
                                </div>
                            </div>
                        <?php
                        }
                        ?>
                    </div>

                    <!-- Empty State for No Results -->
                    <div class="switchboard-empty-state" id="empty-state" style="display:none;">
                        <div class="empty-state-content">
                            <svg class="empty-state-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                                <path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
                            </svg>
                            <h3 class="empty-state-title">No modules found</h3>
                            <p class="empty-state-message">We couldn't find any modules matching your search criteria.</p>
                            <div class="empty-state-suggestions">
                                <p class="suggestions-title">Try:</p>
                                <ul class="suggestions-list">
                                    <li>Checking your spelling</li>
                                    <li>Using different keywords</li>
                                    <li>Removing filters to see more results</li>
                                    <li>Browsing all categories</li>
                                </ul>
                            </div>
                            <button class="empty-state-clear-btn" id="clear-all-filters">
                                <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="18" height="18">
                                    <path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99" />
                                </svg>
                                Clear all filters
                            </button>
                        </div>
                    </div>

                    <?php
                    // Group modules by category for list views
                    $modules_by_category = [];
                    foreach ($modules as $key => $module) {
                        $cat = $module['category'];
                        if (!isset($modules_by_category[$cat])) {
                            $modules_by_category[$cat] = [];
                        }
                        $modules_by_category[$cat][$key] = $module;
                    }
                    ?>

                </form>
            </div>
                <?php endif; // End modules tab ?>

                <?php if ($current_tab === 'configurations'): ?>
                    <?php include SWITCHBOARD_PATH . 'includes/templates/configurations-page.php'; ?>
                <?php endif; ?>

                <?php if ($current_tab === 'settings'): ?>
                    <?php include SWITCHBOARD_PATH . 'includes/templates/settings-page.php'; ?>
                <?php endif; ?>

            <?php
            $pro_preset_modals = SWITCHBOARD_PATH . 'pro/templates/preset-modals.php';
            if (file_exists($pro_preset_modals)) {
                include $pro_preset_modals;
            }
            ?>

            <!-- Import Preset Modal -->
            <div id="import-preset-modal" class="switchboard-modal" style="display:none;">
                <div class="modal-content">
                    <div class="modal-header">
                        <h2>Import Configuration</h2>
                        <button class="modal-close">&times;</button>
                    </div>
                    <div class="modal-body">
                        <p>Select a Switchboard preset file to import:</p>
                        <input type="file" id="import-file-input" accept=".switchboard" style="margin: 15px 0;">
                        <div id="import-preview" style="display:none; margin-top: 15px; padding: 15px; background: #f0f6fc; border-radius: 6px;">
                            <h4 style="margin-top: 0;">Preview:</h4>
                            <p><strong>Name:</strong> <span id="preview-name"></span></p>
                            <p><strong>Description:</strong> <span id="preview-description"></span></p>
                            <p><strong>Modules:</strong> <span id="preview-modules"></span></p>
                            <label style="display: block; margin-top: 12px; cursor: pointer;">
                                <input type="checkbox" id="import-apply-immediately" checked style="margin-right: 8px;">
                                Apply settings immediately after import
                            </label>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="sb-module-button sb-module-button-secondary sb-module-button-sm modal-cancel">Cancel</button>
                        <button type="button" id="confirm-import" class="sb-module-button sb-module-button-primary sb-module-button-sm" disabled>Import & Apply</button>
                    </div>
                </div>
            </div>

            <!-- Hidden file input for import -->
            <input type="file" id="preset-file-input" accept=".switchboard" style="display:none;">

            <!-- Settings Panel (Slide-out) -->
            <div id="settings-panel" class="settings-panel" style="display:none;">
                <div class="settings-panel-overlay"></div>
                <div class="settings-panel-content">
                    <div class="settings-panel-header">
                        <h2 id="settings-panel-title">Module Settings</h2>
                        <button class="settings-panel-close">&times;</button>
                    </div>
                    <div class="settings-panel-body" id="settings-panel-body">
                        <p>Loading settings...</p>
                    </div>
                    <div class="settings-panel-footer">
                        <button type="button" class="button settings-panel-cancel">Cancel</button>
                        <button type="button" class="button settings-panel-save" data-action="save">Save</button>
                        <button type="button" class="button button-primary settings-panel-save" data-action="save-refresh">Save & Close</button>
                    </div>
                </div>
            </div>

            <!-- Settings Modal -->
            <div id="settings-modal" class="switchboard-modal" style="display:none;">
                <div class="modal-content">
                    <div class="modal-header">
                        <h2>Plugin Settings</h2>
                        <button class="modal-close">&times;</button>
                    </div>
                    <div class="modal-body">
                        <?php if (SWITCHBOARD_IS_PRO): ?>
                        <?php $beta_mode = filter_var(get_option('switchboard_beta_mode', false), FILTER_VALIDATE_BOOLEAN); ?>

                        <!-- Beta Mode Section (Pro only) -->
                        <div class="sb-module-option-item sb-module-toggle-item">
                            <div class="sb-module-option-content">
                                <div class="sb-module-toggle-header">
                                    <div class="sb-module-option-info">
                                        <strong class="sb-module-option-title">Beta Mode</strong>
                                        <p class="sb-module-option-text">Show experimental features</p>
                                    </div>
                                    <label class="sb-module-toggle">
                                        <input type="checkbox" id="beta-mode-settings-toggle" <?php echo $beta_mode ? 'checked' : ''; ?>>
                                        <span class="sb-module-toggle-slider"></span>
                                    </label>
                                </div>
                            </div>
                        </div>

                        <!-- License Section (Pro only) -->
                        <div class="sb-module-option-item sb-module-toggle-item">
                            <div class="sb-module-option-content">
                                <div class="sb-module-toggle-header">
                                    <div class="sb-module-option-info">
                                        <strong class="sb-module-option-title">License Management</strong>
                                        <?php
                                        $license_status = $this->check_license_status();
                                        $is_active = $license_status['is_active'];
                                        ?>
                                        <p class="sb-module-option-text"><?php echo $is_active ? 'Your license is active and all modules are available' : 'Activate your license to unlock all premium modules'; ?></p>
                                    </div>
                                    <button type="button" class="sb-module-button sb-module-button-primary sb-module-button-sm" onclick="openLicenseModal(); jQuery('#settings-modal').fadeOut();">
                                        Manage License
                                    </button>
                                </div>
                            </div>
                        </div>

                            <!-- Presets Section (Pro only) -->
                            <div class="sb-module-option-item sb-module-toggle-item">
                                <div class="sb-module-option-content">
                                    <div class="sb-module-toggle-header">
                                        <div class="sb-module-option-info">
                                            <strong class="sb-module-option-title">Configuration Presets</strong>
                                            <p class="sb-module-option-text">Save and load module configurations</p>
                                        </div>
                                        <div class="sb-module-button-group">
                                            <button type="button" class="sb-module-button sb-module-button-secondary sb-module-button-sm" id="switchboard-save-preset">Save</button>
                                            <button type="button" class="sb-module-button sb-module-button-secondary sb-module-button-sm" id="switchboard-load-preset">Load</button>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        <?php endif; ?>

                            <!-- Import/Export Section -->
                            <div class="sb-module-option-item sb-module-toggle-item">
                                <div class="sb-module-option-content">
                                    <div class="sb-module-toggle-header">
                                        <div class="sb-module-option-info">
                                            <strong class="sb-module-option-title">Import / Export</strong>
                                            <p class="sb-module-option-text">Backup or transfer your configuration</p>
                                        </div>
                                        <div class="sb-module-button-group">
                                            <button type="button" class="sb-module-button sb-module-button-secondary sb-module-button-sm" id="switchboard-export-preset">Export</button>
                                            <button type="button" class="sb-module-button sb-module-button-secondary sb-module-button-sm" id="switchboard-import-preset">Import</button>
                                        </div>
                                    </div>
                                </div>
                            </div>

                        <!-- Advanced Section -->
                        <div class="sb-module-option-item sb-module-toggle-item">
                            <div class="sb-module-option-content">
                                <div class="sb-module-toggle-header">
                                    <div class="sb-module-option-info">
                                        <strong class="sb-module-option-title">Advanced</strong>
                                        <p class="sb-module-option-text">Force refresh the module list. Use this if modules aren't appearing correctly.</p>
                                    </div>
                                    <button type="button" class="sb-module-button sb-module-button-secondary sb-module-button-sm" id="clear-module-cache">
                                        Clear Cache & Refresh
                                    </button>
                                </div>
                            </div>
                        </div>

                        <!-- Danger Zone -->
                        <div class="sb-sp-info-box sb-sp-info-box-danger" style="margin-top: 24px;">
                            <h4>Danger Zone</h4>
                            <p class="sb-sp-description">Data removal actions that cannot be undone</p>

                            <!-- Option 1: Clear Inactive Module Data -->
                            <div class="sb-module-option-item sb-module-toggle-item" style="margin-top: 12px;">
                                <div class="sb-module-option-content">
                                    <div class="sb-module-toggle-header">
                                        <div class="sb-module-option-info">
                                            <strong class="sb-module-option-title">Clear Inactive Module Data</strong>
                                            <p class="sb-module-option-text">Removes data from disabled modules. Active modules are safe.</p>
                                        </div>
                                        <button type="button" class="sb-module-button sb-module-button-danger sb-module-button-sm" id="clear-inactive-data-btn">
                                            Clear Inactive
                                        </button>
                                    </div>
                                </div>
                            </div>

                            <!-- Option 2: Clear All Plugin Data -->
                            <div class="sb-module-option-item sb-module-toggle-item" style="margin-top: 8px;">
                                <div class="sb-module-option-content">
                                    <div class="sb-module-toggle-header">
                                        <div class="sb-module-option-info">
                                            <strong class="sb-module-option-title">Clear All Plugin Data</strong>
                                            <p class="sb-module-option-text">Permanently deletes ALL data. Cannot be undone.</p>
                                        </div>
                                        <button type="button" class="sb-module-button sb-module-button-danger sb-module-button-sm" id="clear-all-data-btn">
                                            Clear All Data
                                        </button>
                                    </div>
                                </div>
                            </div>
                        </div>

                        <?php if (SWITCHBOARD_IS_PRO): ?>
                        <!-- Privacy / Opt-out Section (Pro only) -->
                        <div class="sb-module-option-item sb-module-toggle-item" style="margin-top: 24px;">
                            <div class="sb-module-option-content">
                                <div class="sb-module-toggle-header">
                                    <div class="sb-module-option-info">
                                        <strong class="sb-module-option-title">Privacy & Data</strong>
                                        <p class="sb-module-option-text">Control data collection preferences</p>
                                    </div>
                                    <button type="button" class="sb-module-button sb-module-button-secondary sb-module-button-sm" id="toggle-privacy-options">
                                        Advanced <span id="privacy-chevron"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="12" height="12"><path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" /></svg></span>
                                    </button>
                                </div>

                                <!-- Advanced Privacy Options (Hidden by default) -->
                                <div id="privacy-advanced-options" style="display: none; margin-top: 16px; padding-top: 16px; border-top: 1px solid #e2e8f0;">
                                    <!-- Telemetry Toggle -->
                                    <div class="sb-module-toggle-header" style="padding: 12px 0; border-bottom: 1px solid #e2e8f0;">
                                        <div class="sb-module-option-info">
                                            <strong class="sb-module-option-title">Disable Usage Telemetry</strong>
                                            <p class="sb-module-option-text">Turn ON to stop collecting anonymous analytics</p>
                                        </div>
                                        <label class="sb-module-toggle">
                                            <?php $opt_out_telemetry = filter_var(get_option('switchboard_opt_out_telemetry', false), FILTER_VALIDATE_BOOLEAN); ?>
                                            <input type="checkbox" id="opt-out-telemetry-toggle" <?php echo $opt_out_telemetry ? 'checked' : ''; ?>>
                                            <span class="sb-module-toggle-slider"></span>
                                        </label>
                                    </div>

                                    <!-- Disable Feedback Toggle -->
                                    <div class="sb-module-toggle-header" style="padding: 12px 0;">
                                        <div class="sb-module-option-info">
                                            <strong class="sb-module-option-title">Disable Feedback Prompts</strong>
                                            <p class="sb-module-option-text">Turn ON to stop asking for feedback when disabling modules</p>
                                        </div>
                                        <label class="sb-module-toggle">
                                            <?php $disable_feedback = filter_var(get_option('switchboard_disable_feedback', true), FILTER_VALIDATE_BOOLEAN); ?>
                                            <input type="checkbox" id="disable-feedback-toggle" <?php echo $disable_feedback ? 'checked' : ''; ?>>
                                            <span class="sb-module-toggle-slider"></span>
                                        </label>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <?php endif; ?>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="sb-module-button sb-module-button-secondary modal-cancel">Cancel</button>
                        <button type="button" class="sb-module-button sb-module-button-primary" id="save-settings">Save Changes</button>
                    </div>
                </div>
            </div>

            <?php if (SWITCHBOARD_IS_PRO): ?>
            <!-- License Modal -->
            <div id="license-modal" class="license-modal" style="display: none; position: fixed; z-index: 100000; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5);">
                <div class="license-modal-content" style="background-color: #ffffff; margin: 5% auto; padding: 0; border: 1px solid #e5e7eb; border-radius: 6px; width: 90%; max-width: 600px; box-shadow: 0 10px 25px rgba(0,0,0,0.1);">
                    <div class="license-modal-header" style="background: #ffffff; border-bottom: 1px solid #e5e7eb; padding: 20px; position: relative;">
                        <h2 style="margin: 0; font-size: 18px; font-weight: 500; color: #111827;">License Management</h2>
                        <span class="license-modal-close" onclick="closeLicenseModal()" style="position: absolute; right: 20px; top: 20px; font-size: 20px; font-weight: normal; cursor: pointer; color: #6b7280; opacity: 0.7;">&times;</span>
                    </div>
                    <div class="license-modal-body" style="padding: 30px;">
                        <?php
                        // Use the same license status check as the main page
                        $license_status = $this->check_license_status();
                        $is_active = $license_status['is_active'];
                        $is_valid = $license_status['is_valid'];
                        $has_license_key = !empty($license_status['license_key']);
                        $has_activation_id = !empty($license_status['activation_id']);
                        ?>

                        <!-- License Status Section -->
                        <div class="license-status-section" style="margin-bottom: 30px;">
                            <div style="display: flex; align-items: center; justify-content: space-between; padding: 16px; background: <?php echo $is_active ? 'var(--primary-50)' : '#fef2f2'; ?>; border: 1px solid <?php echo $is_active ? 'var(--primary-200)' : '#fecaca'; ?>; border-radius: 6px;">
                                <div style="display: flex; align-items: center;">
                                    <div style="font-size: 24px; margin-right: 12px;"><?php echo $is_active ? '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="#16a34a" width="24" height="24"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" /></svg>' : '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="#dc2626" width="24" height="24"><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 0 0 2.25-2.25v-6.75a2.25 2.25 0 0 0-2.25-2.25H6.75a2.25 2.25 0 0 0-2.25 2.25v6.75a2.25 2.25 0 0 0 2.25 2.25Z" /></svg>'; ?></div>
                                    <div>
                                        <div style="font-weight: 500; color: #111827; margin-bottom: 2px;">
                                            <?php echo $is_active ? 'License Active' : 'License Required'; ?>
                                        </div>
                                        <div style="font-size: 14px; color: #6b7280;">
                                            <?php echo $is_active ? 'All modules are available' : 'Enter license key to activate modules'; ?>
                                        </div>
                                    </div>
                                </div>
                                <div style="text-align: right;">
                                    <div style="font-size: 12px; color: #6b7280; margin-bottom: 2px;">Status</div>
                                    <div style="font-weight: 500; color: <?php echo $is_active ? 'var(--primary-600)' : '#dc2626'; ?>;">
                                        <?php echo $is_active ? 'Active' : 'Inactive'; ?>
                                    </div>
                                </div>
                            </div>
                        </div>

                        <?php if ($is_active): ?>
                            <!-- Active License Actions -->
                            <div style="display: flex; gap: 12px; flex-wrap: wrap; justify-content: end;">
                                <button type="button" class="button button-secondary" onclick="refreshLicenseStatus()" style="background: white; border: 1px solid var(--primary-100); color: var(--primary-700); padding: 4px 12px; border-radius: 8px; font-weight: 500; font-size: 14px; transition: all 0.15s ease;">
                                    Refresh Status
                                </button>
                                <button type="button" class="button button-secondary" onclick="deactivateLicense()" style="background: white; border: 1px solid #fee2e2; color: #dc2626; padding: 4px 12px; border-radius: 8px; font-weight: 500; font-size: 14px; transition: all 0.15s ease;">
                                    Deactivate License
                                </button>
                            </div>
                        <?php else: ?>
                            <!-- Inactive License Actions -->
                            <form id="license-form" onsubmit="activateLicense(event)">
                                <div style="margin-bottom: 16px;">
                                    <label for="license-key" style="display: block; margin-bottom: 8px; font-weight: 500; color: #374151;">License Key</label>
                                    <input type="text" id="license-key" name="license_key" placeholder="Enter your license key"
                                        style="width: 100%; padding: 12px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px; box-sizing: border-box; background: #ffffff;"
                                        value="<?php echo esc_attr($license_status['license_key'] ?? ''); ?>">
                                </div>

                                <div id="license-message" style="margin-bottom: 16px; padding: 12px; border-radius: 6px; display: none; font-size: 14px;"></div>

                                <div style="display: flex; gap: 12px; justify-content: flex-end;">
                                    <button type="button" class="button button-secondary" onclick="closeLicenseModal()" style="background: #f3f4f6; border-color: #d1d5db; color: #374151;">
                                        Cancel
                                    </button>
                                    <button type="submit" class="button button-primary" id="activate-btn" style="background: var(--primary-600); border-color: var(--primary-600); color: white;">
                                        <span class="btn-text">Activate License</span>
                                        <span class="btn-loading" style="display: none;">Activating...</span>
                                    </button>
                                </div>
                            </form>
                        <?php endif; ?>
                    </div>
                </div>
            </div>
        </div>

        <script>
            function openLicenseModal() {
                document.getElementById('license-modal').style.display = 'block';
            }

            function closeLicenseModal() {
                document.getElementById('license-modal').style.display = 'none';
                clearLicenseMessage();
            }

            function showLicenseMessage(message, type = 'info') {
                const messageDiv = document.getElementById('license-message');
                if (!messageDiv) return;

                messageDiv.style.display = 'block';
                messageDiv.textContent = message;
                messageDiv.style.padding = '4px 12px';
                messageDiv.style.borderRadius = '8px';
                messageDiv.style.marginTop = '16px';
                messageDiv.style.fontSize = '14px';
                messageDiv.style.fontWeight = '500';
                messageDiv.style.transition = 'all 0.2s ease';

                if (type === 'error') {
                    messageDiv.style.backgroundColor = '#fef2f2';
                    messageDiv.style.color = '#dc2626';
                    messageDiv.style.border = '1px solid #fecaca';
                } else if (type === 'success') {
                    messageDiv.style.backgroundColor = 'var(--primary-50)';
                    messageDiv.style.color = 'var(--primary-700)';
                    messageDiv.style.border = '1px solid var(--primary-300)';
                } else {
                    // Info state with primary theme
                    messageDiv.style.backgroundColor = 'var(--primary-50)';
                    messageDiv.style.color = 'var(--primary-900)';
                    messageDiv.style.border = '1px solid var(--primary-200)';
                }
            }

            function clearLicenseMessage() {
                const messageDiv = document.getElementById('license-message');
                if (messageDiv) {
                    messageDiv.style.display = 'none';
                    messageDiv.textContent = '';
                }
            }

            function setLoadingState(loading) {
                const btn = document.getElementById('activate-btn');
                if (!btn) return;

                const btnText = btn.querySelector('.btn-text');
                const btnLoading = btn.querySelector('.btn-loading');

                if (loading) {
                    btn.disabled = true;
                    if (btnText) btnText.style.display = 'none';
                    if (btnLoading) btnLoading.style.display = 'inline';
                } else {
                    btn.disabled = false;
                    if (btnText) btnText.style.display = 'inline';
                    if (btnLoading) btnLoading.style.display = 'none';
                }
            }

            function refreshLicenseStatus() {
                // Get the button element
                const refreshBtn = event.target.closest('button');
                const originalHTML = refreshBtn.innerHTML;

                // Disable button and show loading state
                refreshBtn.disabled = true;
                refreshBtn.style.opacity = '0.6';
                refreshBtn.style.cursor = 'not-allowed';
                refreshBtn.innerHTML = '<span style="display: inline-flex; align-items: center; gap: 6px;"><svg style="animation: spin 1s linear infinite;" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>Refreshing...</span>';

                showLicenseMessage('Checking license status with server...', 'info');

                // Create form data
                const formData = new FormData();
                formData.append('action', 'switchboard_get_license_status');
                formData.append('_wpnonce', '<?php echo esc_attr(wp_create_nonce('switchboard_license')); ?>');

                fetch(ajaxurl, {
                        method: 'POST',
                        body: formData
                    })
                    .then(response => response.json())
                    .then(data => {
                        if (data.success) {
                            refreshBtn.innerHTML = '<span style="display: inline-flex; align-items: center; gap: 6px;">Refreshed!</span>';
                            showLicenseMessage('License status refreshed successfully! Reloading page...', 'success');
                            setTimeout(() => {
                                window.location.reload();
                            }, 1500);
                        } else {
                            refreshBtn.disabled = false;
                            refreshBtn.style.opacity = '1';
                            refreshBtn.style.cursor = 'pointer';
                            refreshBtn.innerHTML = originalHTML;
                            showLicenseMessage(data.data.message || 'Failed to refresh license status.', 'error');
                        }
                    })
                    .catch(error => {
                        refreshBtn.disabled = false;
                        refreshBtn.style.opacity = '1';
                        refreshBtn.style.cursor = 'pointer';
                        refreshBtn.innerHTML = originalHTML;
                        showLicenseMessage('An error occurred while refreshing status.', 'error');
                    });
            }

            function activateLicense(event) {
                event.preventDefault();
                clearLicenseMessage();

                const licenseKey = document.getElementById('license-key').value.trim();
                if (!licenseKey) {
                    showLicenseMessage('Please enter a license key.', 'error');
                    return;
                }

                setLoadingState(true);

                // Create form data
                const formData = new FormData();
                formData.append('action', 'switchboard_activate_license');
                formData.append('license_key', licenseKey);
                formData.append('_wpnonce', '<?php echo esc_attr(wp_create_nonce('switchboard_license')); ?>');

                fetch(ajaxurl, {
                        method: 'POST',
                        body: formData
                    })
                    .then(response => response.json())
                    .then(data => {
                        setLoadingState(false);

                        if (data.success) {
                            showLicenseMessage('License activated successfully! Reloading page...', 'success');

                            // If this is first activation, flag to show welcome tour after reload
                            if (data.data && data.data.show_welcome_tour) {
                                try {
                                    sessionStorage.setItem('switchboard_show_welcome_tour', '1');
                                } catch (e) {
                                    // Fallback: use URL parameter if sessionStorage fails
                                    console.log('SessionStorage not available, using URL parameter');
                                }
                            }

                            setTimeout(() => {
                                // Add URL parameter as fallback if sessionStorage isn't available
                                if (data.data && data.data.show_welcome_tour) {
                                    window.location.href = window.location.href + (window.location.href.indexOf('?') > -1 ? '&' : '?') + 'show_tour=1';
                                } else {
                                    window.location.reload();
                                }
                            }, 2000);
                        } else {
                            showLicenseMessage(data.data.message || 'Activation failed. Please check your license key.', 'error');
                        }
                    })
                    .catch(error => {
                        setLoadingState(false);
                        showLicenseMessage('An error occurred. Please try again.', 'error');
                    });
            }

            function deactivateLicense() {
                if (!confirm('Are you sure you want to deactivate your license? This will disable all modules.')) {
                    return;
                }

                showLicenseMessage('Deactivating license...', 'info');

                // Create form data
                const formData = new FormData();
                formData.append('action', 'switchboard_deactivate_license');
                formData.append('_wpnonce', '<?php echo esc_attr(wp_create_nonce('switchboard_license')); ?>');

                fetch(ajaxurl, {
                        method: 'POST',
                        body: formData
                    })
                    .then(response => response.json())
                    .then(data => {
                        if (data.success) {
                            showLicenseMessage('License deactivated successfully! Reloading page...', 'success');
                            setTimeout(() => {
                                window.location.reload();
                            }, 2000);
                        } else {
                            showLicenseMessage(data.data.message || 'Deactivation failed.', 'error');
                        }
                    })
                    .catch(error => {
                        showLicenseMessage('An error occurred. Please try again.', 'error');
                    });
            }

            // Close modal when clicking outside
            window.onclick = function(event) {
                const modal = document.getElementById('license-modal');
                if (event.target === modal) {
                    closeLicenseModal();
                }
            }

            // Close modal with Escape key
            document.addEventListener('keydown', function(event) {
                if (event.key === 'Escape') {
                    closeLicenseModal();
                }
            });
        </script>
        <?php endif; ?>

        <?php if (SWITCHBOARD_IS_PRO): ?>
        <!-- Feedback Button -->
        <a href="https://wpswitchboard.ideas.userback.io"
            target="_blank"
            rel="noopener noreferrer"
            class="switchboard-feedback-btn"
            title="Share feedback, report bugs, or request features">
            Feedback
        </a>
        <?php endif; ?>

    <?php
    }

    // ============================================================================
    // 6. UTILITY FUNCTIONS
    // ============================================================================

    /**
     * Get the upsell URL for pro upgrade prompts
     */
    private function get_upsell_url()
    {
        $json_path = SWITCHBOARD_PATH . 'pro-modules.json';
        if (file_exists($json_path)) {
            $config = json_decode(file_get_contents($json_path), true);
            if (!empty($config['upsell_url'])) {
                return $config['upsell_url'];
            }
        }
        return 'https://wpswitchboard.com/pricing';
    }

    /**
     * Merge pro module placeholders from pro-modules.json for locked display in free version
     */
    private function merge_pro_module_placeholders($modules)
    {
        if (SWITCHBOARD_IS_PRO) {
            return $modules;
        }

        $json_path = SWITCHBOARD_PATH . 'pro-modules.json';
        if (!file_exists($json_path)) {
            return $modules;
        }

        $config = json_decode(file_get_contents($json_path), true);
        if (!isset($config['pro_modules']) || !is_array($config['pro_modules'])) {
            return $modules;
        }

        foreach ($config['pro_modules'] as $slug => $meta) {
            if (!isset($modules[$slug])) {
                $modules[$slug] = [
                    'name' => $meta['name'] ?? 'Pro Module',
                    'description' => $meta['description'] ?? '',
                    'category' => $meta['category'] ?? 'General',
                    'tags' => $meta['tags'] ?? '',
                    'added' => '',
                    'beta' => false,
                    'disabled' => false,
                    'usecases' => [],
                    'wheretofind' => null,
                    'has_settings' => false,
                    'is_pro_locked' => true,
                ];
            }
        }

        return $modules;
    }

    /**
     * Check license status
     * 
     * @return array License status information
     */
    private function check_license_status($force_api_check = false)
    {
        // Free version doesn't need licensing
        if (!SWITCHBOARD_IS_PRO) {
            return [
                'is_valid' => true,
                'is_active' => true,
                'license_key' => '',
                'activation_id' => '',
                'last_checked' => current_time('mysql')
            ];
        }

        // Grab cached license status unless forced to hit the API
        if (!$force_api_check) {
            $cached_status = get_option('wpswitchboard_license_status', null);

            if ($cached_status !== null && is_array($cached_status)) {
                return $cached_status;
            }
        }

        // Fresh check - happens on activate, deactivate, or cache miss
        $config = switchboard_get_surecart_config();
        $client = new \WP_Switchboard\Licensing\Client(
            $config['plugin_name'],
            $config['public_token'],
            __FILE__
        );

        // Set the same consistent option key
        $client->settings()->set_option_key('wpswitchboard_license_options');
        
        // Verify we're using the correct plugin slug (safeguard against conflicts)
        if ($client->slug !== SWITCHBOARD_PLUGIN_SLUG) {
            // Clear any stale license data that might be from another product
            $client->settings()->clear_options();
            delete_option('wpswitchboard_license_status');
            return [
                'is_valid' => false,
                'is_active' => false,
                'error' => 'Plugin configuration error: slug mismatch'
            ];
        }

        $license = $client->license();

        // Get the settings object (same as license management page)
        $settings = $client->settings();

        // Use a more reliable check since !empty() seems to be failing
        // Cast to string to bypass any magic property issues
        $license_key_str = (string)$settings->license_key;
        $activation_id_str = (string)$settings->activation_id;

        $has_license_key = $license_key_str !== '' && $license_key_str !== 'Not set' && strlen($license_key_str) > 0;
        $has_activation_id = $activation_id_str !== '' && $activation_id_str !== 'Not set' && strlen($activation_id_str) > 0;

        // Only call API if we have activation data (otherwise it's a fresh install)
        $is_valid = false;
        if ($has_license_key && $has_activation_id) {
            $is_valid = $license->is_valid();
        }

        // If we have both, consider it active
        $is_active = $has_license_key && $has_activation_id && $is_valid;

        $status = [
            'is_valid' => $is_valid === true,
            'is_active' => $is_active,
            'license_key' => $settings->license_key,
            'activation_id' => $settings->activation_id,
            'last_checked' => current_time('mysql')
        ];

        // Cache the result in DB (only if we actually checked, or if forced)
        if ($force_api_check || $has_license_key || $has_activation_id) {
            update_option('wpswitchboard_license_status', $status, false);
        }

        return $status;
    }

    /**
     * Recursively sanitize arrays and values
     *
     * @param mixed $value Value to sanitize (can be string, array, etc.)
     * @return mixed Sanitized value
     */
    private function sanitize_recursive($value)
    {
        if (is_array($value)) {
            return array_map([$this, 'sanitize_recursive'], $value);
        }
        // Use wp_unslash to remove magic quotes added by WordPress
        return sanitize_text_field(wp_unslash($value));
    }

    private function get_category_icon($category)
    {
        // Map categories to their SVG icons
        $icons = [
            'Content' => '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="16" height="16"><path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10" /></svg>',
            'Media' => '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="16" height="16"><path stroke-linecap="round" stroke-linejoin="round" d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z" /></svg>',
            'Admin' => '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="16" height="16"><path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" /><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" /></svg>',
            'User Management' => '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="16" height="16"><path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z" /></svg>',
            'Security' => '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="16" height="16"><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 0 0 2.25-2.25v-6.75a2.25 2.25 0 0 0-2.25-2.25H6.75a2.25 2.25 0 0 0-2.25 2.25v6.75a2.25 2.25 0 0 0 2.25 2.25Z" /></svg>',
            'Optimization' => '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="16" height="16"><path stroke-linecap="round" stroke-linejoin="round" d="m3.75 13.5 10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75Z" /></svg>',
            'Utilities' => '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="16" height="16"><path stroke-linecap="round" stroke-linejoin="round" d="M11.42 15.17 17.25 21A2.652 2.652 0 0 0 21 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 1 1-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 0 0 4.486-6.336l-3.276 3.277a3.004 3.004 0 0 1-2.25-2.25l3.276-3.276a4.5 4.5 0 0 0-6.336 4.486c.091 1.076-.071 2.264-.904 2.95l-.102.085m-1.745 1.437L5.909 7.5H4.5L2.25 3.75l1.5-1.5L7.5 4.5v1.409l4.26 4.26m-1.745 1.437 1.745-1.437m6.615 8.206L15.75 15.75M4.867 19.125h.008v.008h-.008v-.008Z" /></svg>',
            'General' => '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="16" height="16"><path stroke-linecap="round" stroke-linejoin="round" d="m20.25 7.5-.625 10.632a2.25 2.25 0 0 1-2.247 2.118H6.622a2.25 2.25 0 0 1-2.247-2.118L3.75 7.5m8.25 3v6.75m0 0-3-3m3 3 3-3M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125Z" /></svg>'
        ];
        return isset($icons[$category]) ? $icons[$category] : $icons['General'];
    }

    /**
     * Mark crowd favorites with the Popular badge
     */
    private function is_popular_module($module_key)
    {
        $popular_modules = [
            'limit-login-attempts',
            'disable-file-editing',
            'database-optimizer',
            'media-replacement',
            'cpt-builder',
            'admin-columns-manager',
            'email-smtp',
            'code-snippets-manager',
            'custom-field-groups',
            'gutenberg-block-control',
            'temporary-login',
            'login-as-user',
            'coming-soon',
            'simple-forms',
            'lean-analytics'
        ];
        return in_array($module_key, $popular_modules);
    }

    /**
     * Check if a module is new (added recently)
     */
    private function is_new_module($module_key)
    {
        $modules = $this->get_available_modules();

        if (!isset($modules[$module_key]) || empty($modules[$module_key]['added'])) {
            return false;
        }

        $added_date = $modules[$module_key]['added'];

        // Try to parse the date
        $timestamp = strtotime($added_date);
        if ($timestamp === false) {
            return false;
        }

        // Consider "new" if added within last 30 days
        $thirty_days_ago = strtotime('-30 days');
        return $timestamp >= $thirty_days_ago;
    }

    /**
     * Get icon for a module
     * Uses module's Icon header if defined, otherwise falls back to category icon
     */
    private function get_module_icon($module_key, $category, $module = [])
    {
        // Use module's Icon header if defined
        if (!empty($module['icon'])) {
            return $module['icon'];
        }

        // Fall back to category icon
        return $this->get_category_icon($category);
    }

    // Post/taxonomy order AJAX handler methods removed - now handled by their respective modules

    public function render_quick_launcher()
    {
        // Admins only - keeps the launcher exclusive
        if (!current_user_can('manage_options')) {
            return;
        }
    ?>
        <!-- Quick Launcher Modal (Cmd/Ctrl + K) -->
        <div id="switchboard-quick-launcher" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.7); z-index: 999999; align-items: flex-start; justify-content: center; padding-top: 15vh;">
            <div style="background: white; border-radius: 12px; width: 90%; max-width: 600px; box-shadow: 0 20px 25px -5px rgba(0,0,0,0.3); overflow: hidden;">
                <!-- Search Input -->
                <div style="padding: 20px; border-bottom: 1px solid #e5e7eb;">
                    <input type="text" id="switchboard-quick-search" placeholder="Search Switchboard modules... (Ctrl/Cmd + K)" style="width: 100%; padding: 12px 16px; border: 2px solid #3b82f6; border-radius: 8px; font-size: 16px; outline: none;" autofocus>
                    <p style="margin: 8px 0 0 0; font-size: 12px; color: #6b7280;">
                        Press <kbd style="background: #f3f4f6; padding: 2px 6px; border-radius: 3px; font-family: monospace;">↑</kbd>
                        <kbd style="background: #f3f4f6; padding: 2px 6px; border-radius: 3px; font-family: monospace;">↓</kbd> to navigate,
                        <kbd style="background: #f3f4f6; padding: 2px 6px; border-radius: 3px; font-family: monospace;">Enter</kbd> to select,
                        <kbd style="background: #f3f4f6; padding: 2px 6px; border-radius: 3px; font-family: monospace;">Esc</kbd> to close
                    </p>
                </div>

                <!-- Results -->
                <div id="switchboard-quick-results" style="max-height: 400px; overflow-y: auto;">
                    <div style="padding: 40px; text-align: center; color: #9ca3af;">
                        <p style="margin: 0; font-size: 14px;">Start typing to search modules...</p>
                    </div>
                </div>
            </div>
        </div>

        <script>
            jQuery(document).ready(function($) {
                let selectedIndex = -1;
                let searchTimeout = null;
                let currentResults = [];

                // Open launcher with Cmd/Ctrl + K (but not on Switchboard page)
                $(document).on('keydown', function(e) {
                    if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
                        // Check if we're on the Switchboard page
                        const urlParams = new URLSearchParams(window.location.search);
                        const isOnSwitchboardPage = urlParams.get('page') === 'switchboard';

                        // If on Switchboard page, let the native search handle it
                        if (!isOnSwitchboardPage) {
                            e.preventDefault();
                            openQuickLauncher();
                        }
                    }

                    // Close with Escape
                    if (e.key === 'Escape' && $('#switchboard-quick-launcher').is(':visible')) {
                        closeQuickLauncher();
                    }
                });

                // Search input
                $('#switchboard-quick-search').on('input', function() {
                    clearTimeout(searchTimeout);
                    const query = $(this).val();

                    searchTimeout = setTimeout(function() {
                        searchModules(query);
                    }, 200);
                });

                // Keyboard navigation
                $('#switchboard-quick-search').on('keydown', function(e) {
                    const $results = $('#switchboard-quick-results .quick-result-item');

                    if (e.key === 'ArrowDown') {
                        e.preventDefault();
                        selectedIndex = Math.min(selectedIndex + 1, $results.length - 1);
                        updateSelection();
                    } else if (e.key === 'ArrowUp') {
                        e.preventDefault();
                        selectedIndex = Math.max(selectedIndex - 1, -1);
                        updateSelection();
                    } else if (e.key === 'Enter' && selectedIndex >= 0) {
                        e.preventDefault();
                        const $selected = $results.eq(selectedIndex);
                        const moduleKey = $selected.data('module');
                        const isActive = $selected.data('active');
                        const hasSettings = $selected.data('has-settings');
                        const hasAdminPage = $selected.data('has-admin-page');
                        const adminPageUrl = $selected.data('admin-page-url');
                        handleModuleClick(moduleKey, isActive, hasSettings, hasAdminPage, adminPageUrl);
                    }
                });

                // Click outside to close
                $('#switchboard-quick-launcher').on('click', function(e) {
                    if (e.target.id === 'switchboard-quick-launcher') {
                        closeQuickLauncher();
                    }
                });

                function openQuickLauncher() {
                    $('#switchboard-quick-launcher').css('display', 'flex');
                    $('#switchboard-quick-search').val('').focus();
                    selectedIndex = -1;
                    searchModules(''); // Show all modules initially
                }

                function closeQuickLauncher() {
                    $('#switchboard-quick-launcher').hide();
                    $('#switchboard-quick-results').html('<div style="padding: 40px; text-align: center; color: #9ca3af;"><p style="margin: 0; font-size: 14px;">Start typing to search modules...</p></div>');
                }

                function searchModules(query) {
                    $.ajax({
                        url: ajaxurl,
                        type: 'POST',
                        data: {
                            action: 'switchboard_search_modules',
                            query: query
                        },
                        success: function(response) {
                            if (response.success) {
                                currentResults = response.data;
                                renderResults(response.data);
                            }
                        }
                    });
                }

                function renderResults(results) {
                    if (results.length === 0) {
                        $('#switchboard-quick-results').html('<div style="padding: 40px; text-align: center; color: #9ca3af;"><p style="margin: 0; font-size: 14px;">No modules found</p></div>');
                        return;
                    }

                    let html = '';
                    results.forEach(function(module, index) {
                        const categoryIcons = {
                            'admin': '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="20" height="20"><path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" /><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" /></svg>',
                            'content': '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="20" height="20"><path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10" /></svg>',
                            'media': '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="20" height="20"><path stroke-linecap="round" stroke-linejoin="round" d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z" /></svg>',
                            'security': '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="20" height="20"><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 0 0 2.25-2.25v-6.75a2.25 2.25 0 0 0-2.25-2.25H6.75a2.25 2.25 0 0 0-2.25 2.25v6.75a2.25 2.25 0 0 0 2.25 2.25Z" /></svg>',
                            'optimization': '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="20" height="20"><path stroke-linecap="round" stroke-linejoin="round" d="m3.75 13.5 10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75Z" /></svg>',
                            'user-management': '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="20" height="20"><path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z" /></svg>',
                            'utilities': '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="20" height="20"><path stroke-linecap="round" stroke-linejoin="round" d="M11.42 15.17 17.25 21A2.652 2.652 0 0 0 21 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 1 1-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 0 0 4.486-6.336l-3.276 3.277a3.004 3.004 0 0 1-2.25-2.25l3.276-3.276a4.5 4.5 0 0 0-6.336 4.486c.091 1.076-.071 2.264-.904 2.95l-.102.085m-1.745 1.437L5.909 7.5H4.5L2.25 3.75l1.5-1.5L7.5 4.5v1.409l4.26 4.26m-1.745 1.437 1.745-1.437m6.615 8.206L15.75 15.75M4.867 19.125h.008v.008h-.008v-.008Z" /></svg>'
                        };

                        const icon = categoryIcons[module.category] || '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="20" height="20"><path stroke-linecap="round" stroke-linejoin="round" d="m20.25 7.5-.625 10.632a2.25 2.25 0 0 1-2.247 2.118H6.622a2.25 2.25 0 0 1-2.247-2.118L3.75 7.5m8.25 3v6.75m0 0-3-3m3 3 3-3M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125Z" /></svg>';
                        const statusBadge = module.is_active ?
                            '<span style="background: var(--primary-100); color: var(--primary-800); padding: 4px 8px; border-radius: 4px; font-size: 11px; font-weight: 600;">ACTIVE</span>' :
                            '<span style="background: #fee2e2; color: #991b1b; padding: 4px 8px; border-radius: 4px; font-size: 11px; font-weight: 600;">DISABLED</span>';

                        html += '<div class="quick-result-item" data-index="' + index + '" data-module="' + module.key + '" data-active="' + module.is_active + '" data-has-settings="' + (module.has_settings || false) + '" data-has-admin-page="' + (module.has_admin_page || false) + '" data-admin-page-url="' + (module.admin_page_url || '') + '" style="padding: 16px 20px; border-bottom: 1px solid #f3f4f6; cursor: pointer; display: flex; align-items: center; gap: 12px; transition: background 0.15s;">';
                        html += '<span style="font-size: 24px;">' + icon + '</span>';
                        html += '<div style="flex: 1; min-width: 0;">';
                        html += '<div style="font-weight: 600; font-size: 14px; color: #111827; margin-bottom: 2px;">' + module.name + '</div>';
                        html += '<div style="font-size: 12px; color: #6b7280; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">' + module.description + '</div>';
                        html += '</div>';
                        html += statusBadge;
                        html += '</div>';
                    });

                    $('#switchboard-quick-results').html(html);

                    // Click handler
                    $('.quick-result-item').on('click', function() {
                        const moduleKey = $(this).data('module');
                        const isActive = $(this).data('active');
                        const hasSettings = $(this).data('has-settings');
                        const hasAdminPage = $(this).data('has-admin-page');
                        const adminPageUrl = $(this).data('admin-page-url');

                        handleModuleClick(moduleKey, isActive, hasSettings, hasAdminPage, adminPageUrl);
                    });

                    // Hover effect
                    $('.quick-result-item').on('mouseenter', function() {
                        $(this).css('background', '#f9fafb');
                    }).on('mouseleave', function() {
                        if (!$(this).hasClass('selected')) {
                            $(this).css('background', 'white');
                        }
                    });
                }

                function updateSelection() {
                    $('.quick-result-item').removeClass('selected').css('background', 'white');

                    if (selectedIndex >= 0) {
                        const $selected = $('.quick-result-item').eq(selectedIndex);
                        $selected.addClass('selected').css('background', '#eff6ff');

                        // Scroll into view
                        const container = $('#switchboard-quick-results');
                        const item = $selected[0];
                        if (item) {
                            const containerTop = container.scrollTop();
                            const containerBottom = containerTop + container.height();
                            const itemTop = item.offsetTop;
                            const itemBottom = itemTop + item.offsetHeight;

                            if (itemTop < containerTop) {
                                container.scrollTop(itemTop);
                            } else if (itemBottom > containerBottom) {
                                container.scrollTop(itemBottom - container.height());
                            }
                        }
                    }
                }

                function handleModuleClick(moduleKey, isActive, hasSettings, hasAdminPage, adminPageUrl) {
                    closeQuickLauncher();

                    // If module is not active, go to modules page to activate
                    if (!isActive) {
                    const url = new URL('<?php echo esc_url(admin_url('admin.php?page=switchboard')); ?>');
                        url.searchParams.set('highlight', moduleKey);
                        window.location.href = url.toString();
                        return;
                    }

                    // If module is active and has a separate admin page, go to that page
                    if (isActive && hasAdminPage && adminPageUrl) {
                        window.location.href = adminPageUrl;
                        return;
                    }

                    // If module is active and has settings (but no separate page), open settings panel
                    if (isActive && hasSettings) {
                        const url = new URL('<?php echo esc_url(admin_url('admin.php?page=switchboard')); ?>');
                        url.searchParams.set('open_settings', moduleKey);
                        window.location.href = url.toString();
                        return;
                    }

                    // Fallback: just go to Switchboard page
                    window.location.href = '<?php echo esc_url(admin_url('admin.php?page=switchboard')); ?>';
                }
            });
        </script>

        <!-- Toast Notification - Top Right -->
        <div id="sb-toast-container" style="position: fixed; top: 20px; right: 20px; z-index: 100001; pointer-events: none;">
            <div id="sb-toast" style="display: none; pointer-events: auto; border-radius: 8px; padding: 12px 16px; box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15); font-size: 13px; font-weight: 500; transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); max-width: 240px;">
                <div style="display: flex; align-items: center; gap: 8px;">
                    <span id="sb-toast-icon" style="font-size: 16px; line-height: 1;"></span>
                    <span id="sb-toast-message" style="flex: 1;"></span>
                </div>
            </div>
        </div>

        <!-- Feedback Popover - Compact -->
        <div id="sb-feedback-popover" style="display: none; position: fixed; z-index: 100002; background: hsl(0, 0%, 100%); border: 1px solid hsl(0, 0%, 90%); border-radius: 10px; box-shadow: 0 10px 40px -10px rgba(0, 0, 0, 0.15), 0 0 1px rgba(0, 0, 0, 0.1); width: 320px; overflow: hidden;">
            <!-- Header with Background -->
            <div style="display: flex; align-items: center; justify-content: space-between; padding: 14px 16px; background: hsl(0, 0%, 98%); border-bottom: 1px solid hsl(0, 0%, 93%);">
                <div style="font-size: 13px; font-weight: 500; color: hsl(0, 0%, 9%);">
                    Why disable <span id="sb-feedback-module-name" style="font-weight: 600; color: hsl(0, 0%, 20%);"></span>? <span style="color: hsl(0, 100%, 45%);">*</span>
                </div>
                <button id="sb-feedback-cancel" style="background: transparent; border: none; color: hsl(0, 0%, 64%); cursor: pointer; padding: 4px; line-height: 1; font-size: 20px;">
                    &times;
                </button>
            </div>

            <!-- Reasons - Tag-style with Colors (4 clear options) -->
            <div id="sb-feedback-reasons" style="display: flex; flex-wrap: wrap; gap: 6px; padding: 16px;">
                <button class="sb-feedback-reason" data-reason="done" style="background: hsl(142, 76%, 96%); border: 1px solid hsl(142, 76%, 85%); color: hsl(142, 70%, 25%); padding: 6px 10px; border-radius: 6px; font-size: 11px; font-weight: 500; cursor: pointer; transition: all 0.15s; display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;">
                    <span>Done with it</span>
                </button>
                <button class="sb-feedback-reason" data-reason="testing" style="background: hsl(45, 93%, 95%); border: 1px solid hsl(45, 93%, 85%); color: hsl(25, 75%, 30%); padding: 6px 10px; border-radius: 6px; font-size: 11px; font-weight: 500; cursor: pointer; transition: all 0.15s; display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;">
                    <span>Just testing</span>
                </button>
                <button class="sb-feedback-reason" data-reason="bug" data-needs-details="true" style="background: hsl(0, 93%, 96%); border: 1px solid hsl(0, 93%, 88%); color: hsl(0, 70%, 35%); padding: 6px 10px; border-radius: 6px; font-size: 11px; font-weight: 500; cursor: pointer; transition: all 0.15s; display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;">
                    <span>Bug/Error</span>
                </button>
                <button class="sb-feedback-reason" data-reason="missing-feature" data-needs-details="true" style="background: hsl(262, 90%, 96%); border: 1px solid hsl(262, 90%, 87%); color: hsl(262, 80%, 35%); padding: 6px 10px; border-radius: 6px; font-size: 11px; font-weight: 500; cursor: pointer; transition: all 0.15s; display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;">
                    <span>Need feature</span>
                </button>
                <button class="sb-feedback-reason" data-reason="other" data-needs-details="true" style="background: hsl(0, 0%, 97%); border: 1px solid hsl(0, 0%, 89%); color: hsl(0, 0%, 30%); padding: 6px 10px; border-radius: 6px; font-size: 11px; font-weight: 500; cursor: pointer; transition: all 0.15s; display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;">
                    <span>Other</span>
                </button>
            </div>

            <!-- Optional Details Input (shown for bug, missing-feature, other) -->
            <div id="sb-feedback-details" style="display: none; padding: 0 16px 16px; border-top: 1px solid hsl(0, 0%, 93%);">
                <label for="sb-feedback-details-input" style="display: block; font-size: 11px; font-weight: 600; color: hsl(0, 0%, 40%); margin-bottom: 6px; margin-top: 12px;">
                    Tell us more (optional):
                </label>
                <textarea
                    id="sb-feedback-details-input"
                    placeholder="Help us understand what happened..."
                    style="width: 100%; min-height: 60px; padding: 8px; font-size: 12px; border: 1px solid hsl(0, 0%, 85%); border-radius: 6px; resize: vertical; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;"
                    maxlength="500"
                ></textarea>
                <div style="display: flex; justify-content: space-between; align-items: center; margin-top: 8px;">
                    <span style="font-size: 10px; color: hsl(0, 0%, 60%);">
                        <span id="sb-details-char-count">0</span>/500
                    </span>
                    <button id="sb-feedback-submit" style="background: hsl(217, 91%, 60%); border: none; color: white; padding: 6px 14px; border-radius: 6px; font-size: 11px; font-weight: 600; cursor: pointer; transition: background 0.15s;">
                        Disable Module
                    </button>
                </div>
            </div>
        </div>

        <!-- Helper Link beside Toggle (simple text link) -->
        <div id="sb-helper-link-badge" style="display: none; position: fixed; z-index: 100003; pointer-events: auto;">
            <a id="sb-dynamic-link" href="#" target="_blank" style="color: hsl(217, 91%, 50%); text-decoration: none; font-size: 11px; font-weight: 500; display: inline-flex; align-items: center; gap: 4px;">
                <span id="sb-link-text">Contact support</span>
            </a>
        </div>

<?php
    }
}

// Conflict prevention and activation hooks are handled at the top of this file
// (see switchboard_prevent_conflict_activation and switchboard_modify_plugin_action_links)


// ============================================================================
// MANUAL "CHECK FOR UPDATES" LINK → Updates page
// ============================================================================

// Add action links in the plugin row for Switchboard Pro
add_filter('plugin_action_links_' . plugin_basename(__FILE__), function ($links) {
    if (! current_user_can('manage_options')) {
        return $links;
    }

    // Settings link
    $settings_url = admin_url('admin.php?page=switchboard');
    $links['settings'] = '<a href="' . esc_url($settings_url) . '">' . esc_html__('Settings', 'wpswitchboard') . '</a>';

    // Check for updates link
    $url = wp_nonce_url(
        add_query_arg('switchboard_force_update', '1', self_admin_url('update-core.php')),
        'switchboard_force_update'
    );
    $links['switchboard_force_update'] = '<a href="' . esc_url($url) . '">' . esc_html__('Check for updates', 'wpswitchboard') . '</a>';

    // Support link (mailto)
    $support_email = 'support@wpswitchboard.com';
    $links['support'] = '<a href="mailto:' . esc_attr($support_email) . '">' . esc_html__('Support', 'wpswitchboard') . '</a>';

    // Docs link with UTM tags
    $docs_url = add_query_arg(
        array(
            'utm_source' => 'wp-admin',
            'utm_medium' => 'plugin-list',
            'utm_campaign' => 'plugin-links'
        ),
        'https://docs.wpswitchboard.com'
    );
    $links['docs'] = '<a href="' . esc_url($docs_url) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('Docs', 'wpswitchboard') . '</a>';

    return $links;
});

// Handle the forced update when arriving on the Updates page
add_action('admin_init', function () {
    if (! current_user_can('manage_options')) {
        return;
    }

    if (empty($_GET['switchboard_force_update'])) {
        return;
    }

    if (! wp_verify_nonce(isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '', 'switchboard_force_update')) {
        return;
    }

    // 1) Clear WordPress' plugin update cache
    delete_site_transient('update_plugins');

    // 2) Clear SureCart version cache for Switchboard Pro
    if (function_exists('switchboard_get_surecart_config')) {
        $config = switchboard_get_surecart_config();

        if (! class_exists('\WP_Switchboard\Licensing\Client')) {
            $client_file = plugin_dir_path(__FILE__) . 'pro/licensing/src/Client.php';
            if (file_exists($client_file)) {
                require_once $client_file;
            }
        }

        if (class_exists('\WP_Switchboard\Licensing\Client')) {
            $client = new \WP_Switchboard\Licensing\Client(
                $config['plugin_name'],
                $config['public_token'],
                __FILE__
            );
            $client->settings()->set_option_key('wpswitchboard_license_options');

            $cache_key = 'surecart_' . md5($client->slug) . '_version_info';
            delete_transient($cache_key);
        }
    }

    // 3) Ask WordPress to immediately refresh plugin updates
    if (function_exists('wp_update_plugins')) {
        wp_update_plugins();
    }

    // Show a notice on the Updates page
    add_action('admin_notices', function () {
        echo '<div class="notice notice-success is-dismissible"><p><strong>Switchboard:</strong> Update check completed. If a new version is available, it should now be visible below.</p></div>';
    });
});

// ============================================================================
// INITIALIZE PLUGIN
// ============================================================================

// Initialize the plugin
new Switchboard_Pro();

} // End class_exists check
