<?php
/**
 * Name: Duplicate Menu
 * Description: Quickly duplicate navigation menus with all menu items, structure, and hierarchy preserved for faster menu management and testing
 * Category: Admin
 * Icon: -
 * UseCases: [
 *   {
 *     "title": "Test Menu Changes",
 *     "description": "Duplicate your main menu before making changes so you can safely experiment without affecting the live navigation."
 *   },
 *   {
 *     "title": "Seasonal Menus",
 *     "description": "Clone your standard menu to create holiday or seasonal variations with special promotional links and pages."
 *   },
 *   {
 *     "title": "Multi-Location Menus",
 *     "description": "Duplicate a base menu template to quickly create location-specific navigation for multi-site or franchise setups."
 *   }
 * ]
 * WhereToFind: {
 *   "location": "Appearance - Menus",
 *   "url": "nav-menus.php",
 *   "instructions": "Click 'Duplicate Menu' button when editing a menu",
 *   "newTab": false
 * }
 * Added: 2025-02-06
 * Tags: menu, duplicate, clone, navigation, copy
 */

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

class SwitchboardDuplicateMenu {

    public function __construct() {
        // Add the Duplicate Menu button to the nav-menus admin page
        add_action('admin_footer', [$this, 'add_duplicate_button'], 10);

        // Handle the duplicate action
        add_action('admin_action_switchboard_duplicate_menu', [$this, 'handle_duplicate']);

        // Show success message after duplication
        add_action('admin_notices', [$this, 'show_duplicate_success_notice']);
    }

