<?php
/**
 * Name: Admin Menu Separator
 * Description: Add visual separators to admin menu sidebar to organize and group related menu items for better navigation and improved visual hierarchy in dashboard
 * Category: Admin
 * Icon: -
 * UseCases: [
 *   {
 *     "title": "Visual Menu Organization",
 *     "description": "Add visual separators to divide admin menu into sections like 'Content Management', 'E-Commerce', and 'Site Settings' for better navigation."
 *   },
 *   {
 *     "title": "Training New Team Members",
 *     "description": "Separate beginner-friendly tools from advanced settings with labeled dividers to help new employees learn the dashboard faster."
 *   },
 *   {
 *     "title": "Large Plugin Installations",
 *     "description": "Break up long menu lists on sites with 30+ menu items by grouping related functionality with clear visual boundaries."
 *   }
 * ]
 * Added: 2025-08-25
 * Tags: admin, menu, separator, organization, ui
 * WhereToFind: {
 *   "location": "Admin Sidebar Menu",
 *   "url": "",
 *   "instructions": "Configure separator positions in module settings. Visual dividers appear in admin sidebar to organize menu sections.",
 *   "newTab": false
 * }
 */

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

class SwitchboardAdminMenuSeparator {

    public function __construct() {
        // Hook into admin_head which fires AFTER admin_menu is complete and menu is fully processed,
        // but BEFORE menu-header.php renders the menu HTML.
        // This ensures we run after Admin Menu Organizer (and all other plugins) have finished
        // modifying the menu, and after WordPress has processed/finalized the menu structure.
        add_action('admin_head', [$this, 'add_separators'], 1);

        add_action('admin_head', [$this, 'add_separator_styles'], 10);

        // Register with data registry
        $this->register_with_data_registry();
    }
    
    private function register_with_data_registry() {
        if (class_exists('Switchboard_Data_Registry')) {
            $registry = Switchboard_Data_Registry::get_instance();
            $registry->auto_register($this, __FILE__);
        }
    }
    
    public function register_data_locations() {
        return [
            'options' => [
                'switchboard_admin-menu-separator_settings'
            ],
            'option_patterns' => [],
            'tables' => [],
            'post_meta' => [],
            'user_meta' => [],
            'term_meta' => [],
            'transients' => []
        ];
    }
    
    /**
     * Get all admin menu items dynamically
     *
     * @return array Associative array of menu slug => menu title
     */
    private function get_all_menu_items() {
        global $menu, $_wp_menu_nopriv, $_wp_submenu_nopriv;

        // If menu isn't loaded yet (e.g., during AJAX), we need to build it
        if (!is_array($menu) || empty($menu)) {
            // Initialize required globals
            if (!isset($_wp_menu_nopriv)) {
                $_wp_menu_nopriv = [];
            }
            if (!isset($_wp_submenu_nopriv)) {
                $_wp_submenu_nopriv = [];
            }

            // Load WordPress core admin menu structure
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
            require_once ABSPATH . 'wp-admin/includes/menu.php';

            // This loads the core WordPress menu items (Dashboard, Posts, Media, etc.)
            require ABSPATH . 'wp-admin/menu.php';

            // Now trigger the admin_menu action for plugins to add their items
            do_action('admin_menu', ''); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Core WordPress action
        }

        $menu_items = [];

        if (!is_array($menu)) {
            return $menu_items;
        }

        foreach ($menu as $item) {
            // Skip separators and empty items
            if (empty($item[0]) || empty($item[2])) {
                continue;
            }

            // Skip if it's already a separator
            if (strpos($item[4] ?? '', 'wp-menu-separator') !== false) {
                continue;
            }

            $slug = $item[2];
            // Strip HTML tags from menu title (some plugins add icons/badges)
            $title = wp_strip_all_tags($item[0]);

            // Skip if title is empty after stripping
            if (empty(trim($title))) {
                continue;
            }

            $menu_items[$slug] = $title;
        }

        return $menu_items;
    }

