<?php

namespace ASENHA\Classes;

/**
 * Class for Admin Menu Organizer module
 *
 * @since 6.9.5
 */
class Admin_Menu_Organizer {
    
    /**
     * Make the "Collapse Menu" toggler sticky at the bottom of the admin menu
     * 
     * @since 8.2.3
     */
    public function make_collapse_menu_item_sticky() {
        ?>
        <style>
            #adminmenu #collapse-menu {
                position: sticky;
                bottom: 0;
                background: #1d2327;
                z-index: 100;
                box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.2);
                border-top: 1px solid #3c4349;
            }
            #adminmenuwrap {
                display: flex;
                flex-direction: column;
                height: 100%;
            }
            #adminmenu {
                display: flex;
                flex-direction: column;
                flex: 1;
                margin: 12px 0 0;
            }
            .folded #adminmenu #collapse-menu {
                position: sticky;
                bottom: 0;
                background: #1d2327;
            }
        </style>
        <?php        
    }
    
    /**
     * Add Admin Menu item under Settings menu
     * 
     * @since 7.8.5
     */
    public function add_menu_item() {
        add_submenu_page(
            'options-general.php', // Parent page/menu
            __( 'Admin Menu Settings', 'admin-site-enhancements' ), // Browser tab/window title
            __( 'Admin Menu', 'admin-site-enhancements' ), // Sube menu title
            'manage_options', // Minimal user capabililty
            'admin-menu-organizer', // Page slug. Shows up in URL.
            array( $this, 'add_admin_menu_settings_page' )
        );
    }
    
    /**
     * Create settings page for Admin Menu
     * 
     * @since 7.8.5
     */
    public function add_admin_menu_settings_page() {
        $render_field = new Settings_Fields_Render;
        ?>
        <div class="wrap admin-menu-organizer">
            <h1 class="wp-heading-inline"><?php echo __( 'Admin Menu Organizer', 'admin-site-enhancements' ); ?></h1>
            <div class="admin-menu-organizer-main">
                <div class="admin-menu-sortables-wrapper">
                    <?php $render_field->render_sortable_menu(); ?>
                </div>
                <div class="admin-menu-actions">
                    <button id="amo-save-changes" class="button button-primary button-large"><?php echo __( 'Save Changes', 'admin-site-enhancements' ); ?></button>
                    <div class="asenha-saving-changes" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#2271b1" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" opacity=".25"/><path fill="#2271b1" d="M12,4a8,8,0,0,1,7.89,6.7A1.53,1.53,0,0,0,21.38,12h0a1.5,1.5,0,0,0,1.48-1.75,11,11,0,0,0-21.72,0A1.5,1.5,0,0,0,2.62,12h0a1.53,1.53,0,0,0,1.49-1.3A8,8,0,0,1,12,4Z"><animateTransform attributeName="transform" dur="0.75s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/></path></svg></div>
                    <div class="asenha-changes-saved" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24"><path fill="seagreen" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2zM9.29 16.29L5.7 12.7a.996.996 0 1 1 1.41-1.41L10 14.17l6.88-6.88a.996.996 0 1 1 1.41 1.41l-7.59 7.59a.996.996 0 0 1-1.41 0z"/></svg></div>
                </div>
            </div>
        </div>
        <?php
    }
    
    /**
     * Render custom menu order
     *
     * @param $menu_order array an ordered array of menu items
     * @link https://developer.wordpress.org/reference/hooks/menu_order/
     * @since 2.0.0
     */
    public function render_custom_menu_order( $menu_order ) {

        global $menu;

        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();

        // Get deleted built-in separators
        $deleted_separators = isset( $options['custom_menu_deleted_separators'] ) ? $options['custom_menu_deleted_separators'] : '';
        $deleted_separators_array = array();
        if ( is_string( $deleted_separators ) && ! empty( $deleted_separators ) ) {
            $deleted_separators_array = json_decode( $deleted_separators, true );
            if ( ! is_array( $deleted_separators_array ) ) {
                $deleted_separators_array = array();
            }
        }

        $common_methods = new Common_Methods();

        // Get current menu order. We're not using the default $menu_order which uses index.php, edit.php as array values.
        $current_menu_order = array();

        foreach ( $menu as $menu_key => $menu_info ) {

            if ( false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
                $menu_item_id = $menu_info[2];

                // Skip deleted built-in separators; Pro distinguishes ASE additional-separator rows.
                $skip_deleted_separator = in_array( $menu_item_id, $deleted_separators_array, true );
                if ( bwasenha_fs()->can_use_premium_code__premium_only() ) {
                    $skip_deleted_separator = $common_methods->is_deleted_builtin_separator_menu_row__premium_only( $menu_info, $deleted_separators_array );
                }
                if ( $skip_deleted_separator ) {
                    continue;
                }
            } else {
                $menu_item_id = $menu_info[5];
            }

            $current_menu_order[] = array( $menu_item_id, $menu_info[2] );

        }

        // Get custom menu order
        $custom_menu_order = $options['custom_menu_order']; // comma separated
        $custom_menu_order = explode( ",", $custom_menu_order ); // array of menu ID, e.g. menu-dashboard

        // Return menu order for rendering

        $rendered_menu_order = array();

        // Render menu based on items saved in custom menu order

        foreach ( $custom_menu_order as $custom_menu_item_id ) {

            foreach ( $current_menu_order as $current_menu_item_id => $current_menu_item ) {

                // Standard comparison using menu ID from position [5]
                if ( $custom_menu_item_id == $current_menu_item[0] ) {

                    $rendered_menu_order[] = $current_menu_item[1];

                } 
                // Also check using menu slug from position [2] for custom menus
                // This handles cases where custom-menu-1 needs to match against itself
                elseif ( $custom_menu_item_id == $current_menu_item[1] ) {

                    $rendered_menu_order[] = $current_menu_item[1];

                }

            }

        }

        // Add items from current menu not already part of custom menu order, e.g. new plugin activated and adds new menu item

        foreach ( $current_menu_order as $current_menu_item_id => $current_menu_item ) {

            // Check both menu ID and slug to prevent duplicate entries for custom menus
            if ( ! in_array( $current_menu_item[0], $custom_menu_order ) && ! in_array( $current_menu_item[1], $custom_menu_order ) ) {

                $rendered_menu_order[] = $current_menu_item[1];

            }

        }
        
        if ( bwasenha_fs()->can_use_premium_code__premium_only() ) {
            global $submenu;

            // Rearrange the sequence / positioning of sub-menu items

            // Get custom submenus order
            $custom_submenus_order = ( array_key_exists( 'custom_submenus_order', $options ) ) ? $options['custom_submenus_order'] : ''; // comma separated
            $custom_submenus_order = ( is_array( json_decode( $custom_submenus_order, true ) ) ) ? json_decode( $custom_submenus_order, true ) : array();
            $custom_submenus_order_transformed = array();

            foreach ( $custom_submenus_order as $submenu_id => $submenu_order ) {
                // Transform e.g. edit__php___post_type____page ==> edit.php?post_type=page
                $submenu_id = $common_methods->restore_menu_item_id( $submenu_id );
                $submenu_order = explode( ",", $submenu_order );
                $custom_submenus_order_transformed[$submenu_id] = $submenu_order;
            }
            
            // Change the order of submenu items for $submenu global
            foreach ( $submenu as $submenu_global_id => $submenu_global_items ) {

                // There's an empty $submenu_global_id for items that are 'hidden'
                // if ( empty( $submenu_global_id ) ) {
                //  $submenu_global_id = 'unassigned';
                // }

                $custom_ordered_items = array();

                foreach ( $custom_submenus_order_transformed as $submenu_custom_id => $submenu_custom_order ) {

                    if ( $submenu_global_id == $submenu_custom_id ) {
                        
                        // Assign new index / position for submenu items as saved in $submenu_custom_order

                        foreach ( $submenu_custom_order as $new_index => $item_id ) {
                            // Convert special HTML character into it's entity equivalent, e.g. '&' into '&amp;'
                            $item_id_htmlentities = htmlentities( $item_id );
                            foreach ( $submenu_global_items as $index => $info ) {
                                if ( isset( $info[2] ) ) {
                                    // Most of the time, this is true
                                    if ( $item_id_htmlentities == $info[2] ) {
                                        $custom_ordered_items[$new_index] = $info;
                                    } else {
                                        // Sometimes, $info[2] does not contain HTML entities, so they are teh same as $item_id
                                        if ( $item_id == $info[2] ) {
                                            $custom_ordered_items[$new_index] = $info;
                                        }
                                    }                                    
                                }
                            }
                        }

                        // Here we append submenu items that are newly added and not part of the saved $submenu_custom_order yet
                        
                        $existing_submenu_items_count = count( $submenu_custom_order );
                        $counter = $existing_submenu_items_count;

                        foreach( $submenu_global_items as $index => $info  ) {
                            if ( isset( $info[2] ) ) {
                                $info2 = $info[2];
                                // $info2 = str_replace( '&amp;', '&', $info2 ); // This has caused some submenu items to go missing
                                $info2 = html_entity_decode( $info2 );                          
                                if ( ! in_array( $info2, $submenu_custom_order ) ) {
                                    $custom_ordered_items[$counter] = $info;
                                    $counter++;
                                }                                
                            }
                        }

                        $submenu[$submenu_global_id] = $custom_ordered_items;
                    }
                }
            }
        }

        return $rendered_menu_order;

    }
    
    /**
     * Handle capabilities that certain plugins require to view a menu item but has not properly add to user role(s)
     * 
     * @since 6.9.12
     */
    public function handle_plugins_custom_capabilities__premium_only() {
        if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'contact-form-7/wp-contact-form-7.php' )) {
            // Contact Form 7
            // See: https://plugins.trac.wordpress.org/browser/contact-form-7/tags/5.9.4/includes/contact-form.php#L58
            $user_roles = array(
                'administrator',
                'editor',
                'author',
                'contributor',
                'subscriber'
            );

            $cf7_custom_capabilitiess = array(
                'wpcf7_read_contact_form',
                // 'wpcf7_edit_contact_form',
                // 'wpcf7_delete_contact_form',
                'wpcf7_read_contact_forms', // plural
                // 'wpcf7_edit_contact_forms', // plural
                // 'wpcf7_delete_contact_forms', // plural
            );
            
            foreach ( $user_roles as $role_slug ) {
                $role = get_role( $role_slug );
                foreach( $cf7_custom_capabilitiess as $custom_capability ) {
                    $role->add_cap( $custom_capability, true );             
                }
            }
        }
    }

    /**
     * Apply custom menu item titles
     *
     * @since 2.9.0
     */
    public function apply_custom_menu_item_titles() {

        global $menu;

        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();

        // Get custom menu item titles
        $custom_menu_titles = $options['custom_menu_titles'];
        $custom_menu_titles = explode( ',', $custom_menu_titles );

        foreach ( $menu as $menu_key => $menu_info ) {

            // Determine menu item ID
            if ( false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
                $menu_item_id = $menu_info[2];
            } else {
                // Check if this is a custom menu item (for pro version)
                $is_custom_menu = false;
                if ( bwasenha_fs()->can_use_premium_code__premium_only() ) {
                    $new_items = isset( $options['custom_menu_new_items'] ) ? json_decode( $options['custom_menu_new_items'], true ) : array();
                    if ( is_array( $new_items ) && isset( $new_items[ $menu_info[2] ] ) ) {
                        $is_custom_menu = true;
                    }
                }
                
                // For custom menus, use slug; for regular menus, use CSS ID
                if ( $is_custom_menu ) {
                    $menu_item_id = $menu_info[2]; // Use slug for custom menus
                } else {
                    $menu_item_id = $menu_info[5]; // Use CSS ID for regular menus
                }
            }

            // Get defaul/custom menu item title
            foreach ( $custom_menu_titles as $custom_menu_title ) {

                // At this point, $custom_menu_title value looks like toplevel_page_snippets__Code Snippets

                $custom_menu_title = explode( '__', $custom_menu_title );

                if ( $custom_menu_title[0] == $menu_item_id ) {
                    $menu_item_title = $custom_menu_title[1]; // e.g. Code Snippets
                    break; // stop foreach loop so $menu_item_title is not overwritten in the next iteration
                } else {
                    $menu_item_title = $menu_info[0];
                }

            }

            $menu[$menu_key][0] = $menu_item_title;

        }
    }

    /**
     * Apply custom menu item titles
     *
     * @since 7.9.7
     */
    public function apply_custom_title_for_posts_menu() {
        global $wp_post_types;
        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $admin_menu_options = ( isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array() );
        // $admin_menu_organizer = new ASENHA\Classes\Admin_Menu_Organizer();
    
        // For 'Posts' menu, if the title has been changed, try changing the labels for it everywhere
        $custom_menu_titles = explode( ',', $admin_menu_options['custom_menu_titles'] );
        foreach ( $custom_menu_titles as $custom_menu_title ) {
            if ( false !== strpos( $custom_menu_title, 'menu-posts__' ) ) {
                $custom_menu_title = explode( '__', $custom_menu_title );
                $posts_custom_title = $custom_menu_title[1];
                $posts_default_title = __( 'Posts', 'admin-site-enhancements' );
                if ( is_array( $wp_post_types ) ) {
                    if ( isset( $wp_post_types['post'] ) && property_exists( $wp_post_types['post'], 'label' ) ) {
                        $posts_default_title = $wp_post_types['post']->label;
                    }
                }
                if ( $posts_default_title != $posts_custom_title ) {
                    add_filter( 'post_type_labels_post', [ $this, 'change_post_labels' ] );
                    add_action( 'init', [ $this, 'change_post_object_label' ] );
                    add_action( 'admin_menu', [ $this, 'change_post_menu_label' ], PHP_INT_MAX );
                    add_action( 'admin_bar_menu', [ $this, 'change_wp_admin_bar' ], 80 );
                }
            }
        }
    }
        
    /**
     * Get custom title for 'Posts' menu item
     * 
     * @since 6.9.13
     */
    public function get_posts_custom_title() {
        $post_object = get_post_type_object( 'post' ); // object
        $posts_default_title = '';

        if ( is_object( $post_object ) ) {
            if ( property_exists( $post_object, 'label' ) ) {
                $posts_default_title = $post_object->label;                     
            } else {
                $posts_default_title = $post_object->labels->name;                          
            }            
        }
        
        $posts_custom_title = $posts_default_title;

        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();

        $custom_menu_titles = isset( $options['custom_menu_titles'] ) ? explode( ',', $options['custom_menu_titles'] ) : array();

        if ( ! empty( $custom_menu_titles ) ) {
            foreach ( $custom_menu_titles as $custom_menu_title ) {
                if ( false !== strpos( $custom_menu_title, 'menu-posts__' ) ) {
                    $custom_menu_title = explode( '__', $custom_menu_title );
                    $posts_custom_title = $custom_menu_title[1];
                }
            }            
        }
        
        return $posts_custom_title;
    }
    
    /**
     * For 'Posts', apply custom label
     * 
     * @link https://developer.wordpress.org/reference/hooks/post_type_labels_post_type/
     * @since 6.9.13
     */
    public function change_post_labels( $labels ) {
        $post_object = get_post_type_object( 'post' ); // object
        $posts_default_title_plural = '';
        
        if ( is_object( $post_object ) ) {
            if ( property_exists( $post_object, 'label' ) ) {
                $posts_default_title_plural = $post_object->label;
            } else {
                $posts_default_title_plural = $post_object->labels->name;
            }            

            $posts_default_title_singular = $post_object->labels->singular_name;

            $posts_custom_title = $this->get_posts_custom_title();

            foreach( $labels as $key => $label ){
                if ( null === $label ) {
                    continue;
                }
                $labels->{$key} = str_replace( [ $posts_default_title_plural, $posts_default_title_singular ], $posts_custom_title, $label );
            }
        }

        return $labels;        
    }
    
    /**
     * For 'Posts', apply custom label in post object
     * 
     * @since 6.9.12
     */
    public function change_post_object_label() {
        global $wp_post_types;
        $posts_custom_title = $this->get_posts_custom_title();

        $labels                     = &$wp_post_types['post']->labels;
        $labels->name               = $posts_custom_title;
        $labels->singular_name      = $posts_custom_title;
        $labels->all_items          = sprintf( 
                                        /* translators: %s is post type or taxonomy label */
                                        __( 'All %s', 'admin-site-enhancements' ),
                                        $posts_custom_title
                                    );
        $labels->add_new            = __( 'Add New', 'admin-site-enhancements' );
        $labels->add_new_item       = __( 'Add New', 'admin-site-enhancements' );
        $labels->edit_item          = __( 'Edit', 'admin-site-enhancements' );
        $labels->new_item           = $posts_custom_title;
        $labels->view_item          = __( 'View', 'admin-site-enhancements' );
        $labels->search_items       = sprintf( 
                                        /* translators: %s is post type or taxonomy label */
                                        __( 'Search %s', 'admin-site-enhancements' ),
                                        $posts_custom_title
                                    );
        $labels->not_found          = sprintf( 
                                        /* translators: %s is the post type label */
                                        __( 'No %s found', 'admin-site-enhancements' ),
                                        strtolower( $posts_custom_title )
                                    );
        $labels->not_found_in_trash = sprintf( 
                                        /* translators: %s is the post type label */
                                        __( 'No %s found in Trash', 'admin-site-enhancements' ),
                                        strtolower( $posts_custom_title )
                                    );
    }

    /**
     * For 'Posts', apply custom label in menu and submenu
     * 
     * @since 6.9.12
     */
    public function change_post_menu_label() {
        global $submenu;
        $posts_custom_title = $this->get_posts_custom_title();
        
        if ( ! empty( $posts_custom_title ) ) {
            $submenu['edit.php'][5][0]  = sprintf( 
                /* translators: %s is post type or taxonomy label */
                __( 'All %s', 'admin-site-enhancements' ),
                $posts_custom_title
            );
        } else {
            $submenu['edit.php'][5][0]  = sprintf( 
                /* translators: %s is post type or taxonomy label */
                __( 'All %s', 'admin-site-enhancements' ),
                $posts_default_title
            );
        }        
    }

    /**
     * For 'Posts', apply custom label in admin bar
     * 
     * @since 6.9.12
     */
    public function change_wp_admin_bar( $wp_admin_bar ) {
        $posts_custom_title = $this->get_posts_custom_title();
        
        $new_post_node = $wp_admin_bar->get_node( 'new-post' );
        if ( $new_post_node ) {
            $new_post_node->title = $posts_custom_title;
            $wp_admin_bar->add_node( $new_post_node );
        }
    }

    /**
     * Hide parent menu items by adding class(es) to hide them
     *
     * @since 2.0.0
     */
    public function hide_menu_items() {

        global $menu;

        if ( bwasenha_fs()->can_use_premium_code__premium_only() ) {
            $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
            $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();

            // Get data on parent menu items to be hidden by toggle and/or user roles
            if ( isset( $options['custom_menu_always_hidden'] ) ) {
                $menu_always_hidden = $options['custom_menu_always_hidden'];
                $menu_always_hidden = json_decode( $menu_always_hidden, true );
            } else {
                $menu_always_hidden = array();
            }
        }

        $common_methods = new Common_Methods;
        $menu_hidden_by_toggle = $common_methods->get_menu_hidden_by_toggle(); // indexed array

        foreach ( $menu as $menu_key => $menu_info ) {

            if ( false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
                $menu_item_id = $menu_info[2];
            } else {
                $menu_item_id = $menu_info[5];
            }

            // Append 'hidden' class to hide menu item until toggled
            if ( in_array( $menu_item_id, $menu_hidden_by_toggle ) ) {
                $menu[$menu_key][4] = $menu_info[4] . ' hidden asenha_hidden_menu';
            }

            if ( bwasenha_fs()->can_use_premium_code__premium_only() ) {
                // Maybe append 'always-hidden' class
                if ( is_array( $menu_always_hidden ) && ! empty( $menu_always_hidden ) ) {
                    foreach( $menu_always_hidden as $hidden_menu_item_id => $info ) {
                        $hidden_menu_item_id = $common_methods->restore_menu_item_id( $hidden_menu_item_id );
                        if ( $hidden_menu_item_id == $menu_item_id 
                            // Special case where Yoast SEO is showing Yoast SEO menu only to editors using a different $menu_item_id
                            || ( 'toplevel_page_wpseo_dashboard' == $hidden_menu_item_id && 'toplevel_page_wpseo_workouts' == $menu_item_id  )
                        ) {
                            if ( $common_methods->menu_item_is_hidden_for_user__premium_only( $info ) ) {
                                $menu[$menu_key][4] = $menu[$menu_key][4] . ' always-hidden';
                            }
                        }
                    }                   
                }
            }

        }

    }
    
    /**
     * Hide submenu items by adding class(es) to hide them
     * 
     * @since 6.9.13
     */
    public function hide_submenu_items__premium_only() {
        global $submenu;
        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();
        
        // Get data on submenu items to be hidden by toggle and/or user roles
        if ( isset( $options['custom_submenu_always_hidden'] ) ) {
            $submenu_always_hidden = $options['custom_submenu_always_hidden'];
            $submenu_always_hidden = json_decode( $submenu_always_hidden, true );
        } else {
            $submenu_always_hidden = array();
        }

        $common_methods = new Common_Methods;
        $submenu_hidden_by_toggle = $common_methods->get_submenu_hidden_by_toggle__premium_only(); // indexed array
                
        foreach ( $submenu as $submenu_key => $submenu_infos ) {
            foreach ( $submenu_infos as $submenu_info_key => $submenu_info ) {
                if ( isset( $submenu_info[0] ) ) {
                    // Very rarely, submenu items use special characters / symbols, which breaks the JS. Let's remove it.
                    // We've removed it in ASE settings, so, we need to remove it here from the global $submenu as well, so both can match
                    $submenu_item_title = str_replace( 
                        array( 
                            '↳', 
                            '➤', // e.g. WP fail2ban
                            '⭐️', 
                            '✛',
                            '<img draggable="false" role="img" class="emoji" alt="👋" src="https://s.w.org/images/core/emoji/15.0.3/svg/1f44b.svg">', // WOW-Plugin (Float Menu plugin) sub-menu 'Hey' has this image at the end of the title.
                            '👋', // WOW-Plugin (Float Menu plugin) sub-menu 'Hey' has this image at the end of the title.
                            '→', // Independent Analytics >> Upgrade to Pro submenu item
                            '»', // wpDiscuz
                        ), 
                        '', 
                        $submenu_info[0] 
                    );
                    $sanitized_submenu_title = sanitize_title( $submenu_item_title );
                } else {
                    $sanitized_submenu_title = '';
                }

                $submenu_url_fragment = isset( $submenu_info[2] ) ? $submenu_info[2] : '';
                $submenu_url_fragment_length = strlen( $submenu_url_fragment );

                // We also need to reassemble the item ID, so it matches the one stored via ASE settings
                $submenu_item_id = $submenu_key . '_-_' . $sanitized_submenu_title .'-_-' . $submenu_url_fragment_length;
                // e.g. index.php_-_site-options-_-24

                // Append 'asenha_hidden_menu hidden' classes to hide menu item until toggled
                if ( in_array( $submenu_item_id, $submenu_hidden_by_toggle ) ) {
                    $submenu[$submenu_key][$submenu_info_key][4] = 'asenha_hidden_menu hidden';
                } else {
                    $submenu[$submenu_key][$submenu_info_key][4] = '';
                }

                // Maybe append 'always-hidden' class
                if ( is_array( $submenu_always_hidden ) && ! empty( $submenu_always_hidden ) ) {
                    foreach( $submenu_always_hidden as $hidden_menu_item_id => $info ) {
                        $hidden_menu_item_id = $common_methods->restore_menu_item_id( $hidden_menu_item_id ); 
                        // e.g. index__php_-_site-options ==> index.php_-_site-options                        
                        
                        if ( $hidden_menu_item_id == $submenu_item_id ) {
                            if ( $common_methods->menu_item_is_hidden_for_user__premium_only( $info ) ) {
                                $submenu[$submenu_key][$submenu_info_key][4] = $submenu[$submenu_key][$submenu_info_key][4] . ' always-hidden';
                            }
                        }
                    }                   
                }
            }
        }
    }

    /**
     * Add toggle to show hidden menu items
     *
     * @since 2.0.0
     */
    public function add_hidden_menu_toggle() {
        
        global $current_user;

        // Get menu items hidden by toggle
        $common_methods = new Common_Methods;       
        $menu_hidden_by_toggle = $common_methods->get_menu_hidden_by_toggle();
        if ( bwasenha_fs()->can_use_premium_code__premium_only() ) {
            $submenu_hidden_by_toggle = $common_methods->get_submenu_hidden_by_toggle__premium_only();        
        } else {
            $submenu_hidden_by_toggle = array();
        }

        // Get user capabilities the "Show All/Less" toggle should be shown for
        $user_capabilities_to_show_menu_toggle_for = $common_methods->get_user_capabilities_to_show_menu_toggle_for();

        // Get current user's capabilities from the user's role(s)
        $current_user_capabilities = '';
        $current_user_roles = $current_user->roles; // indexed array of role slugs
        foreach( $current_user_roles as $current_user_role ) {
            $current_user_role_capabilities = get_role( $current_user_role )->capabilities;
            if ( is_array( $current_user_role_capabilities ) ) {
                $current_user_role_capabilities = array_keys( $current_user_role_capabilities ); // indexed array
                $current_user_role_capabilities = implode( ",", $current_user_role_capabilities );
                $current_user_capabilities .= ',' . $current_user_role_capabilities;                
            }
        }

        // Maybe show "Show All/Less" toggle
        $show_toggle_menu = false;

        if ( ! empty( $current_user_capabilities ) ) {
            $current_user_capabilities = array_unique( explode(",", $current_user_capabilities) );

            foreach( $user_capabilities_to_show_menu_toggle_for as $user_capability_to_show_menu_toggle_for ) {
                if ( in_array( $user_capability_to_show_menu_toggle_for, $current_user_capabilities ) ) {
                    $show_toggle_menu = true;
                    break;
                }
            }
        }

        if ( ( ! empty( $menu_hidden_by_toggle ) || ! empty( $submenu_hidden_by_toggle ) ) 
            && $show_toggle_menu ) {

            add_menu_page(
                __( 'Show All', 'admin-site-enhancements' ),
                __( 'Show All', 'admin-site-enhancements' ),
                'read',
                'asenha_show_hidden_menu',
                function () {  return false;  },
                "dashicons-arrow-down-alt2",
                300 // position
            );

            add_menu_page(
                __( 'Show Less', 'admin-site-enhancements' ),
                __( 'Show Less', 'admin-site-enhancements' ),
                'read',
                'asenha_hide_hidden_menu',
                function () {  return false;  },
                "dashicons-arrow-up-alt2",
                301 // position
            );
        }

    }

    /**
     * Script to toggle hidden menu itesm
     *
     * @since 2.0.0
     */
    public function enqueue_toggle_hidden_menu_script() {

        // Get menu items hidden by toggle
        $common_methods = new Common_Methods;
        $menu_hidden_by_toggle = $common_methods->get_menu_hidden_by_toggle();
        if ( bwasenha_fs()->can_use_premium_code__premium_only() ) {
            $submenu_hidden_by_toggle = $common_methods->get_submenu_hidden_by_toggle__premium_only();        
        } else {
            $submenu_hidden_by_toggle = array();
        }

        if ( ! empty( $menu_hidden_by_toggle ) || ! empty( $submenu_hidden_by_toggle ) ) {

            // Script to set behaviour and actions of the sortable menu
            wp_enqueue_script( 'asenha-toggle-hidden-menu', ASENHA_URL . 'assets/js/toggle-hidden-menu.js', array(), ASENHA_VERSION, false );

        }

    }
    
    /**
     * Maybe restrict access to current screen based on 'hide', 'hide always', 'for all/some roles' settings for each menu item
     * 
     * @since 5.1.0
     */
    public function maybe_restrict_access_to_current_screen__premium_only() {
        global $wp, $menu, $submenu;

        // $current_screen = get_current_screen(); // WP_Screen object
        // $current_screen_id = $current_screen->id;
        // $current_screen_parent_file = $current_screen->parent_file;

        $current_user = wp_get_current_user();

        $common_methods = new Common_Methods;
        $always_hidden_menu_url_fragments = $common_methods->get_url_fragments_of_always_hidden_menu_pages__premium_only();

        // Get the page url id for the current admin page
        $current_admin_page_url_fragment = add_query_arg( $wp->query_vars ); // e.g. /wp-admin/admin.php?page=some-admin-page-slug

        if ( '/wp-admin/' == $current_admin_page_url_fragment && ! in_array( 'index.php', $always_hidden_menu_url_fragments ) ) {
            // do nothing, this is the dashboard at /wp-admin/ without index.php and it's not restricted for access
            $page_url_id = $current_admin_page_url_fragment; 
        } elseif ( '/wp-admin/' == $current_admin_page_url_fragment && in_array( 'index.php', $always_hidden_menu_url_fragments ) ) {
            // we're on the dashboard at /wp-admin/ and it (index.php) is restricted for access, so, change url ID to 'index.php'
            $page_url_id = 'index.php';
        } elseif ( false !== strpos( $current_admin_page_url_fragment, '?page=' ) ) {
            $current_admin_page_url_fragments = explode( '?page=', $current_admin_page_url_fragment );
            $page_url_id = $current_admin_page_url_fragments[1]; // e.g. some-admin-page-slug
        } elseif ( false !== strpos( $current_admin_page_url_fragment, '?' ) ) {
            $page_url_id = str_replace( '/wp-admin/', '', $current_admin_page_url_fragment ); // e.g. post-new.php?post_type=todos
        } else {
            $page_url_id = str_replace( '/wp-admin/', '', $current_admin_page_url_fragment ); // e.g. edit-comments.php or plugins.php  
        }

        $restrict_access = false;
        
        if ( in_array( $page_url_id, $always_hidden_menu_url_fragments ) ) {
            
            // Maybe restrict access for (parent) admin page

            // Get data for which roles should the current page be hidden for
            $always_hide_for_raw = $common_methods->get_always_hide_for__premium_only( $page_url_id );

            $hiding_info = isset( $always_hide_for_raw[ $page_url_id ] ) ? $always_hide_for_raw[ $page_url_id ] : array();
            $restrict_access = $common_methods->menu_item_is_hidden_for_user__premium_only( $hiding_info, $current_user );

            if ( $restrict_access ) {
                wp_die( 'Sorry, you are not allowed to access this page.' );        
                die(); // in case wp_die() is not working as it should
            }
            
        } else {
            
            // Maybe restrict access for (child) admin pages whose parent has been restricted

            if ( is_array( $submenu ) ) {               
                foreach( $submenu as $menu_id => $submenu_items ) {
                    if ( in_array( $menu_id, $always_hidden_menu_url_fragments ) ) {
                        foreach( $submenu_items as $submenu_item_priority => $submenu_item_info ) {
                            if ( $page_url_id == $submenu_item_info[2] ) {

                                // Get roles for which parent menu page should be hidden for
                                $always_hide_for_raw = $common_methods->get_always_hide_for__premium_only( $menu_id );

                                $hiding_info = isset( $always_hide_for_raw[ $menu_id ] ) ? $always_hide_for_raw[ $menu_id ] : array();
                                $restrict_access = $common_methods->menu_item_is_hidden_for_user__premium_only( $hiding_info, $current_user );

                                if ( $restrict_access ) {
                                    wp_die( 'Sorry, you are not allowed to access this page.' );
                                    die(); // in case wp_die() is not working as it should
                                }
                                                        
                            }
                        }
                    }
                }
            }
            
        }
                        
    }
    
    /**
     * Register new separators added via the "Add separator" button
     * 
     * @since 6.9.13
     */
    public function add_new_separators__premium_only() {
        global $menu;
        
        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();

        $new_separators = isset( $options['custom_menu_new_separators'] ) ? $options['custom_menu_new_separators'] : '';
        
        if ( is_string( $new_separators ) && ! empty( $new_separators ) ) {
            $new_separators = json_decode( $new_separators, true );

            if ( is_array( $new_separators ) && ! empty( $new_separators ) ) {
                foreach( $new_separators as $separator_menu_item_id => $info ) {
                    $menu[] = array(
                        '',
                        'read',
                        $separator_menu_item_id,
                        '',
                        'wp-menu-separator additional-separator',
                    );
                }
            }            
        }
    }

    /**
     * Save admin menu via AJAX
     * 
     * @since 6.3.1
     */
    public function save_admin_menu() {

        if ( isset( $_REQUEST ) ) {
            if ( check_ajax_referer( 'save-menu-nonce', 'nonce', false ) ) {

                if ( ! current_user_can( 'manage_options' ) ) {
                    wp_send_json_error(
                        array(
                            'message' => __( 'You do not have permission to perform this action.', 'admin-site-enhancements' ),
                        )
                    );
                }

                $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
                $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();
                
                if ( bwasenha_fs()->can_use_premium_code__premium_only() ) {

                    $custom_menu_new_items = isset( $_REQUEST['custom_menu_new_items'] ) ? wp_unslash( $_REQUEST['custom_menu_new_items'] ) : $options['custom_menu_new_items'];
                    $custom_menu_always_hidden = isset( $_REQUEST['custom_menu_always_hidden'] ) ? wp_unslash( $_REQUEST['custom_menu_always_hidden'] ) : $options['custom_menu_always_hidden'];
                    $custom_submenu_always_hidden = isset( $_REQUEST['custom_submenu_always_hidden'] ) ? wp_unslash( $_REQUEST['custom_submenu_always_hidden'] ) : $options['custom_submenu_always_hidden'];
                    $built_in_capability_map_payload = isset( $_REQUEST['built_in_menu_rule_capability_map'] ) ? wp_unslash( $_REQUEST['built_in_menu_rule_capability_map'] ) : '';
                    $built_in_capability_map_signature = isset( $_REQUEST['built_in_menu_rule_capability_signature'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['built_in_menu_rule_capability_signature'] ) ) : '';
                    $custom_menu_new_items_decoded = $this->decode_admin_menu_json_payload__premium_only( $custom_menu_new_items );
                    $built_in_capability_map = $this->get_verified_built_in_menu_rule_capability_map__premium_only(
                        $built_in_capability_map_payload,
                        $built_in_capability_map_signature
                    );

                    if ( false === $built_in_capability_map ) {
                        wp_send_json_error(
                            array(
                                'code'    => 'amo_reload_required',
                                'message' => __( 'The Admin Menu Organizer data is outdated. Please reload the page and try again.', 'admin-site-enhancements' ),
                            ),
                            409
                        );
                    }

                    $menu_rule_capability_map = $this->get_menu_rule_capability_map__premium_only(
                        $custom_menu_new_items_decoded,
                        $built_in_capability_map
                    );
                    $custom_menu_always_hidden_decoded = $this->decode_admin_menu_json_payload__premium_only( $custom_menu_always_hidden );
                    $custom_submenu_always_hidden_decoded = $this->decode_admin_menu_json_payload__premium_only( $custom_submenu_always_hidden );

                    $options['custom_menu_order'] = isset( $_REQUEST['custom_menu_order'] ) ? wp_unslash( $_REQUEST['custom_menu_order'] ) : $options['custom_menu_order'];

                    $options['custom_menu_titles'] = isset( $_REQUEST['custom_menu_titles'] ) ? wp_unslash( $_REQUEST['custom_menu_titles'] ) : $options['custom_menu_titles'];

                    $options['custom_submenus_order'] = isset( $_REQUEST['custom_submenus_order'] ) ? wp_unslash( $_REQUEST['custom_submenus_order'] ) : $options['custom_submenus_order'];

                    $options['custom_menu_always_hidden'] = wp_json_encode(
                        $this->sanitize_menu_visibility_rules__premium_only(
                            $custom_menu_always_hidden_decoded,
                            $menu_rule_capability_map,
                            true
                        )
                    );

                    $options['custom_submenu_always_hidden'] = wp_json_encode(
                        $this->sanitize_menu_visibility_rules__premium_only(
                            $custom_submenu_always_hidden_decoded,
                            $menu_rule_capability_map,
                            false
                        )
                    );

                    $options['custom_menu_new_separators'] = isset( $_REQUEST['custom_menu_new_separators'] ) ? wp_unslash( $_REQUEST['custom_menu_new_separators'] ) : $options['custom_menu_new_separators'];

                    $options['custom_menu_deleted_separators'] = isset( $_REQUEST['custom_menu_deleted_separators'] ) ? wp_unslash( $_REQUEST['custom_menu_deleted_separators'] ) : $options['custom_menu_deleted_separators'];

                    $options['custom_menu_new_items'] = $custom_menu_new_items;

                } else {

                    $options['custom_menu_order'] = isset( $_REQUEST['custom_menu_order'] ) ? wp_unslash( $_REQUEST['custom_menu_order'] ) : $options['custom_menu_order'];

                    $options['custom_menu_titles'] = isset( $_REQUEST['custom_menu_titles'] ) ? wp_unslash( $_REQUEST['custom_menu_titles'] ) : $options['custom_menu_titles'];

                    $options['custom_menu_hidden'] = isset( $_REQUEST['custom_menu_hidden'] ) ? wp_unslash( $_REQUEST['custom_menu_hidden'] ) : $options['custom_menu_hidden'];
                    
                }
                
                $options_extra['admin_menu'] = $options;
                // vi( $options_extra, '', 'save menu' );

                $updated = update_option( ASENHA_SLUG_U . '_extra', $options_extra, true );

                wp_send_json_success(
                    array(
                        'status'  => ( $updated ? 'success' : 'unchanged' ),
                        'updated' => (bool) $updated,
                    )
                );
            }
        }
        
    }

    /**
     * Decode an AMO JSON payload into an array.
     *
     * @since 8.8.0
     *
     * @param mixed $payload JSON payload.
     * @return array
     */
    private function decode_admin_menu_json_payload__premium_only( $payload ) {
        if ( ! is_string( $payload ) || empty( $payload ) ) {
            return array();
        }

        $decoded_payload = json_decode( $payload, true );

        if ( ! is_array( $decoded_payload ) ) {
            return array();
        }

        return $decoded_payload;
    }

    /**
     * Normalize AMO rule capabilities for save-time validation.
     *
     * @since 8.8.0
     *
     * @param string $required_capability Required capability.
     * @return string
     */
    private function normalize_menu_required_capability__premium_only( $required_capability ) {
        $required_capability = is_string( $required_capability ) ? sanitize_text_field( $required_capability ) : '';

        // Posts > Tags maps its menu capability to manage_categories in WordPress.
        if ( 'manage_post_tags' === $required_capability ) {
            $required_capability = 'manage_categories';
        }

        return $required_capability;
    }

    /**
     * Build the submenu rule ID used by AMO's role-based visibility settings.
     *
     * @since 8.8.0
     *
     * @param string $parent_menu_id Parent menu ID.
     * @param string $submenu_title  Submenu title.
     * @param string $submenu_id     Submenu ID.
     * @return string
     */
    private function get_submenu_rule_id__premium_only( $parent_menu_id, $submenu_title, $submenu_id ) {
        $common_methods = new Common_Methods;

        $submenu_title = str_replace(
            array(
                '↳',
                '➤',
                '⭐️',
                '✛',
                '<img draggable="false" role="img" class="emoji" alt="👋" src="https://s.w.org/images/core/emoji/15.0.3/svg/1f44b.svg">',
                '👋',
                '→',
                '»',
            ),
            '',
            $submenu_title
        );

        return $common_methods->transform_menu_item_id( $parent_menu_id ) . '_-_' . sanitize_title( $submenu_title ) . '-_-' . strlen( $submenu_id );
    }

    /**
     * Build a stable submenu capability lookup key.
     *
     * @since 8.8.0
     *
     * @param string $parent_rule_id Parent rule ID.
     * @param string $submenu_id     Submenu ID.
     * @return string
     */
    private function get_submenu_rule_lookup_key__premium_only( $parent_rule_id, $submenu_id ) {
        return sanitize_text_field( $parent_rule_id ) . '::' . sanitize_text_field( $submenu_id );
    }

    /**
     * Extract the parent rule ID from a stored submenu rule ID.
     *
     * @since 8.8.0
     *
     * @param string $rule_id Stored submenu rule ID.
     * @return string
     */
    private function get_parent_rule_id_from_submenu_rule_id__premium_only( $rule_id ) {
        if ( ! is_string( $rule_id ) || false === strpos( $rule_id, '_-_' ) ) {
            return '';
        }

        $rule_id_parts = explode( '_-_', $rule_id, 2 );

        return isset( $rule_id_parts[0] ) ? sanitize_text_field( $rule_id_parts[0] ) : '';
    }

    /**
     * Get the default structure for an AMO capability map.
     *
     * @since 8.8.0
     *
     * @return array
     */
    private function get_menu_rule_capability_map_template__premium_only() {
        return array(
            'menus'                         => array(),
            'submenus'                      => array(),
            'submenus_by_parent_and_original' => array(),
            'custom_submenus_by_id'         => array(),
        );
    }

    /**
     * Get a signed built-in capability payload for the current AMO page render.
     *
     * @since 8.8.0
     *
     * @return array
     */
    public function get_signed_built_in_menu_rule_capability_payload__premium_only() {
        $payload = wp_json_encode( $this->get_built_in_menu_rule_capability_map__premium_only() );

        if ( ! is_string( $payload ) || empty( $payload ) ) {
            return array(
                'payload'   => '',
                'signature' => '',
            );
        }

        $encoded_payload = base64_encode( $payload );

        if ( ! is_string( $encoded_payload ) || empty( $encoded_payload ) ) {
            return array(
                'payload'   => '',
                'signature' => '',
            );
        }

        return array(
            'payload'   => $encoded_payload,
            'signature' => $this->sign_built_in_menu_rule_capability_payload__premium_only( $encoded_payload ),
        );
    }

    /**
     * Sign a built-in AMO capability payload for the current user/site context.
     *
     * @since 8.8.0
     *
     * @param string $payload Encoded built-in capability payload.
     * @return string
     */
    private function sign_built_in_menu_rule_capability_payload__premium_only( $payload ) {
        $payload = is_string( $payload ) ? $payload : '';
        $user_id = get_current_user_id();

        if ( empty( $payload ) || empty( $user_id ) ) {
            return '';
        }

        return hash_hmac(
            'sha256',
            get_current_blog_id() . '|' . $user_id . '|' . $payload,
            wp_salt( 'auth' )
        );
    }

    /**
     * Verify and decode a built-in AMO capability payload from the current save request.
     *
     * @since 8.8.0
     *
     * @param string $payload   Encoded built-in capability payload.
     * @param string $signature Payload signature.
     * @return array|false
     */
    private function get_verified_built_in_menu_rule_capability_map__premium_only( $payload, $signature ) {
        $payload = is_string( $payload ) ? $payload : '';
        $signature = is_string( $signature ) ? sanitize_text_field( $signature ) : '';

        if ( empty( $payload ) || empty( $signature ) ) {
            return false;
        }

        $expected_signature = $this->sign_built_in_menu_rule_capability_payload__premium_only( $payload );

        if ( empty( $expected_signature ) || ! hash_equals( $expected_signature, $signature ) ) {
            return false;
        }

        $decoded_payload = base64_decode( $payload, true );

        if ( ! is_string( $decoded_payload ) || empty( $decoded_payload ) ) {
            return false;
        }

        $payload_map = $this->decode_admin_menu_json_payload__premium_only( $decoded_payload );
        $capability_map = $this->get_menu_rule_capability_map_template__premium_only();

        foreach ( $capability_map as $map_key => $default_values ) {
            if ( isset( $payload_map[ $map_key ] ) && is_array( $payload_map[ $map_key ] ) ) {
                $capability_map[ $map_key ] = $payload_map[ $map_key ];
            }
        }

        if ( empty( $capability_map['menus'] ) ) {
            return false;
        }

        return $capability_map;
    }

    /**
     * Build the built-in portion of the AMO capability map from live menu globals.
     *
     * @since 8.8.0
     *
     * @return array
     */
    private function get_built_in_menu_rule_capability_map__premium_only() {
        global $menu, $submenu;

        $common_methods = new Common_Methods;
        $capability_map = $this->get_menu_rule_capability_map_template__premium_only();

        if ( is_array( $menu ) ) {
            foreach ( $menu as $menu_item ) {
                $menu_item_id = isset( $menu_item[2] ) ? $menu_item[2] : '';

                if ( empty( $menu_item_id ) || 0 === strpos( $menu_item_id, 'custom-menu-' ) ) {
                    continue;
                }

                $menu_rule_capability = array(
                    'required_capability' => $this->normalize_menu_required_capability__premium_only( isset( $menu_item[1] ) ? $menu_item[1] : '' ),
                );

                $capability_map['menus'][ $common_methods->transform_menu_item_id( $menu_item_id ) ] = $menu_rule_capability;

                if ( isset( $menu_item[5] ) && ! empty( $menu_item[5] ) ) {
                    $capability_map['menus'][ $common_methods->transform_menu_item_id( $menu_item[5] ) ] = $menu_rule_capability;
                }
            }
        }

        if ( is_array( $submenu ) ) {
            foreach ( $submenu as $parent_menu_id => $submenu_items ) {
                if ( 0 === strpos( $parent_menu_id, 'custom-menu-' ) ) {
                    continue;
                }

                foreach ( $submenu_items as $submenu_item ) {
                    $submenu_id = isset( $submenu_item[2] ) ? $submenu_item[2] : '';

                    if ( empty( $submenu_id ) || 0 === strpos( $submenu_id, 'custom-submenu-' ) ) {
                        continue;
                    }

                    $parent_rule_id = $common_methods->transform_menu_item_id( $parent_menu_id );
                    $submenu_rule_id = $this->get_submenu_rule_id__premium_only(
                        $parent_menu_id,
                        isset( $submenu_item[0] ) ? $submenu_item[0] : '',
                        $submenu_id
                    );
                    $submenu_rule_capability = array(
                        'required_capability'        => $this->normalize_menu_required_capability__premium_only( isset( $submenu_item[1] ) ? $submenu_item[1] : '' ),
                        'parent_required_capability' => isset( $capability_map['menus'][ $parent_rule_id ]['required_capability'] ) ? $capability_map['menus'][ $parent_rule_id ]['required_capability'] : '',
                        'require_parent_capability'  => false,
                        'parent_rule_id'             => $parent_rule_id,
                        'original_menu_id'           => $submenu_id,
                    );

                    $capability_map['submenus'][ $submenu_rule_id ] = $submenu_rule_capability;
                    $capability_map['submenus_by_parent_and_original'][ $this->get_submenu_rule_lookup_key__premium_only( $parent_rule_id, $submenu_id ) ] = $submenu_rule_capability;
                }
            }
        }

        return $capability_map;
    }

    /**
     * Build a canonical capability map for AMO menu visibility rules.
     *
     * @since 8.8.0
     *
     * @param array $custom_menu_new_items Saved custom menu items.
     * @param array $built_in_capability_map Stored built-in capability map.
     * @return array
     */
    private function get_menu_rule_capability_map__premium_only( $custom_menu_new_items, $built_in_capability_map = array() ) {
        $common_methods = new Common_Methods;
        $capability_map = $this->get_menu_rule_capability_map_template__premium_only();

        if ( empty( $built_in_capability_map ) ) {
            $built_in_capability_map = $this->get_built_in_menu_rule_capability_map__premium_only();
        }

        foreach ( $capability_map as $map_key => $default_values ) {
            if ( isset( $built_in_capability_map[ $map_key ] ) && is_array( $built_in_capability_map[ $map_key ] ) ) {
                $capability_map[ $map_key ] = $built_in_capability_map[ $map_key ];
            }
        }

        if ( is_array( $custom_menu_new_items ) && ! empty( $custom_menu_new_items ) ) {
            foreach ( $custom_menu_new_items as $item_id => $menu_item_info ) {
                if ( ! is_array( $menu_item_info ) ) {
                    continue;
                }

                $parent_id = isset( $menu_item_info['parent_id'] ) ? sanitize_text_field( $menu_item_info['parent_id'] ) : '';

                if ( ! empty( $parent_id ) ) {
                    continue;
                }

                $capability_map['menus'][ $common_methods->transform_menu_item_id( $item_id ) ] = array(
                    'required_capability' => $this->normalize_menu_required_capability__premium_only( isset( $menu_item_info['capability'] ) ? $menu_item_info['capability'] : 'manage_options' ),
                );
            }
        }

        if ( is_array( $custom_menu_new_items ) && ! empty( $custom_menu_new_items ) ) {
            foreach ( $custom_menu_new_items as $item_id => $menu_item_info ) {
                if ( ! is_array( $menu_item_info ) ) {
                    continue;
                }

                $parent_id = isset( $menu_item_info['parent_id'] ) ? sanitize_text_field( $menu_item_info['parent_id'] ) : '';

                if ( empty( $parent_id ) ) {
                    continue;
                }

                $parent_rule_id = $common_methods->transform_menu_item_id( $parent_id );
                $submenu_rule_capability = array(
                    'required_capability'        => $this->normalize_menu_required_capability__premium_only( isset( $menu_item_info['capability'] ) ? $menu_item_info['capability'] : 'manage_options' ),
                    'parent_required_capability' => isset( $capability_map['menus'][ $parent_rule_id ]['required_capability'] ) ? $capability_map['menus'][ $parent_rule_id ]['required_capability'] : '',
                    'require_parent_capability'  => ( 0 === strpos( $parent_id, 'custom-menu-' ) ),
                    'parent_rule_id'             => $parent_rule_id,
                    'original_menu_id'           => $item_id,
                );

                $capability_map['custom_submenus_by_id'][ $item_id ] = $submenu_rule_capability;
                $capability_map['submenus_by_parent_and_original'][ $this->get_submenu_rule_lookup_key__premium_only( $parent_rule_id, $item_id ) ] = $submenu_rule_capability;
            }
        }

        return $capability_map;
    }

    /**
     * Resolve the effective capability context for a stored AMO visibility rule.
     *
     * @since 8.8.0
     *
     * @param string $rule_id         Stored rule ID.
     * @param array  $rule_info       Stored rule data.
     * @param array  $capability_map  Save-time capability map.
     * @param bool   $is_parent_menu  Whether this is a parent-menu rule.
     * @return array
     */
    private function get_menu_rule_capability_context__premium_only( $rule_id, $rule_info, $capability_map, $is_parent_menu ) {
        $common_methods = new Common_Methods;
        $capability_context = array(
            'required_capability'        => '',
            'parent_required_capability' => '',
            'require_parent_capability'  => false,
        );

        if ( $is_parent_menu ) {
            if ( isset( $capability_map['menus'][ $rule_id ] ) ) {
                return $capability_map['menus'][ $rule_id ];
            }

            if ( isset( $rule_info['original_menu_id'] ) ) {
                $original_menu_rule_id = $common_methods->transform_menu_item_id( sanitize_text_field( $rule_info['original_menu_id'] ) );

                if ( isset( $capability_map['menus'][ $original_menu_rule_id ] ) ) {
                    return $capability_map['menus'][ $original_menu_rule_id ];
                }
            }

            return $capability_context;
        }

        if ( isset( $capability_map['submenus'][ $rule_id ] ) ) {
            return $capability_map['submenus'][ $rule_id ];
        }

        if ( isset( $rule_info['original_menu_id'] ) ) {
            $original_menu_id = sanitize_text_field( $rule_info['original_menu_id'] );

            if ( isset( $capability_map['custom_submenus_by_id'][ $original_menu_id ] ) ) {
                return $capability_map['custom_submenus_by_id'][ $original_menu_id ];
            }

            $parent_rule_id = $this->get_parent_rule_id_from_submenu_rule_id__premium_only( $rule_id );

            if ( ! empty( $parent_rule_id ) ) {
                $submenu_lookup_key = $this->get_submenu_rule_lookup_key__premium_only( $parent_rule_id, $original_menu_id );

                if ( isset( $capability_map['submenus_by_parent_and_original'][ $submenu_lookup_key ] ) ) {
                    return $capability_map['submenus_by_parent_and_original'][ $submenu_lookup_key ];
                }
            }
        }

        return $capability_context;
    }

    /**
     * Sanitize per-user exclusions for a menu visibility rule.
     *
     * @since 8.8.0
     *
     * @param array $always_show_for_users Submitted user IDs.
     * @param array $capability_context    Effective capability context.
     * @return array
     */
    private function sanitize_always_show_for_users__premium_only( $always_show_for_users, $capability_context ) {
        $common_methods = new Common_Methods;
        $always_show_for_users = $common_methods->sanitize_user_ids__premium_only( $always_show_for_users );

        if ( empty( $always_show_for_users ) || empty( $capability_context['required_capability'] ) ) {
            return array();
        }

        $eligible_user_ids = array();

        foreach ( $always_show_for_users as $user_id ) {
            $user = get_user_by( 'id', $user_id );

            if ( ! $user ) {
                continue;
            }

            if ( ! user_can( $user, $capability_context['required_capability'] ) ) {
                continue;
            }

            if ( ! empty( $capability_context['require_parent_capability'] ) && ! empty( $capability_context['parent_required_capability'] ) && ! user_can( $user, $capability_context['parent_required_capability'] ) ) {
                continue;
            }

            $eligible_user_ids[] = (int) $user->ID;
        }

        return $eligible_user_ids;
    }

    /**
     * Sanitize AMO visibility rules before saving them.
     *
     * @since 8.8.0
     *
     * @param array $rules           Submitted rules.
     * @param array $capability_map  Save-time capability map.
     * @param bool  $is_parent_menu  Whether these are parent-menu rules.
     * @return array
     */
    private function sanitize_menu_visibility_rules__premium_only( $rules, $capability_map, $is_parent_menu ) {
        if ( ! is_array( $rules ) ) {
            return array();
        }

        $sanitized_rules = array();

        foreach ( $rules as $rule_id => $rule_info ) {
            if ( ! is_array( $rule_info ) ) {
                continue;
            }

            $capability_context = $this->get_menu_rule_capability_context__premium_only( $rule_id, $rule_info, $capability_map, $is_parent_menu );
            $rule_info['required_capability'] = isset( $capability_context['required_capability'] ) ? $capability_context['required_capability'] : '';
            $rule_info['always_show_for_users'] = $this->sanitize_always_show_for_users__premium_only(
                isset( $rule_info['always_show_for_users'] ) ? $rule_info['always_show_for_users'] : array(),
                $capability_context
            );

            $sanitized_rules[ $rule_id ] = $rule_info;
        }

        return $sanitized_rules;
    }

    /**
     * Search users eligible for AMO per-user exclusions.
     *
     * @since 8.8.0
     */
    public function search_users_for_admin_menu_visibility__premium_only() {
        if ( ! isset( $_REQUEST['nonce'] ) || ! check_ajax_referer( 'search-users-for-admin-menu-visibility', 'nonce', false ) ) {
            wp_send_json_error(
                array(
                    'message' => __( 'Security check failed.', 'admin-site-enhancements' ),
                )
            );
        }

        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error(
                array(
                    'message' => __( 'You do not have permission to perform this action.', 'admin-site-enhancements' ),
                )
            );
        }

        $required_capability = isset( $_REQUEST['required_capability'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['required_capability'] ) ) : 'read';
        $search = isset( $_REQUEST['search'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['search'] ) ) : '';

        $users = $this->get_users_for_admin_menu_visibility__premium_only( $required_capability, $search );
        $common_methods = new Common_Methods;
        $results = array();

        foreach ( $users as $user ) {
            $results[] = array(
                'id'   => (string) $user->ID,
                'text' => $common_methods->format_user_label_for_menu_visibility__premium_only( $user ),
            );
        }

        wp_send_json_success(
            array(
                'results' => $results,
            )
        );
    }

    /**
     * Get users that can access a menu item's required capability.
     *
     * @since 8.8.0
     *
     * @param string $required_capability Required capability.
     * @param string $search              Search term.
     * @return array
     */
    private function get_users_for_admin_menu_visibility__premium_only( $required_capability, $search = '' ) {
        $user_query_args = array(
            'number'  => 100,
            'orderby' => 'display_name',
            'order'   => 'ASC',
        );

        if ( ! empty( $search ) ) {
            $user_query_args['search'] = '*' . $search . '*';
            $user_query_args['search_columns'] = array(
                'display_name',
                'user_login',
                'user_email',
            );
        }

        $users = get_users( $user_query_args );
        $eligible_users = array();

        foreach ( $users as $user ) {
            if ( user_can( $user, $required_capability ) ) {
                $eligible_users[] = $user;
            }
        }

        return $eligible_users;
    }

    /**
     * Get hiding info for a saved custom menu item.
     *
     * @since 8.8.0
     *
     * @param string $current_page          Current custom menu page.
     * @param array  $new_items             Saved custom menu items.
     * @param array  $menu_always_hidden    Parent menu hiding rules.
     * @param array  $submenu_always_hidden Submenu hiding rules.
     * @return array|null
     */
    private function get_custom_menu_hiding_info__premium_only( $current_page, $new_items, $menu_always_hidden, $submenu_always_hidden ) {
        $common_methods = new Common_Methods;
        $parent_id = isset( $new_items[ $current_page ]['parent_id'] ) ? $new_items[ $current_page ]['parent_id'] : '';
        $is_submenu = ! empty( $parent_id );

        if ( $is_submenu ) {
            if ( is_array( $submenu_always_hidden ) && ! empty( $submenu_always_hidden ) ) {
                foreach ( $submenu_always_hidden as $submenu_id => $info ) {
                    $submenu_id_restored = $common_methods->restore_menu_item_id( $submenu_id );

                    if ( $submenu_id_restored === $current_page ) {
                        return $info;
                    }

                    if ( isset( $info['original_menu_id'] ) && $info['original_menu_id'] === $current_page ) {
                        return $info;
                    }
                }
            }

            if ( strpos( $parent_id, 'custom-menu-' ) === 0 && is_array( $menu_always_hidden ) && ! empty( $menu_always_hidden ) ) {
                foreach ( $menu_always_hidden as $menu_id => $info ) {
                    $menu_id_restored = $common_methods->restore_menu_item_id( $menu_id );

                    if ( $menu_id_restored === $parent_id ) {
                        return $info;
                    }
                }
            }

            return null;
        }

        if ( is_array( $menu_always_hidden ) && ! empty( $menu_always_hidden ) ) {
            foreach ( $menu_always_hidden as $menu_id => $info ) {
                $menu_id_restored = $common_methods->restore_menu_item_id( $menu_id );

                if ( $menu_id_restored === $current_page ) {
                    return $info;
                }
            }
        }

        return null;
    }
    
    /**
     * Reset admin menu via AJAX
     * 
     * @since 6.3.1
     */
    public function reset_admin_menu__premium_only() {

        if ( isset( $_REQUEST ) ) {
            if ( check_ajax_referer( 'reset-menu-nonce', 'nonce', false ) ) {

                $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
                $admin_menu = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();
                // vi( $admin_menu, '', 'menu before reset' );
                
                if ( ! empty( $admin_menu ) ) {
                    unset( $admin_menu['custom_menu_order'] );
                    unset( $admin_menu['custom_menu_titles'] );
                    unset( $admin_menu['custom_submenus_order'] );
                    unset( $admin_menu['custom_menu_hidden'] );
                    unset( $admin_menu['custom_menu_always_hidden'] );
                    unset( $admin_menu['custom_submenu_always_hidden'] );
                    unset( $admin_menu['custom_menu_new_separators'] );
                    unset( $admin_menu['custom_menu_deleted_separators'] );
                    unset( $admin_menu['custom_menu_new_items'] );
                    // vi( $admin_menu, '', 'menu after reset' );
                    
                    $options_extra['admin_menu'] = $admin_menu;
                    // vi( $options_extra, '', 'reset menu' );

                    $updated = update_option( ASENHA_SLUG_U . '_extra', $options_extra, true );
                    // vi( $updated );
                    
                    if ( $updated ) {
                        $response = array(
                            'status'    => 'success',
                        );
                    } else {
                        $response = array(
                            'status'    => 'failed',
                        );                  
                    }                    
                } else {
                    $response = array(
                        'status'    => 'success',
                    );
                }
                
                echo json_encode( $response );
            }
        }
        
    }
    
    /**
     * Get all registered WordPress capabilities with their associated roles
     * 
     * @since 7.10.0
     */
    public function get_all_capabilities__premium_only() {
        global $wp_roles;
        
        $capabilities_with_roles = array();
        
        if ( isset( $wp_roles->roles ) && is_array( $wp_roles->roles ) ) {
            foreach ( $wp_roles->roles as $role_slug => $role_data ) {
                if ( isset( $role_data['capabilities'] ) && is_array( $role_data['capabilities'] ) ) {
                    foreach ( $role_data['capabilities'] as $cap => $has_cap ) {
                        // Only include if the role actually has this capability (value is true)
                        if ( $has_cap ) {
                            if ( ! isset( $capabilities_with_roles[$cap] ) ) {
                                $capabilities_with_roles[$cap] = array();
                            }
                            $capabilities_with_roles[$cap][] = $role_slug;
                        }
                    }
                }
            }
        }
        
        // Filter out deprecated level_0 to level_10 capabilities (deprecated since WordPress 3.0)
        $capabilities_with_roles = array_filter( $capabilities_with_roles, function( $capability ) {
            return ! preg_match( '/^level_(0|[1-9]|10)$/', $capability );
        }, ARRAY_FILTER_USE_KEY );
        
        // Sort by capability name for consistent display
        ksort( $capabilities_with_roles );
        
        return $capabilities_with_roles;
    }

    /**
     * Get list of WordPress core capabilities
     * 
     * @since 7.10.0
     */
    public function get_wordpress_core_capabilities__premium_only() {
        return array(
            // Administrator
            'activate_plugins',
            'create_users',
            'delete_others_pages',
            'delete_others_posts',
            'delete_pages',
            'delete_posts',
            'delete_private_pages',
            'delete_private_posts',
            'delete_published_pages',
            'delete_published_posts',
            'delete_users',
            'delete_plugins',
            'delete_themes',
            'edit_dashboard',
            'edit_files',
            'edit_plugins',
            'edit_others_pages',
            'edit_others_posts',
            'edit_pages',
            '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',
            // Multisite specific
            'manage_network',
            'manage_sites',
            'manage_network_users',
            'manage_network_plugins',
            'manage_network_themes',
            'manage_network_options',
            'create_sites',
            'delete_sites',
            'upload_plugins',
            'upload_themes',
        );
    }

    /**
     * Register custom menu items added via "Add Menu" button
     * 
     * @since 7.10.0
     */
    public function add_custom_menu_items__premium_only() {
        global $menu, $submenu;
        
        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();

        $new_items = isset( $options['custom_menu_new_items'] ) ? $options['custom_menu_new_items'] : '';
        
        if ( is_string( $new_items ) && ! empty( $new_items ) ) {
            $new_items = json_decode( $new_items, true );

            if ( is_array( $new_items ) && ! empty( $new_items ) ) {
                foreach( $new_items as $item_id => $info ) {
                    $title = isset( $info['title'] ) ? $info['title'] : '';
                    $url = isset( $info['url'] ) ? $info['url'] : '#';
                    $capability = isset( $info['capability'] ) ? $info['capability'] : 'manage_options';
                    $icon = isset( $info['icon'] ) ? $info['icon'] : 'dashicons-admin-generic';
                    $parent_id = isset( $info['parent_id'] ) ? $info['parent_id'] : '';
                    $target_type = isset( $info['target_type'] ) ? $info['target_type'] : 'url';
                    
                    // Handle 'none' target type - no URL, no icon
                    if ( $target_type === 'none' && empty( $parent_id ) ) {
                        $url = '#';
                        $icon = 'dashicons-minus'; // Use a minimal icon
                        
                        // Compute text color if background color is set
                        if ( isset( $info['color_type'] ) && $info['color_type'] === 'background' && isset( $info['color_value'] ) && ! empty( $info['color_value'] ) ) {
                            $common_methods = new Common_Methods;
                            $computed_text_color = $common_methods->is_color_dark( $info['color_value'] ) ? '#ffffff' : '#3c434a';
                            // Store computed text color back to the array for later use in styling
                            $new_items[ $item_id ]['computed_text_color'] = $computed_text_color;
                        }
                    }
                    
                    if ( empty( $parent_id ) ) {
                        // Top-level menu
                        if ( Admin_Menu_Svg_Icon_Mask::is_svg_data_uri( $icon ) ) {
                            Admin_Menu_Svg_Icon_Mask::register_toplevel_menu_slug_svg_icon( $item_id, $icon );
                            $icon = 'dashicons-admin-generic';
                        }

                        add_menu_page(
                            $title,
                            $title,
                            $capability,
                            $item_id,
                            array( $this, 'render_custom_menu_page__premium_only' ),
                            $icon,
                            100
                        );
                    } else {
                        // Submenu item
                        add_submenu_page(
                            $parent_id,
                            $title,
                            $title,
                            $capability,
                            $item_id,
                            array( $this, 'render_custom_menu_page__premium_only' )
                        );
                    }
                }
                
                // Store updated items back to options if any computed values were added
                $options['custom_menu_new_items'] = wp_json_encode( $new_items );
                $options_extra['admin_menu'] = $options;
                update_option( ASENHA_SLUG_U . '_extra', $options_extra );
                
                // Remove duplicate parent menu items from submenus
                // WordPress automatically adds parent as first submenu when add_submenu_page() is called
                foreach( $new_items as $item_id => $info ) {
                    $parent_id = isset( $info['parent_id'] ) ? $info['parent_id'] : '';
                    
                    // If this is a submenu item with a custom parent
                    if ( ! empty( $parent_id ) && strpos( $parent_id, 'custom-menu-' ) === 0 ) {
                        // Check if the parent appears as its own first submenu item
                        if ( isset( $submenu[$parent_id] ) && is_array( $submenu[$parent_id] ) ) {
                            // Check the first submenu item
                            if ( isset( $submenu[$parent_id][0][2] ) && $submenu[$parent_id][0][2] === $parent_id ) {
                                // Remove the duplicate parent submenu item
                                array_shift( $submenu[$parent_id] );
                                // Reindex the array to maintain proper order
                                $submenu[$parent_id] = array_values( $submenu[$parent_id] );
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Hide custom menu items after they've been registered
     * Runs AFTER add_custom_menu_items__premium_only() so custom menus exist in $menu/$submenu arrays
     * 
     * @since 7.10.0
     */
    public function hide_custom_menu_items__premium_only() {
        global $menu, $submenu, $current_user;
        
        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();
        
        // Get data on parent menu items to be hidden by toggle and/or user roles
        if ( isset( $options['custom_menu_always_hidden'] ) ) {
            $menu_always_hidden = $options['custom_menu_always_hidden'];
            $menu_always_hidden = json_decode( $menu_always_hidden, true );
        } else {
            $menu_always_hidden = array();
        }

        // Get data on submenu items to be hidden by toggle and/or user roles
        if ( isset( $options['custom_submenu_always_hidden'] ) ) {
            $submenu_always_hidden = $options['custom_submenu_always_hidden'];
            $submenu_always_hidden = json_decode( $submenu_always_hidden, true );
        } else {
            $submenu_always_hidden = array();
        }

        $common_methods = new Common_Methods;
        $menu_hidden_by_toggle = $common_methods->get_menu_hidden_by_toggle();
        $submenu_hidden_by_toggle = $common_methods->get_submenu_hidden_by_toggle__premium_only();
        
        // Hide custom parent menu items
        foreach ( $menu as $menu_key => $menu_info ) {
            $menu_item_id = isset( $menu_info[2] ) ? $menu_info[2] : '';
            
            // Check if this is actually a custom menu (starts with 'custom-menu-')
            if ( strpos( $menu_item_id, 'custom-menu-' ) === 0 ) {
                // Check if this is a custom menu that should be hidden by toggle
                if ( in_array( $menu_item_id, $menu_hidden_by_toggle ) ) {
                    $menu[$menu_key][4] = $menu[$menu_key][4] . ' hidden asenha_hidden_menu';
                }
                
                // Check if this custom menu should be hidden based on user roles
                if ( is_array( $menu_always_hidden ) && ! empty( $menu_always_hidden ) ) {
                    foreach( $menu_always_hidden as $hidden_menu_item_id => $info ) {
                        $hidden_menu_item_id = $common_methods->restore_menu_item_id( $hidden_menu_item_id );
                        if ( $hidden_menu_item_id == $menu_item_id ) {
                            if ( $common_methods->menu_item_is_hidden_for_user__premium_only( $info, $current_user ) ) {
                                $menu[$menu_key][4] = $menu[$menu_key][4] . ' always-hidden';
                            }
                        }
                    }                   
                }
            }
        }
        
        // Hide custom submenu items
        foreach ( $submenu as $parent_key => $submenu_items ) {
            foreach ( $submenu_items as $submenu_key => $submenu_info ) {
                $submenu_item_id = isset( $submenu_info[2] ) ? $submenu_info[2] : '';
                
                // Check if this is a custom submenu (starts with 'custom-submenu-')
                if ( strpos( $submenu_item_id, 'custom-submenu-' ) === 0 ) {
                    // Construct the combination ID format used for submenu hiding
                    // Format: parent_key_-_sanitized-title_-_url-length
                    $submenu_title = isset( $submenu_info[0] ) ? $submenu_info[0] : '';
                    $sanitized_submenu_title = sanitize_title( $submenu_title );
                    $submenu_url_fragment = isset( $submenu_info[2] ) ? $submenu_info[2] : '';
                    $submenu_url_fragment_length = strlen( $submenu_url_fragment );
                    $submenu_item_id_combination = $parent_key . '_-_' . $sanitized_submenu_title . '-_-' . $submenu_url_fragment_length;
                    
                    // Check if this submenu combination ID is in the hidden list
                    if ( in_array( $submenu_item_id_combination, $submenu_hidden_by_toggle ) ) {
                        if ( isset( $submenu[$parent_key][$submenu_key][4] ) ) {
                            $submenu[$parent_key][$submenu_key][4] = $submenu[$parent_key][$submenu_key][4] . ' hidden asenha_hidden_menu';
                        } else {
                            $submenu[$parent_key][$submenu_key][4] = 'hidden asenha_hidden_menu';
                        }
                    }
                    
                    // Check if this custom submenu should be hidden based on user roles
                    $submenu_has_own_settings = false;
                    $hiding_info = null;
                    
                    // First, check if submenu has its own role-based hiding settings
                    if ( is_array( $submenu_always_hidden ) && ! empty( $submenu_always_hidden ) ) {
                        foreach( $submenu_always_hidden as $hidden_submenu_item_id => $info ) {
                            $hidden_submenu_item_id = $common_methods->restore_menu_item_id( $hidden_submenu_item_id );
                            if ( $hidden_submenu_item_id == $submenu_item_id_combination ) {
                                $submenu_has_own_settings = true;
                                $hiding_info = $info;
                                break;
                            }
                        }
                    }
                    
                    // If submenu doesn't have its own settings, inherit from parent custom menu
                    if ( ! $submenu_has_own_settings && strpos( $parent_key, 'custom-menu-' ) === 0 ) {
                        if ( is_array( $menu_always_hidden ) && ! empty( $menu_always_hidden ) ) {
                            foreach( $menu_always_hidden as $hidden_menu_item_id => $info ) {
                                $hidden_menu_item_id = $common_methods->restore_menu_item_id( $hidden_menu_item_id );
                                if ( $hidden_menu_item_id == $parent_key ) {
                                    $hiding_info = $info;
                                    break;
                                }
                            }
                        }
                    }
                    
                    // Apply hiding based on role settings
                    if ( $hiding_info !== null ) {
                        if ( $common_methods->menu_item_is_hidden_for_user__premium_only( $hiding_info, $current_user ) ) {
                            if ( isset( $submenu[$parent_key][$submenu_key][4] ) ) {
                                $submenu[$parent_key][$submenu_key][4] = $submenu[$parent_key][$submenu_key][4] . ' always-hidden';
                            } else {
                                $submenu[$parent_key][$submenu_key][4] = 'always-hidden';
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Redirect custom menu items to their target URLs early in admin_init
     * This runs before WordPress commits to rendering a specific admin page
     * 
     * @since 7.10.0
     */
    public function redirect_custom_menu_items__premium_only() {
        // Only run in admin area
        if ( ! is_admin() ) {
            return;
        }
        
        // Check if we have a page parameter
        if ( ! isset( $_GET['page'] ) ) {
            return;
        }
        
        $current_page = sanitize_text_field( $_GET['page'] );
        
        // Check if this is a custom menu item (starts with custom-menu- or custom-submenu-)
        if ( strpos( $current_page, 'custom-menu-' ) !== 0 && strpos( $current_page, 'custom-submenu-' ) !== 0 ) {
            return;
        }
        
        // Get custom menu items from database
        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();
        $new_items = isset( $options['custom_menu_new_items'] ) ? json_decode( $options['custom_menu_new_items'], true ) : array();
        
        // Check if this custom menu item exists and has a URL
        if ( ! isset( $new_items[$current_page] ) || ! isset( $new_items[$current_page]['url'] ) ) {
            return;
        }
        
        // Check if current user should have access based on role-based hiding settings
        $common_methods = new Common_Methods;
        $current_user = wp_get_current_user();
        
        // Get always-hidden menu and submenu settings
        $menu_always_hidden = isset( $options['custom_menu_always_hidden'] ) ? json_decode( $options['custom_menu_always_hidden'], true ) : array();
        $submenu_always_hidden = isset( $options['custom_submenu_always_hidden'] ) ? json_decode( $options['custom_submenu_always_hidden'], true ) : array();
        $restrict_access = false;
        $hiding_info = $this->get_custom_menu_hiding_info__premium_only( $current_page, $new_items, $menu_always_hidden, $submenu_always_hidden );
        
        // Apply access restriction based on hiding settings
        if ( $hiding_info !== null ) {
            $restrict_access = $common_methods->menu_item_is_hidden_for_user__premium_only( $hiding_info, $current_user );
        }
        
        // Block access if user's role is restricted
        if ( $restrict_access ) {
            wp_die( 'Sorry, you are not allowed to access this page.' );
        }
        
        // Get target URL and validate
        $target_url = $new_items[$current_page]['url'];
        
        if ( empty( $target_url ) ) {
            wp_safe_redirect( admin_url() );
            exit;
        }
        
        // Normalize the URL - handle different formats
        $target_url = trim( $target_url );
        
        // If URL is relative and doesn't start with http:// or https://
        if ( strpos( $target_url, 'http://' ) !== 0 && strpos( $target_url, 'https://' ) !== 0 ) {
            if ( strpos( $target_url, '/wp-admin/' ) === 0 ) {
                // Convert to full admin URL
                $target_url = admin_url( substr( $target_url, 10 ) );
            } elseif ( strpos( $target_url, '/' ) === 0 ) {
                // Starts with / but not /wp-admin/, assume it's a site-relative URL
                $target_url = site_url( $target_url );
            } else {
                // Doesn't start with /, assume it's an admin-relative URL
                $target_url = admin_url( $target_url );
            }
        }
        
        // Perform the redirect
        // Use wp_redirect() for external URLs, wp_safe_redirect() for internal URLs
        if ( strpos( $target_url, 'http://' ) === 0 || strpos( $target_url, 'https://' ) === 0 ) {
            // External URL - use wp_redirect() to allow redirect to any domain
            wp_redirect( $target_url );
        } else {
            // Internal URL - use wp_safe_redirect() for security
            wp_safe_redirect( $target_url );
        }
        exit;
    }

    /**
     * Render custom menu page (redirects to target URL)
     * 
     * @since 7.10.0
     */
    public function render_custom_menu_page__premium_only() {
        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();
        $new_items = isset( $options['custom_menu_new_items'] ) ? json_decode( $options['custom_menu_new_items'], true ) : array();
        
        $current_page = isset( $_GET['page'] ) ? sanitize_text_field( $_GET['page'] ) : '';
        
        if ( isset( $new_items[$current_page] ) && isset( $new_items[$current_page]['url'] ) ) {
            
            // Check if current user should have access based on role-based hiding settings
            $common_methods = new Common_Methods;
            $current_user = wp_get_current_user();
            
            // Get always-hidden menu and submenu settings
            $menu_always_hidden = isset( $options['custom_menu_always_hidden'] ) ? json_decode( $options['custom_menu_always_hidden'], true ) : array();
            $submenu_always_hidden = isset( $options['custom_submenu_always_hidden'] ) ? json_decode( $options['custom_submenu_always_hidden'], true ) : array();
            $restrict_access = false;
            $hiding_info = $this->get_custom_menu_hiding_info__premium_only( $current_page, $new_items, $menu_always_hidden, $submenu_always_hidden );
            
            // Apply access restriction based on hiding settings
            if ( $hiding_info !== null ) {
                $restrict_access = $common_methods->menu_item_is_hidden_for_user__premium_only( $hiding_info, $current_user );
            }
            
            // Block access if user's role is restricted
            if ( $restrict_access ) {
                wp_die( 'Sorry, you are not allowed to access this page.' );
            }
            
            // Redirect to target URL if access is allowed
            $target_url = $new_items[$current_page]['url'];
            
            // Validate and normalize the target URL
            if ( empty( $target_url ) ) {
                // If URL is empty, redirect to dashboard
                wp_safe_redirect( admin_url() );
                exit;
            }
            
            // Normalize the URL - handle different formats
            $target_url = trim( $target_url );
            
            // If URL is relative and doesn't start with http:// or https://
            if ( strpos( $target_url, 'http://' ) !== 0 && strpos( $target_url, 'https://' ) !== 0 ) {
                // Check if URL already starts with /wp-admin/
                if ( strpos( $target_url, '/wp-admin/' ) === 0 ) {
                    // Convert to full admin URL
                    $target_url = admin_url( substr( $target_url, 10 ) ); // Remove '/wp-admin/' prefix
                } elseif ( strpos( $target_url, '/' ) === 0 ) {
                    // Starts with / but not /wp-admin/, assume it's a site-relative URL
                    $target_url = site_url( $target_url );
                } else {
                    // Doesn't start with /, assume it's an admin-relative URL
                    $target_url = admin_url( $target_url );
                }
            }
            
            // Perform the redirect
            // Use wp_redirect() for external URLs, wp_safe_redirect() for internal URLs
            if ( strpos( $target_url, 'http://' ) === 0 || strpos( $target_url, 'https://' ) === 0 ) {
                // External URL - use wp_redirect() to allow redirect to any domain
                wp_redirect( $target_url );
            } else {
                // Internal URL - use wp_safe_redirect() for security
                wp_safe_redirect( $target_url );
            }
            exit;
        }
        
        // Fallback if custom menu item not found or URL is missing
        // This shouldn't normally happen, but provides a safety net
        wp_safe_redirect( admin_url() );
        exit;
    }
    
    /**
     * Apply custom styling to custom menu items with target_type = 'none'
     * 
     * @since 7.11.0
     */
    public function apply_custom_menu_styles__premium_only() {
        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();
        $new_items = isset( $options['custom_menu_new_items'] ) ? $options['custom_menu_new_items'] : '';
        
        if ( empty( $new_items ) ) {
            return;
        }
        
        if ( is_string( $new_items ) ) {
            $new_items = json_decode( $new_items, true );
        }
        
        if ( ! is_array( $new_items ) || empty( $new_items ) ) {
            return;
        }
        
        $css = '<style id="asenha-custom-menu-styles">';
        $has_styles = false;
        
        foreach ( $new_items as $item_id => $info ) {
            $target_type = isset( $info['target_type'] ) ? $info['target_type'] : 'url';
            $parent_id = isset( $info['parent_id'] ) ? $info['parent_id'] : '';
            
            // Only apply styles to parent menus with target_type = 'none'
            if ( $target_type === 'none' && empty( $parent_id ) ) {
                $has_styles = true;
                $text_transform = isset( $info['text_transform'] ) ? $info['text_transform'] : 'title';
                $text_size = isset( $info['text_size'] ) ? $info['text_size'] : 'normal';
                $font_weight = isset( $info['font_weight'] ) ? $info['font_weight'] : 'normal';
                $alignment = isset( $info['alignment'] ) ? $info['alignment'] : 'left';
                $color_type = isset( $info['color_type'] ) ? $info['color_type'] : 'title';
                $color_value = isset( $info['color_value'] ) ? $info['color_value'] : '';
                $line_style = isset( $info['line_style'] ) ? $info['line_style'] : 'no-line';
                $computed_text_color = isset( $info['computed_text_color'] ) ? $info['computed_text_color'] : '';
                
                // Build CSS for this menu item
                // WordPress adds 'toplevel_page_' prefix to custom menu slugs
                $sanitized_id = 'toplevel_page_' . esc_attr( $item_id );
                
                // Apply background color to the menu item if needed
                if ( ! empty( $color_value ) && $color_type === 'background' ) {
                    // Fix any double hash issue
                    $bg_color = str_replace( '##', '#', $color_value );
                    $css .= "\n#adminmenu #" . $sanitized_id . " {";
                    $css .= " background-color: " . esc_attr( $bg_color ) . " !important;";
                    $css .= " }";
                    $css .= "\n#adminmenu #" . $sanitized_id . ":hover,";
                    $css .= "\n#adminmenu #" . $sanitized_id . ".wp-has-current-submenu {";
                    $css .= " background-color: " . esc_attr( $bg_color ) . " !important;";
                    $css .= " }";
                }
                
                // Apply styles to the menu name
                $css .= "\n#adminmenu #" . $sanitized_id . " .wp-menu-name {";
                $css .= " padding: 8px !important;";
                $css .= " box-sizing: border-box !important;";
                
                // Text transform
                if ( $text_transform === 'uppercase' ) {
                    $css .= " text-transform: uppercase !important;";
                } elseif ( $text_transform === 'title' ) {
                    $css .= " text-transform: capitalize !important;";
                } elseif ( $text_transform === 'lowercase' ) {
                    $css .= " text-transform: lowercase !important;";
                }
                
                // Text size
                if ( $text_size === 'smaller' ) {
                    $css .= " font-size: 0.9em !important;";
                } elseif ( $text_size === 'larger' ) {
                    $css .= " font-size: 1.1em !important;";
                }
                
                // Font weight
                if ( $font_weight === 'medium' ) {
                    $css .= " font-weight: 500 !important;";
                } elseif ( $font_weight === 'bold' ) {
                    $css .= " font-weight: 700 !important;";
                }
                
                // Alignment
                if ( $alignment === 'center' ) {
                    $css .= " text-align: center !important;";
                    $css .= " display: block !important;";
                } elseif ( $alignment === 'right' ) {
                    $css .= " text-align: right !important;";
                    $css .= " display: block !important;";
                    $css .= " padding-right: 10px !important;";
                }
                
                // Color
                if ( ! empty( $color_value ) ) {
                    if ( $color_type === 'background' ) {
                        if ( ! empty( $computed_text_color ) ) {
                            // Fix any double hash issue
                            $text_color = str_replace( '##', '#', $computed_text_color );
                            $css .= " color: " . esc_attr( $text_color ) . " !important;";
                        }
                    } else {
                        // Fix any double hash issue
                        $text_color = str_replace( '##', '#', $color_value );
                        $css .= " color: " . esc_attr( $text_color ) . " !important;";
                    }
                }
                
                $css .= " }";
                
                // Line Style (only when color_type is 'title')
                if ( $color_type === 'title' && $line_style !== 'no-line' ) {
                    $css .= "\n#adminmenu #" . $sanitized_id . " .wp-menu-name {";
                    $css .= " padding: 8px 0 !important;";
                    $css .= " margin: 0 8px !important;";
                    
                    // Use the title color if available, otherwise use default WordPress admin menu text color
                    // Fix any double hash issue that might exist in stored color values
                    $title_color = ! empty( $color_value ) ? str_replace( '##', '#', $color_value ) : '#f0f0f1';
                    
                    if ( $line_style === 'underline' ) {
                        $css .= " border-bottom: 1px solid " . esc_attr( $title_color ) . " !important;";
                        $css .= " padding-bottom: 4px !important;";
                    } elseif ( $line_style === 'side-line' ) {
                        if ( $alignment === 'left' ) {
                            $css .= " display: flex !important;";
                            $css .= " align-items: center !important;";
                            $css .= " }";
                            $css .= "\n#adminmenu #" . $sanitized_id . " .wp-menu-name::after {";
                            $css .= " content: '' !important;";
                            $css .= " flex: 1 !important;";
                            $css .= " height: 1px !important;";
                            $css .= " background-color: " . esc_attr( $title_color ) . " !important;";
                            $css .= " margin-left: 10px !important;";
                        } elseif ( $alignment === 'center' ) {
                            $css .= " display: flex !important;";
                            $css .= " align-items: center !important;";
                            $css .= " }";
                            // Left side line using ::before
                            $css .= "\n#adminmenu #" . $sanitized_id . " .wp-menu-name::before {";
                            $css .= " content: '' !important;";
                            $css .= " flex: 1 !important;";
                            $css .= " height: 1px !important;";
                            $css .= " background-color: " . esc_attr( $title_color ) . " !important;";
                            $css .= " margin-right: 10px !important;";
                            $css .= " }";
                            // Right side line using ::after
                            $css .= "\n#adminmenu #" . $sanitized_id . " .wp-menu-name::after {";
                            $css .= " content: '' !important;";
                            $css .= " flex: 1 !important;";
                            $css .= " height: 1px !important;";
                            $css .= " background-color: " . esc_attr( $title_color ) . " !important;";
                            $css .= " margin-left: 10px !important;";
                        } elseif ( $alignment === 'right' ) {
                            $css .= " display: flex !important;";
                            $css .= " align-items: center !important;";
                            $css .= " }";
                            $css .= "\n#adminmenu #" . $sanitized_id . " .wp-menu-name::before {";
                            $css .= " content: '' !important;";
                            $css .= " flex: 1 !important;";
                            $css .= " height: 1px !important;";
                            $css .= " background-color: " . esc_attr( $title_color ) . " !important;";
                            $css .= " margin-right: 10px !important;";
                        }
                    }
                    
                    $css .= " }";
                }
                
                // Remove link pointer and hover effects for 'none' target type
                $css .= "\n#adminmenu #" . $sanitized_id . " > a {";
                $css .= " cursor: default !important;";
                $css .= " pointer-events: none !important;";
                $css .= " }";
                
                // Hide the icon for 'none' target type
                $css .= "\n#adminmenu #" . $sanitized_id . " .wp-menu-image {";
                $css .= " display: none !important;";
                $css .= " }";
            }
        }
        
        // Add CSS rules for special classes
        $css .= "\n#adminmenu .menu-color-background {";
        $css .= " margin: 8px 0 !important;";
        $css .= " }";
        
        $css .= "\n#adminmenu .menu-line-underline {";
        $css .= " margin-bottom: 6px !important;";
        $css .= " }";
        
        $css .= "\n</style>";
        
        if ( $has_styles ) {
            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
            echo $css;
            
            // Output JavaScript to apply CSS classes based on styling options
            ?>
            <script type="text/javascript">
            (function() {
                if (typeof jQuery === 'undefined') return;
                
                jQuery(document).ready(function($) {
                    // Custom menu items data
                    var customMenuItems = <?php echo wp_json_encode( $new_items ); ?>;
                    
                    // Apply CSS classes based on styling options
                    $.each(customMenuItems, function(itemId, info) {
                        var targetType = info.target_type || 'url';
                        var parentId = info.parent_id || '';
                        
                        // Only apply to parent menus with target_type = 'none'
                        if (targetType === 'none' && !parentId) {
                            var sanitizedId = 'toplevel_page_' + itemId;
                            var menuElement = $('#adminmenu #' + sanitizedId);
                            
                            if (menuElement.length) {
                                var colorType = info.color_type || 'title';
                                var lineStyle = info.line_style || 'no-line';
                                
                                // Add class when Color >> Background is selected
                                if (colorType === 'background') {
                                    menuElement.addClass('menu-color-background');
                                }
                                
                                // Add class when Line Style >> Underline is selected
                                if (lineStyle === 'underline' && colorType === 'title') {
                                    menuElement.addClass('menu-line-underline');
                                }
                            }
                        }
                    });
                });
            })();
            </script>
            <?php
        }
    }
    
}