    /**
     * Show success notice after menu duplication
     */
    public function show_duplicate_success_notice() {
        $screen = get_current_screen();
        if (!$screen || $screen->id !== 'nav-menus') {
            return;
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display-only success notice from redirect
        if (isset($_GET['duplicated']) && $_GET['duplicated'] === '1') {
            $menu_name = isset($_GET['menu_name']) ? sanitize_text_field(wp_unslash(urldecode($_GET['menu_name']))) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Display-only, sanitized via wp_unslash+sanitize_text_field after urldecode
            ?>
            <div class="notice notice-success is-dismissible">
                <p>
                    <?php
                    if ($menu_name) {
                        /* translators: %s: menu name */
                        printf(esc_html__('Menu duplicated successfully! You are now editing "%s".', 'wpswitchboard'), esc_html($menu_name));
                    } else {
                        esc_html_e('Menu duplicated successfully!', 'wpswitchboard');
                    }
                    ?>
                </p>
            </div>
            <?php
        }
    }

    /**
     * Get default settings
     */
    public function get_default_settings() {
        return [];
    }

    /**
     * Render settings UI
     */
    public function render_settings($settings = []) {
        ?>
        <input type="hidden" name="module" value="duplicate-menu">

        <div style="text-align: center; padding: 24px 16px; background: #f9fafb; border-radius: 8px; border: 1px solid #e5e7eb;">
            <span class="dashicons dashicons-yes-alt" style="font-size: 32px; width: 32px; height: 32px; color: #10b981; margin-bottom: 8px;"></span>
            <p style="margin: 0; font-size: 14px; color: #374151; font-weight: 500;"><?php esc_html_e('No configuration needed', 'wpswitchboard'); ?></p>
            <p style="margin: 8px 0 0; font-size: 13px; color: #6b7280;"><?php esc_html_e('This module is ready to use. Go to Appearance → Menus to duplicate a menu.', 'wpswitchboard'); ?></p>
        </div>

        <div class="sb-sp-info-box sb-sp-info-box-tip" style="margin-top: 16px;">
            <h4 class="sb-sp-heading-sm"><?php esc_html_e('What Gets Duplicated', 'wpswitchboard'); ?></h4>
            <ul class="sb-sp-list">
                <li class="sb-sp-list-item"><?php esc_html_e('All menu items and their order', 'wpswitchboard'); ?></li>
                <li class="sb-sp-list-item"><?php esc_html_e('Parent-child hierarchy', 'wpswitchboard'); ?></li>
                <li class="sb-sp-list-item"><?php esc_html_e('CSS classes, link targets, and descriptions', 'wpswitchboard'); ?></li>
            </ul>
        </div>
        <?php
    }

    /**
     * Add duplicate button to the nav-menus page
     */
    public function add_duplicate_button() {
        $screen = get_current_screen();

        if (!$screen || $screen->id !== 'nav-menus') {
            return;
        }

        // Get current menu ID
        $menu_id = 0;
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display-only, reading menu ID for UI rendering
        if (isset($_GET['menu'])) {
            $menu_id = intval($_GET['menu']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        } else {
            $menu_id = intval(get_user_option('nav_menu_recently_edited'));
        }

        // Don't show button if no menu is selected
        if ($menu_id === 0) {
            return;
        }

        // Check user capability
        if (!current_user_can('edit_theme_options')) {
            return;
        }

        $duplicate_url = wp_nonce_url(
            admin_url('admin.php?action=switchboard_duplicate_menu&menu=' . $menu_id),
            'switchboard_duplicate_menu_' . $menu_id
        );
        ?>
        <script type="text/javascript">
        jQuery(document).ready(function($) {
            var $publishingAction = $('#update-nav-menu .publishing-action');
            if ($publishingAction.length) {
                var $wrapper = $('<span style="display: inline-flex; align-items: center; margin-left: 10px;"></span>');
                var $button = $('<a class="button" href="<?php echo esc_url($duplicate_url); ?>" title="WP Switchboard" style="display: inline-flex; align-items: center; gap: 5px;"><svg width="16" height="16" viewBox="-10 -15 88 130" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M16.3003 5.9193C10.6544 9.19757 5.00848 12.749 3.82465 13.7507C0.819569 16.6648 0 20.9447 0 33.6025C0 49.1743 0.637442 50.176 14.5701 58.3717C25.0424 64.4729 28.776 65.6567 29.6866 63.2891C29.9598 62.6517 30.233 56.3683 30.324 49.4475C30.5972 32.874 30.7794 32.6008 45.4405 24.0409C51.4507 20.5805 56.6413 17.2111 56.9145 16.4826C57.8251 14.115 55.6396 12.2027 44.9852 6.10143C30.8704 -2.0032 30.1419 -2.0032 16.3003 5.9193Z" fill="currentColor"/><path d="M37.7912 36.5184C37.6091 37.1558 37.3359 43.4392 37.1537 50.4511C36.7895 67.1156 36.6074 67.4799 21.9462 75.8577C15.936 79.3181 10.8365 82.6874 10.5633 83.4159C9.5616 85.9657 11.7471 87.878 22.4926 93.9793C36.6984 101.993 37.3359 101.993 51.0864 94.0703C56.7323 90.7921 62.3782 87.2406 63.5621 86.2389C66.4761 83.4159 67.3867 79.0449 67.3867 66.8424C67.3867 51.0885 66.5672 49.6315 52.8166 41.6179C42.5265 35.6078 38.5197 34.2418 37.7912 36.5184Z" fill="currentColor"/></svg><?php esc_html_e('Duplicate', 'wpswitchboard'); ?></a>');
                var $spinner = $('<span class="spinner" style="float: none; margin: 0 0 0 6px; visibility: hidden;"></span>');

                $button.on('click', function() {
                    $spinner.css('visibility', 'visible');
                    $button.css('pointer-events', 'none').css('opacity', '0.7');
                });

                $wrapper.append($button).append($spinner);
                $publishingAction.append($wrapper);
            }
        });
        </script>
        <?php
    }

    /**
     * Handle the duplicate action
     */
    public function handle_duplicate() {
        // Verify menu ID
        if (empty($_GET['menu'])) {
            wp_die(esc_html__('No menu to duplicate.', 'wpswitchboard'));
        }

        $menu_id = intval($_GET['menu']);

        // Verify nonce
        check_admin_referer('switchboard_duplicate_menu_' . $menu_id);

        // Check capability
        if (!current_user_can('edit_theme_options')) {
            wp_die(esc_html__('You do not have permission to duplicate menus.', 'wpswitchboard'));
        }

        // Get source menu
        $source_menu = wp_get_nav_menu_object($menu_id);
        if (!$source_menu) {
            wp_die(esc_html__('Menu not found.', 'wpswitchboard'));
        }

        // Create new menu name
        /* translators: %s: original menu name */
        $new_name = sprintf(__('%s (Copy)', 'wpswitchboard'), $source_menu->name);

        // Duplicate the menu
        $result = $this->duplicate_menu($menu_id, $new_name);

        if ($result) {
            // Redirect to the new menu with success flag
            $redirect_url = add_query_arg([
                'action' => 'edit',
                'menu' => $result['menu_id'],
                'duplicated' => '1',
                'menu_name' => $result['menu_name']
            ], admin_url('nav-menus.php'));
            wp_safe_redirect($redirect_url);
            exit;
        } else {
            wp_die(esc_html__('Failed to duplicate menu.', 'wpswitchboard'));
        }
    }

    /**
     * Duplicate a navigation menu
     *
     * @param int $source_id Source menu ID
     * @param string $new_name New menu name
     * @return array|false Array with menu_id and menu_name, or false on failure
     */
    private function duplicate_menu($source_id, $new_name) {
        $source_id = intval($source_id);
        $new_name = sanitize_text_field($new_name);

        if (empty($source_id) || empty($new_name)) {
            return false;
        }

        // Check if menu name already exists, try up to 10 variations
        $base_name = preg_replace('/\s*\(Copy(?:\s+\d+)?\)$/', '', $new_name); // Remove existing (Copy) or (Copy N) suffix
        $final_name = $new_name;
        $attempt = 1;

        while (wp_get_nav_menu_object($final_name) && $attempt <= 10) {
            $attempt++;
            /* translators: 1: original menu name, 2: copy number */
            $final_name = sprintf(__('%1$s (Copy %2$d)', 'wpswitchboard'), $base_name, $attempt);
        }

        // If still exists after 10 attempts, add timestamp
        if (wp_get_nav_menu_object($final_name)) {
            $final_name = $base_name . ' ' . gmdate('Y-m-d H:i:s');
        }

        // Create the new menu
        $new_menu_id = wp_create_nav_menu($final_name);

        if (is_wp_error($new_menu_id)) {
            return false;
        }

        // Get source menu items
        $source_items = wp_get_nav_menu_items($source_id);

        if (empty($source_items)) {
            return [
                'menu_id' => $new_menu_id,
                'menu_name' => $final_name
            ];
        }

        // Map old item IDs to new item IDs for parent relationships
        $id_map = [];
        $order = 1;

        foreach ($source_items as $menu_item) {
            // Build the new menu item args
            $args = [
                'post_title'     => $menu_item->title,
                'post_content'   => $menu_item->description,
                'post_excerpt'   => $menu_item->attr_title,
                'post_status'    => $menu_item->post_status,
                'post_type'      => 'nav_menu_item',
                'menu_order'     => $order,
                'comment_status' => 'closed',
                'ping_status'    => 'closed',
                'tax_input'      => [
                    'nav_menu' => [$new_menu_id]
                ]
            ];

            // Copy all meta data
            global $wpdb;
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Copying all meta for menu item duplication
            $meta_results = $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT meta_key, meta_value FROM $wpdb->postmeta WHERE post_id = %d",
                    $menu_item->ID
                )
            );

            $args['meta_input'] = [];
            foreach ($meta_results as $meta) {
                if ($meta->meta_key === '_menu_item_menu_item_parent') {
                    // Skip parent for now, we'll update it later
                    continue;
                }
                if (is_serialized($meta->meta_value)) {
                    $args['meta_input'][$meta->meta_key] = maybe_unserialize($meta->meta_value);
                } else {
                    $args['meta_input'][$meta->meta_key] = $meta->meta_value;
                }
            }

            // Insert the new menu item
            $new_item_id = wp_insert_post($args);

            if ($new_item_id && !is_wp_error($new_item_id)) {
                // Explicitly assign menu item to the new menu taxonomy term.
                // wp_insert_post()'s tax_input can fail silently if the user
                // lacks the assign_terms capability for nav_menu taxonomy.
                wp_set_object_terms($new_item_id, [$new_menu_id], 'nav_menu');

                // Store the mapping
                $id_map[$menu_item->db_id] = $new_item_id;

                // Update parent relationship if this item had a parent
                if (!empty($menu_item->menu_item_parent) && isset($id_map[$menu_item->menu_item_parent])) {
                    update_post_meta($new_item_id, '_menu_item_menu_item_parent', $id_map[$menu_item->menu_item_parent]);
                } else {
                    update_post_meta($new_item_id, '_menu_item_menu_item_parent', 0);
                }
            }

            $order++;
        }

        return [
            'menu_id' => $new_menu_id,
            'menu_name' => $final_name
        ];
    }

    /**
     * Register data locations for cleanup system
     */
    public function register_data_locations() {
        return [
            'options' => [],
            'option_patterns' => [],
            'tables' => [],
            'post_meta' => [],
            'user_meta' => [],
        ];
    }
}

$switchboard_instance = new SwitchboardDuplicateMenu();
Switchboard_Data_Registry::get_instance()->auto_register($switchboard_instance, __FILE__);