    /**
     * Render settings panel
     *
     * @param array $settings Current module settings
     * @return void Outputs HTML directly
     */
    public function render_settings($settings = []) {
        $positions = isset($settings['positions']) ? $settings['positions'] : '';
        $selected_positions = !empty($positions) ? array_map('trim', explode(',', $positions)) : [];

        // Get all menu items dynamically
        $all_menu_items = $this->get_all_menu_items();

        // Build available positions from actual menu items
        // Interleave After/Before for each menu item for better UX
        $available_positions = [];

        foreach ($all_menu_items as $slug => $title) {
            $after_key = 'after_' . sanitize_key($slug);
            $before_key = 'before_' . sanitize_key($slug);

            // Add After option
            $available_positions[$after_key] = sprintf(
                /* translators: %s: menu item name */
                esc_html__('After %s', 'wpswitchboard'),
                $title
            );

            // Add Before option right after
            $available_positions[$before_key] = sprintf(
                /* translators: %s: menu item name */
                esc_html__('Before %s', 'wpswitchboard'),
                $title
            );
        }
        ?>
        <input type="hidden" name="module" value="admin-menu-separator">
        <input type="hidden" name="setting_positions" id="setting_positions" value="<?php echo esc_attr($positions); ?>">

        <div class="sb-sp-group">
            <h3 class="sb-sp-heading"><?php echo esc_html__('Visual Menu Separators', 'wpswitchboard'); ?></h3>
            <p class="sb-sp-description"><?php echo esc_html__('Add beautiful gradient separators to organize your WordPress admin menu. Click the options below to add or remove separators.', 'wpswitchboard'); ?></p>
        </div>

        <div class="sb-sp-group">
            <label class="sb-sp-label"><?php echo esc_html__('Select Separator Positions', 'wpswitchboard'); ?></label>
        <div class="switchboard-tag-selector">
            <div class="switchboard-selected-tags" id="selected-positions">
                <?php foreach ($selected_positions as $pos_key):
                    if (isset($available_positions[$pos_key])): ?>
                        <span class="switchboard-tag" data-value="<?php echo esc_attr($pos_key); ?>">
                            <?php echo esc_html($available_positions[$pos_key]); ?> <span class="remove-tag">×</span>
                        </span>
                <?php endif; endforeach; ?>
            </div>
            <div class="switchboard-available-tags">
                <?php foreach ($available_positions as $pos_key => $pos_label):
                    $is_selected = in_array($pos_key, $selected_positions) ? 'selected' : ''; ?>
                    <span class="switchboard-tag-option <?php echo esc_attr($is_selected); ?>" data-value="<?php echo esc_attr($pos_key); ?>">
                        <?php echo esc_html($pos_label); ?>
                    </span>
                <?php endforeach; ?>
                </div>
            </div>
        </div>

        <script>
        jQuery(document).ready(function($) {
            // Tag selector functionality
            $(".switchboard-tag-option").on("click", function() {
                const value = $(this).data("value");
                const label = $(this).text();

                if ($(this).hasClass("selected")) {
                    // Remove tag
                    $(this).removeClass("selected");
                    $("#selected-positions .switchboard-tag[data-value=\"" + value + "\"]").remove();
                } else {
                    // Add tag
                    $(this).addClass("selected");
                    $("#selected-positions").append(
                        "<span class=\"switchboard-tag\" data-value=\"" + value + "\">" +
                        label + " <span class=\"remove-tag\">×</span></span>"
                    );
                }

                updateHiddenField();
            });

            $(document).on("click", ".remove-tag", function() {
                const value = $(this).parent().data("value");
                $(this).parent().remove();
                $(".switchboard-tag-option[data-value=\"" + value + "\"]").removeClass("selected");
                updateHiddenField();
            });

            function updateHiddenField() {
                const values = [];
                $("#selected-positions .switchboard-tag").each(function() {
                    values.push($(this).data("value"));
                });
                $("#setting_positions").val(values.join(","));
            }
        });
        </script>
        <?php
    }

    public function add_separators() {
        global $menu;

        if (!is_array($menu)) {
            return;
        }

        // Get settings - no separators if not configured
        $settings = get_option('switchboard_admin-menu-separator_settings', []);
        $positions = isset($settings['positions']) ? $settings['positions'] : '';

        // If no positions configured, don't add any separators
        if (empty($positions)) {
            return;
        }

        $positions = array_map('trim', explode(',', $positions));

        // Build a map of sanitized keys to actual menu slugs
        $slug_map = [];
        foreach ($menu as $item) {
            if (!empty($item[2])) {
                $sanitized_key = sanitize_key($item[2]);
                $slug_map[$sanitized_key] = $item[2];
            }
        }

        // Legacy mapping for backward compatibility with old position keys
        $legacy_map = [
            'after_dashboard' => 'index.php',
            'after_posts' => 'edit.php',
            'after_media' => 'upload.php',
            'after_pages' => 'edit.php?post_type=page',
            'after_comments' => 'edit-comments.php',
            'after_appearance' => 'themes.php',
            'after_plugins' => 'plugins.php',
            'after_users' => 'users.php',
            'after_tools' => 'tools.php',
            'before_settings' => 'options-general.php',
        ];

        // Add separators based on settings
        foreach ($positions as $pos_key) {
            // Check legacy map first for backward compatibility
            if (isset($legacy_map[$pos_key])) {
                if (strpos($pos_key, 'before_') === 0) {
                    $this->add_separator_before($legacy_map[$pos_key]);
                } else {
                    $this->add_separator_after($legacy_map[$pos_key]);
                }
                continue;
            }

            // Parse dynamic position key (format: after_{sanitized_slug} or before_{sanitized_slug})
            if (strpos($pos_key, 'after_') === 0) {
                $sanitized_slug = substr($pos_key, 6); // Remove 'after_' prefix

                // Find actual slug from our map
                if (isset($slug_map[$sanitized_slug])) {
                    $this->add_separator_after($slug_map[$sanitized_slug]);
                }
            } elseif (strpos($pos_key, 'before_') === 0) {
                $sanitized_slug = substr($pos_key, 7); // Remove 'before_' prefix

                // Find actual slug from our map
                if (isset($slug_map[$sanitized_slug])) {
                    $this->add_separator_before($slug_map[$sanitized_slug]);
                }
            }
        }
    }

    private function add_separator_after($menu_slug) {
        $position_info = $this->find_menu_position_info($menu_slug);
        if ($position_info !== false) {
            // Insert after this item - use a position between this and the next item
            $this->insert_separator_at($position_info['position'], 'after', $menu_slug);
        }
    }

    private function add_separator_before($menu_slug) {
        $position_info = $this->find_menu_position_info($menu_slug);
        if ($position_info !== false) {
            // Insert before this item - use a position between previous and this item
            $this->insert_separator_at($position_info['position'], 'before', $menu_slug);
        }
    }

    /**
     * Find menu position info including neighboring positions
     */
    private function find_menu_position_info($menu_slug) {
        global $menu;

        if (!is_array($menu)) {
            return false;
        }

        // Get sorted positions
        $positions = array_keys($menu);
        sort($positions, SORT_NUMERIC);

        foreach ($positions as $index => $position) {
            $item = $menu[$position];
            if (isset($item[2]) && $item[2] === $menu_slug) {
                return [
                    'position' => $position,
                    'index' => $index,
                    'prev_position' => $index > 0 ? $positions[$index - 1] : null,
                    'next_position' => isset($positions[$index + 1]) ? $positions[$index + 1] : null
                ];
            }
        }

        return false;
    }

    /**
     * Insert separator at a calculated safe position
     */
    private function insert_separator_at($reference_position, $placement, $menu_slug) {
        global $menu;

        if (!is_array($menu)) {
            return;
        }

        // Get sorted positions
        $positions = array_keys($menu);
        sort($positions, SORT_NUMERIC);

        $ref_pos = floatval($reference_position);

        if ($placement === 'after') {
            // Find the next position after reference
            $next_pos = null;
            foreach ($positions as $pos) {
                if (floatval($pos) > $ref_pos) {
                    $next_pos = floatval($pos);
                    break;
                }
            }
            // Calculate midpoint, or add 0.001 if no next item
            if ($next_pos !== null) {
                $new_position = ($ref_pos + $next_pos) / 2;
            } else {
                $new_position = $ref_pos + 0.001;
            }
        } else {
            // 'before' - find the previous position
            $prev_pos = null;
            $sorted_positions = $positions;
            rsort($sorted_positions, SORT_NUMERIC);
            foreach ($sorted_positions as $pos) {
                if (floatval($pos) < $ref_pos) {
                    $prev_pos = floatval($pos);
                    break;
                }
            }
            // Calculate midpoint, or subtract 0.001 if no previous item
            if ($prev_pos !== null) {
                $new_position = ($prev_pos + $ref_pos) / 2;
            } else {
                $new_position = $ref_pos - 0.001;
            }
        }

        // Create unique separator key based on menu slug and placement for consistency
        $separator_key = 'separator-switchboard-' . $placement . '-' . sanitize_key($menu_slug);

        // Check if this exact separator already exists (by slug, not position)
        foreach ($menu as $item) {
            if (isset($item[2]) && $item[2] === $separator_key) {
                return; // Already exists
            }
        }

        // Use string key with enough precision
        $position_key = number_format($new_position, 6, '.', '');

        // WordPress separator structure
        $separator = [
            '',                                       // Menu title
            'read',                                   // Capability
            $separator_key,                           // Menu slug (unique ID)
            '',                                       // Page title
            'wp-menu-separator switchboard-separator' // CSS class
        ];

        // Insert separator at position
        $menu[$position_key] = $separator;

        // Sort menu by numeric key value
        uksort($menu, function($a, $b) {
            return floatval($a) <=> floatval($b);
        });
    }

    public function add_separator_styles() {
        ?>
        <style>
            /* Enhanced admin menu separator styling */
            #adminmenu .switchboard-separator {
                height: 10px !important;
                margin: 8px 0 !important;
                padding: 0 !important;
                cursor: default !important;
            }

            /* Target the separator div that WordPress creates */
            #adminmenu .switchboard-separator .separator,
            #adminmenu .switchboard-separator div {
                height: 1px !important;
                background: rgba(255, 255, 255, 0.08) !important;
                margin: 4px 12px !important;
            }

            #adminmenu li.switchboard-separator:hover {
                background: transparent !important;
            }

            /* Folded menu */
            body.folded #adminmenu .switchboard-separator {
                margin: 8px 0 !important;
            }
        </style>
        <?php
    }
}

// Initialize and register with data system
$switchboard_instance = new SwitchboardAdminMenuSeparator();
Switchboard_Data_Registry::get_instance()->auto_register($switchboard_instance, __FILE__);
