<?php
/**
 * Name: Advanced Media Rename Manager
 * Description: Powerful media file renaming with bulk operations, pattern-based naming, automatic reference updates throughout site, undo capability, redirect creation, and full organization control for existing media library files
 * Category: Media
 * Icon: -
 * UseCases: [
 *   {
 *     "title": "SEO-Friendly Renaming",
 *     "description": "Rename existing camera files like DSC_0001.jpg to descriptive names like 'summer-vacation-2024.jpg' with all post content and references automatically updated."
 *   },
 *   {
 *     "title": "Bulk Organization",
 *     "description": "Bulk rename hundreds of product images to follow consistent pattern like 'product-{id}-{001}.jpg' with sequential numbering and progress tracking."
 *   },
 *   {
 *     "title": "Post-Based Naming",
 *     "description": "Rename featured images to match their post titles using {post-title} pattern for better organization and SEO across entire media library."
 *   }
 * ]
 * Added: 2025-11-21
 * Tags: media, rename, seo, bulk, organization, redirects, advanced, pattern, undo, manager
 * WhereToFind: {
 *   "location": "Switchboard → Media Rename",
 *   "url": "admin.php?page=switchboard-media-rename",
 *   "instructions": "Configure rename settings and manage media redirects. Use the Media Library to rename individual or bulk files.",
 *   "newTab": false
 * }
 */

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

class SwitchboardMediaRename {

    private $settings = [];
    private $table_name;

    public function __construct() {
        global $wpdb;
        $this->table_name = $wpdb->prefix . 'switchboard_media_redirects';

        $saved_settings = get_option('switchboard_media-rename_settings', []);
        $this->settings = wp_parse_args($saved_settings, $this->get_default_settings());

        // Create redirects table if needed
        add_action('admin_init', [$this, 'maybe_create_redirects_table']);

        // Admin menu for dedicated module page
        add_action('admin_menu', [$this, 'add_admin_menu']);

        // Single file rename
        add_action('add_meta_boxes_attachment', [$this, 'add_rename_meta_box']);
        add_action('wp_ajax_switchboard_rename_media', [$this, 'ajax_rename_media']);
        add_action('wp_ajax_switchboard_scan_references', [$this, 'ajax_scan_references']);

        // Bulk rename
        add_filter('bulk_actions-upload', [$this, 'add_bulk_rename_action']);
        add_filter('handle_bulk_actions-upload', [$this, 'handle_bulk_rename'], 10, 3);
        add_action('admin_notices', [$this, 'show_bulk_rename_notice']);

        // Enqueue scripts
        add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts']);

        // Undo functionality
        add_action('wp_ajax_switchboard_undo_rename', [$this, 'ajax_undo_rename']);
        add_action('wp_ajax_switchboard_get_bulk_rename_form', [$this, 'ajax_get_bulk_rename_form']);
        add_action('wp_ajax_switchboard_process_bulk_rename', [$this, 'ajax_process_bulk_rename']);

        // AJAX for redirect management
        add_action('wp_ajax_switchboard_delete_redirect', [$this, 'ajax_delete_redirect']);
        add_action('wp_ajax_switchboard_clear_all_redirects', [$this, 'ajax_clear_all_redirects']);

        // Cleanup old undo data daily
        add_action('wp_scheduled_delete', [$this, 'cleanup_old_undo_data']);

        // Built-in redirect handler (runs early on template_redirect)
        if ($this->settings['auto_redirect'] === 'yes') {
            add_action('template_redirect', [$this, 'handle_media_redirects'], 1);
        }
    }

    /**
     * Create redirects table if it doesn't exist
     * Uses option flag to avoid heavy DB checks on every page load
     * Note: get_option is cached by WP and autoloaded, so this is lightweight
     */
    public function maybe_create_redirects_table() {
        $db_version = '1.0';
        $option_name = 'switchboard_media_redirects_db_version';
        $installed_version = get_option($option_name, '');

        // Skip if already at current version (most common case - very fast due to WP option cache)
        if ($installed_version === $db_version) {
            return;
        }

        global $wpdb;
        $charset_collate = $wpdb->get_charset_collate();

        $sql = "CREATE TABLE IF NOT EXISTS {$this->table_name} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            old_path varchar(500) NOT NULL,
            new_path varchar(500) NOT NULL,
            media_id bigint(20) unsigned DEFAULT NULL,
            hits int(11) unsigned DEFAULT 0,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            UNIQUE KEY old_path (old_path(191)),
            KEY media_id (media_id)
        ) $charset_collate;";

        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql);

        // Migrate any existing redirects from option to table (only on first install)
        if (empty($installed_version)) {
            $this->migrate_redirects_from_option();
        }

        // Update version flag (autoload = yes for fast retrieval)
        update_option($option_name, $db_version, true);
    }

    /**
     * Migrate redirects from old option storage to new table
     */
    private function migrate_redirects_from_option() {
        global $wpdb;

        $old_redirects = get_option('switchboard_media_redirects', []);
        if (empty($old_redirects)) {
            return;
        }

        foreach ($old_redirects as $old_path => $data) {
            $wpdb->insert(
                $this->table_name,
                [
                    'old_path' => $old_path,
                    'new_path' => $data['to'],
                    'hits' => $data['hits'] ?? 0,
                    'created_at' => date('Y-m-d H:i:s', $data['created'] ?? time())
                ],
                ['%s', '%s', '%d', '%s']
            );
        }

        // Delete old option after migration
        delete_option('switchboard_media_redirects');
    }

    private function get_default_settings() {
        return [
            'default_pattern' => '{title}',
            'auto_redirect' => 'yes',
            'preserve_accents' => 'no',
            'max_filename_length' => '50',
            'case_transform' => 'lowercase',
            'excluded_types' => '',
            'add_unique_id' => 'yes'
        ];
    }

    /**
     * Register data locations for cleanup system
     */
    public function register_data_locations() {
        global $wpdb;
        return [
            'options' => [
                'switchboard_media-rename_settings',
                'switchboard_media_redirects_db_version'
            ],
            'option_patterns' => [],
            'tables' => [
                $wpdb->prefix . 'switchboard_media_redirects'
            ],
            'post_meta' => [
                '_switchboard_rename_undo',
                '_switchboard_rename_history',
                '_switchboard_old_filename'
            ],
            'user_meta' => [],
            'term_meta' => [],
            'transients' => []
        ];
    }

    /**
     * Add admin menu for dedicated module page
     */
    public function add_admin_menu() {
        add_submenu_page(
            'switchboard',
            __('Media Rename', 'wpswitchboard'),
            __('Media Rename', 'wpswitchboard'),
            'manage_options',
            'switchboard-media-rename',
            [$this, 'render_admin_page']
        );
    }

    /**
     * Render the dedicated module page
     */
    public function render_admin_page() {
        // Handle settings save
        if (isset($_POST['switchboard_media_rename_save']) && check_admin_referer('switchboard_media_rename_settings')) {
            $settings = [
                'default_pattern' => sanitize_text_field($_POST['default_pattern'] ?? '{title}'),
                'case_transform' => sanitize_text_field($_POST['case_transform'] ?? 'lowercase'),
                'max_filename_length' => absint($_POST['max_filename_length'] ?? 50),
                'excluded_types' => sanitize_text_field($_POST['excluded_types'] ?? ''),
                'add_unique_id' => isset($_POST['add_unique_id']) ? 'yes' : 'no',
                'auto_redirect' => isset($_POST['auto_redirect']) ? 'yes' : 'no',
                'preserve_accents' => isset($_POST['preserve_accents']) ? 'yes' : 'no',
            ];
            update_option('switchboard_media-rename_settings', $settings);
            $this->settings = $settings;
            echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('Settings saved.', 'wpswitchboard') . '</p></div>';
        }

        $settings = $this->settings;

        // Pagination
        $per_page = 20;
        $current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
        $offset = ($current_page - 1) * $per_page;

        $redirect_count = $this->get_redirect_count();
        $redirects = $this->get_redirects($per_page, $offset);
        $total_pages = ceil($redirect_count / $per_page);

        // Enqueue Switchboard admin CSS
        if (defined('SWITCHBOARD_URL') && defined('SWITCHBOARD_VERSION')) {
            wp_enqueue_style('switchboard-admin', SWITCHBOARD_URL . 'switchboard-admin.css', [], SWITCHBOARD_VERSION);
        }
        wp_enqueue_script('jquery');
        ?>
        <div class="wrap sb-module-wrap">
            <!-- Header -->
            <div class="sb-module-header">
                <div class="sb-module-header-left">
                    <svg class="sb-module-title-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
                    </svg>
                    <div>
                        <h1 class="sb-module-title">
                            <?php esc_html_e('Media Rename Manager', 'wpswitchboard'); ?>
                            <span class="switchboard-badge">SWITCHBOARD</span>
                        </h1>
                        <p class="sb-module-subtitle"><?php esc_html_e('Rename media files and manage redirects', 'wpswitchboard'); ?></p>
                    </div>
                </div>
                <div class="sb-module-header-right">
                    <a href="https://docs.wpswitchboard.com/modules/media/media-rename" target="_blank" class="sb-module-help-link">
                        <span class="sb-module-help-link-icon dashicons dashicons-editor-help"></span>
                        <?php esc_html_e('Help', 'wpswitchboard'); ?>
                    </a>
                    <a href="<?php echo esc_url(admin_url('upload.php')); ?>" class="sb-module-button sb-module-button-primary">
                        <span class="dashicons dashicons-images-alt2" style="margin-right: 6px;"></span>
                        <?php esc_html_e('Go to Media Library', 'wpswitchboard'); ?>
                    </a>
                </div>
            </div>

            <!-- Main Content -->
            <div class="sb-module-container">
                <!-- Stats Cards -->
                <div class="sb-module-stats">
                    <div class="sb-module-stat-card">
                        <div class="sb-module-stat-label"><?php esc_html_e('Files Renamed', 'wpswitchboard'); ?></div>
                        <div class="sb-module-stat-content">
                            <div class="sb-module-stat-value"><?php echo number_format($redirect_count); ?></div>
                        </div>
                    </div>
                    <div class="sb-module-stat-card">
                        <div class="sb-module-stat-label"><?php esc_html_e('Active Redirects', 'wpswitchboard'); ?></div>
                        <div class="sb-module-stat-content">
                            <div class="sb-module-stat-value"><?php echo number_format($redirect_count); ?></div>
                        </div>
                    </div>
                    <div class="sb-module-stat-card">
                        <div class="sb-module-stat-label"><?php esc_html_e('Redirect Hits', 'wpswitchboard'); ?></div>
                        <div class="sb-module-stat-content">
                            <div class="sb-module-stat-value"><?php echo number_format($this->get_total_hits()); ?></div>
                        </div>
                    </div>
                    <div class="sb-module-stat-card" style="display: flex; flex-direction: column;">
                        <div class="sb-module-stat-label"><?php esc_html_e('Auto Redirects', 'wpswitchboard'); ?></div>
                        <div class="sb-module-stat-content" style="flex: 1; display: flex; align-items: flex-end;">
                            <div class="sb-module-stat-value" style="display: flex; align-items: center; gap: 6px;">
                                <?php if ($settings['auto_redirect'] === 'yes'): ?>
                                    <span class="dashicons dashicons-yes-alt" style="font-size: 20px; width: 20px; height: 20px; color: #16a34a;"></span>
                                <?php else: ?>
                                    <span class="dashicons dashicons-marker" style="font-size: 20px; width: 20px; height: 20px; color: #9ca3af;"></span>
                                <?php endif; ?>
                                <span style="font-size: 13px; text-transform: uppercase; letter-spacing: 0.5px;"><?php echo $settings['auto_redirect'] === 'yes' ? esc_html__('ON', 'wpswitchboard') : esc_html__('OFF', 'wpswitchboard'); ?></span>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- Settings Form -->
                <form method="post" action="">
                    <?php wp_nonce_field('switchboard_media_rename_settings'); ?>

                    <!-- Two Column Layout with equal height -->
                    <div class="sb-module-layout-2col" style="align-items: stretch;">
                        <!-- Settings Card -->
                        <div class="sb-module-card" style="display: flex; flex-direction: column;">
                            <div class="sb-module-card-header">
                                <h2 class="sb-module-card-title"><?php esc_html_e('Rename Settings', 'wpswitchboard'); ?></h2>
                            </div>
                            <div class="sb-module-card-body" style="flex: 1;">
                                <!-- Default Pattern -->
                                <div style="display: flex; align-items: center; justify-content: space-between; gap: 20px; margin-bottom: 20px;">
                                    <div style="flex: 1;">
                                        <label class="sb-module-form-label" for="default_pattern"><?php esc_html_e('Default Pattern', 'wpswitchboard'); ?></label>
                                        <p class="sb-module-form-help"><?php esc_html_e('Template for generating new filenames. Click badges to insert.', 'wpswitchboard'); ?></p>
                                    </div>
                                    <div style="flex: 1;">
                                        <input type="text" name="default_pattern" id="default_pattern" value="<?php echo esc_attr($settings['default_pattern']); ?>" class="sb-module-input">
                                        <?php $media_categories_enabled = in_array('media-categories', get_option('switchboard_active_modules', [])); ?>
                                        <div style="display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px;">
                                            <span class="sb-module-var-badge" data-var="{title}">{title}</span>
                                            <span class="sb-module-var-badge" data-var="{id}">{id}</span>
                                            <span class="sb-module-var-badge" data-var="{date}">{date}</span>
                                            <span class="sb-module-var-badge" data-var="{counter}">{counter}</span>
                                            <span class="sb-module-var-badge" data-var="{post-title}">{post-title}</span>
                                            <?php if ($media_categories_enabled): ?>
                                                <span class="sb-module-var-badge" data-var="{category}">{category}</span>
                                            <?php else: ?>
                                                <span class="sb-module-var-badge sb-module-var-badge-disabled" style="opacity: 0.5; cursor: not-allowed; position: relative;">{category}<span class="sb-tooltip"><?php esc_html_e('Enable Media Categories module', 'wpswitchboard'); ?></span></span>
                                            <?php endif; ?>
                                        </div>
                                    </div>
                                </div>

                                <!-- Case Transformation -->
                                <div style="display: flex; align-items: center; justify-content: space-between; gap: 20px; margin-bottom: 20px;">
                                    <div style="flex: 1;">
                                        <label class="sb-module-form-label" for="case_transform"><?php esc_html_e('Case Transformation', 'wpswitchboard'); ?></label>
                                        <p class="sb-module-form-help"><?php esc_html_e('How to convert the filename text casing.', 'wpswitchboard'); ?></p>
                                    </div>
                                    <select name="case_transform" id="case_transform" class="sb-module-select" style="width: 160px;">
                                        <option value="lowercase" <?php selected($settings['case_transform'], 'lowercase'); ?>><?php esc_html_e('Lowercase', 'wpswitchboard'); ?></option>
                                        <option value="uppercase" <?php selected($settings['case_transform'], 'uppercase'); ?>><?php esc_html_e('Uppercase', 'wpswitchboard'); ?></option>
                                        <option value="titlecase" <?php selected($settings['case_transform'], 'titlecase'); ?>><?php esc_html_e('Title Case', 'wpswitchboard'); ?></option>
                                        <option value="none" <?php selected($settings['case_transform'], 'none'); ?>><?php esc_html_e('No Change', 'wpswitchboard'); ?></option>
                                    </select>
                                </div>

                                <!-- Excluded File Types -->
                                <div style="display: flex; align-items: center; justify-content: space-between; gap: 20px; margin-bottom: 20px;">
                                    <div style="flex: 1;">
                                        <label class="sb-module-form-label" for="excluded_types"><?php esc_html_e('Excluded File Types', 'wpswitchboard'); ?></label>
                                        <p class="sb-module-form-help"><?php esc_html_e('File extensions to skip during renaming (comma-separated).', 'wpswitchboard'); ?></p>
                                    </div>
                                    <input type="text" name="excluded_types" id="excluded_types" value="<?php echo esc_attr($settings['excluded_types']); ?>" class="sb-module-input" placeholder="pdf, zip, doc" style="width: 160px;">
                                </div>

                                <!-- Max Filename Length -->
                                <div style="display: flex; align-items: center; justify-content: space-between; gap: 20px; margin-bottom: 20px;">
                                    <div style="flex: 1;">
                                        <label class="sb-module-form-label" for="max_filename_length"><?php esc_html_e('Max Filename Length', 'wpswitchboard'); ?></label>
                                        <p class="sb-module-form-help"><?php esc_html_e('Maximum characters (20-200). Longer names truncated.', 'wpswitchboard'); ?></p>
                                    </div>
                                    <input type="number" name="max_filename_length" id="max_filename_length" value="<?php echo esc_attr($settings['max_filename_length']); ?>" min="20" max="200" class="sb-module-input" style="width: 100px;">
                                </div>

                                <!-- Checkboxes with proper styling -->
                                <div class="sb-module-checkbox-grid" style="grid-template-columns: 1fr; margin-top: 8px;">
                                    <label class="sb-module-checkbox-item">
                                        <input type="checkbox" name="add_unique_id" value="1" <?php checked($settings['add_unique_id'], 'yes'); ?>>
                                        <span><?php esc_html_e('Add unique ID suffix to prevent conflicts', 'wpswitchboard'); ?></span>
                                    </label>
                                    <label class="sb-module-checkbox-item">
                                        <input type="checkbox" name="auto_redirect" value="1" <?php checked($settings['auto_redirect'], 'yes'); ?>>
                                        <span><?php esc_html_e('Create automatic 301 redirects', 'wpswitchboard'); ?></span>
                                    </label>
                                    <label class="sb-module-checkbox-item">
                                        <input type="checkbox" name="preserve_accents" value="1" <?php checked($settings['preserve_accents'], 'yes'); ?>>
                                        <span><?php esc_html_e('Preserve accented characters (é, ñ, ü)', 'wpswitchboard'); ?></span>
                                    </label>
                                </div>

                                <div style="text-align: right; margin-top: 20px;">
                                    <button type="submit" name="switchboard_media_rename_save" class="sb-module-button sb-module-button-primary">
                                        <span class="dashicons dashicons-saved" style="margin-right: 6px;"></span>
                                        <?php esc_html_e('Save Settings', 'wpswitchboard'); ?>
                                    </button>
                                </div>
                            </div>
                        </div>

                        <!-- How to Use Card -->
                        <div class="sb-module-card" style="display: flex; flex-direction: column;">
                            <div class="sb-module-card-header">
                                <h2 class="sb-module-card-title"><?php esc_html_e('How to Use & Settings Guide', 'wpswitchboard'); ?></h2>
                            </div>
                            <div class="sb-module-card-body" style="flex: 1;">
                                <div class="sb-module-info-list">
                                    <div class="sb-module-info-item">
                                        <strong><?php esc_html_e('Single File Rename', 'wpswitchboard'); ?></strong>
                                        <p><?php esc_html_e('Go to Media Library, click on any image to edit it, and find the "Rename File" metabox in the sidebar. Enter a new name and click Rename.', 'wpswitchboard'); ?></p>
                                    </div>
                                    <div class="sb-module-info-item">
                                        <strong><?php esc_html_e('Bulk Rename', 'wpswitchboard'); ?></strong>
                                        <p><?php esc_html_e('In Media Library list view, select multiple files using checkboxes, choose "Rename Files" from Bulk Actions dropdown, and apply. A modal will let you configure the pattern and preview changes.', 'wpswitchboard'); ?></p>
                                    </div>
                                    <div class="sb-module-info-item">
                                        <strong><?php esc_html_e('Pattern Variables', 'wpswitchboard'); ?></strong>
                                        <p style="margin-bottom: 8px;"><?php esc_html_e('Click badges to insert variables into the pattern:', 'wpswitchboard'); ?></p>
                                        <ul style="margin: 0 0 0 16px; font-size: 13px; line-height: 1.7; color: #475569;">
                                            <li><code>{title}</code> - <?php esc_html_e('Media title from attachment', 'wpswitchboard'); ?></li>
                                            <li><code>{post-title}</code> - <?php esc_html_e('Parent post/page title if attached', 'wpswitchboard'); ?></li>
                                            <li><code>{id}</code> - <?php esc_html_e('Unique attachment ID number', 'wpswitchboard'); ?></li>
                                            <li><code>{date}</code> - <?php esc_html_e('Original upload date (YYYY-MM-DD)', 'wpswitchboard'); ?></li>
                                            <li><code>{counter}</code> - <?php esc_html_e('Sequential number for bulk operations', 'wpswitchboard'); ?></li>
                                            <li><code>{category}</code> - <?php esc_html_e('Media category name', 'wpswitchboard'); ?><?php if (!$media_categories_enabled): ?> <em style="color: #9ca3af;">(<?php esc_html_e('requires Media Categories module', 'wpswitchboard'); ?>)</em><?php endif; ?></li>
                                        </ul>
                                    </div>
                                    <div class="sb-module-info-item">
                                        <strong><?php esc_html_e('Settings Explained', 'wpswitchboard'); ?></strong>
                                        <ul style="margin: 8px 0 0 16px; font-size: 13px; line-height: 1.7; color: #475569;">
                                            <li><strong><?php esc_html_e('Max Length:', 'wpswitchboard'); ?></strong> <?php esc_html_e('Truncates at word boundary to prevent filesystem issues', 'wpswitchboard'); ?></li>
                                            <li><strong><?php esc_html_e('Unique ID:', 'wpswitchboard'); ?></strong> <?php esc_html_e('Appends short hash to avoid duplicate filenames', 'wpswitchboard'); ?></li>
                                            <li><strong><?php esc_html_e('301 Redirects:', 'wpswitchboard'); ?></strong> <?php esc_html_e('Old URLs automatically redirect to new ones', 'wpswitchboard'); ?></li>
                                            <li><strong><?php esc_html_e('Accents:', 'wpswitchboard'); ?></strong> <?php esc_html_e('Keep or convert characters like é→e, ñ→n', 'wpswitchboard'); ?></li>
                                        </ul>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </form>

                <!-- Redirects Table -->
                <div class="sb-module-card" style="margin-top: 24px;">
                    <div class="sb-module-card-header" style="display: flex; justify-content: space-between; align-items: center;">
                        <div>
                            <h2 class="sb-module-card-title"><?php esc_html_e('Media Redirects', 'wpswitchboard'); ?></h2>
                            <?php if ($redirect_count > 0): ?>
                            <p class="sb-module-card-desc" style="margin: 4px 0 0 0;"><?php printf(esc_html__('%d total redirects', 'wpswitchboard'), $redirect_count); ?></p>
                            <?php endif; ?>
                        </div>
                        <?php if ($redirect_count > 0): ?>
                        <button type="button" id="clear-all-redirects" class="sb-module-button sb-module-button-danger sb-module-button-sm">
                            <span class="dashicons dashicons-trash" style="margin-right: 4px;"></span>
                            <?php esc_html_e('Clear All', 'wpswitchboard'); ?>
                        </button>
                        <?php endif; ?>
                    </div>
                    <div class="sb-module-card-body">
                        <?php if (empty($redirects)): ?>
                            <div class="sb-module-empty">
                                <p class="sb-module-empty-desc"><?php esc_html_e('No redirects yet. Redirects are created automatically when you rename media files with "Create automatic 301 redirects" enabled.', 'wpswitchboard'); ?></p>
                            </div>
                        <?php else: ?>
                            <div class="sb-module-table-wrapper">
                                <table class="sb-module-table">
                                    <thead>
                                        <tr>
                                            <th><?php esc_html_e('Old Path', 'wpswitchboard'); ?></th>
                                            <th><?php esc_html_e('New Path', 'wpswitchboard'); ?></th>
                                            <th style="width: 70px; text-align: center;"><?php esc_html_e('Hits', 'wpswitchboard'); ?></th>
                                            <th style="width: 100px;"><?php esc_html_e('Created', 'wpswitchboard'); ?></th>
                                            <th style="width: 120px; text-align: center;"><?php esc_html_e('Actions', 'wpswitchboard'); ?></th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        <?php foreach ($redirects as $redirect): ?>
                                        <tr data-id="<?php echo esc_attr($redirect->id); ?>">
                                            <td style="font-family: monospace; font-size: 12px; word-break: break-all;">
                                                <?php echo esc_html($redirect->old_path); ?>
                                            </td>
                                            <td style="font-family: monospace; font-size: 12px; word-break: break-all;">
                                                <?php echo esc_html($redirect->new_path); ?>
                                            </td>
                                            <td style="text-align: center;">
                                                <span class="sb-module-badge"><?php echo number_format($redirect->hits); ?></span>
                                            </td>
                                            <td style="font-size: 12px; color: #64748b;">
                                                <?php echo esc_html(date('M j, Y', strtotime($redirect->created_at))); ?>
                                            </td>
                                            <td style="text-align: center;">
                                                <div style="display: flex; gap: 6px; justify-content: center;">
                                                    <a href="<?php echo esc_url(home_url($redirect->old_path)); ?>" target="_blank" class="sb-module-button sb-module-button-secondary sb-module-button-sm" title="<?php esc_attr_e('Test redirect', 'wpswitchboard'); ?>">
                                                        <span class="dashicons dashicons-external" style="font-size: 14px; width: 14px; height: 14px;"></span>
                                                    </a>
                                                    <button type="button" class="delete-redirect sb-module-button sb-module-button-secondary sb-module-button-sm" data-id="<?php echo esc_attr($redirect->id); ?>" title="<?php esc_attr_e('Delete redirect', 'wpswitchboard'); ?>">
                                                        <span class="dashicons dashicons-trash" style="font-size: 14px; width: 14px; height: 14px;"></span>
                                                    </button>
                                                </div>
                                            </td>
                                        </tr>
                                        <?php endforeach; ?>
                                    </tbody>
                                </table>
                            </div>

                            <?php if ($total_pages > 1): ?>
                            <!-- Pagination -->
                            <div class="sb-module-pagination" style="padding: 12px; display: flex; justify-content: space-between; align-items: center;">
                                <div class="sb-module-pagination-info" style="font-size: 13px; color: #64748b;">
                                    <?php
                                    $start = $offset + 1;
                                    $end = min($offset + $per_page, $redirect_count);
                                    printf(esc_html__('Showing %d-%d of %d redirects', 'wpswitchboard'), $start, $end, $redirect_count);
                                    ?>
                                </div>
                                <div class="sb-module-pagination-links" style="display: flex; gap: 4px;">
                                    <?php
                                    $base_url = admin_url('admin.php?page=switchboard-media-rename');

                                    // Previous button
                                    if ($current_page > 1): ?>
                                        <a href="<?php echo esc_url(add_query_arg('paged', $current_page - 1, $base_url)); ?>" class="sb-module-button sb-module-button-secondary sb-module-button-sm">
                                            &laquo; <?php esc_html_e('Previous', 'wpswitchboard'); ?>
                                        </a>
                                    <?php endif;

                                    // Page numbers
                                    $range = 2;
                                    for ($i = max(1, $current_page - $range); $i <= min($total_pages, $current_page + $range); $i++):
                                        if ($i == $current_page): ?>
                                            <span class="sb-module-button sb-module-button-primary sb-module-button-sm"><?php echo $i; ?></span>
                                        <?php else: ?>
                                            <a href="<?php echo esc_url(add_query_arg('paged', $i, $base_url)); ?>" class="sb-module-button sb-module-button-secondary sb-module-button-sm"><?php echo $i; ?></a>
                                        <?php endif;
                                    endfor;

                                    // Next button
                                    if ($current_page < $total_pages): ?>
                                        <a href="<?php echo esc_url(add_query_arg('paged', $current_page + 1, $base_url)); ?>" class="sb-module-button sb-module-button-secondary sb-module-button-sm">
                                            <?php esc_html_e('Next', 'wpswitchboard'); ?> &raquo;
                                        </a>
                                    <?php endif; ?>
                                </div>
                            </div>
                            <?php endif; ?>
                        <?php endif; ?>
                    </div>
                </div>
            </div>
        </div>

        <style>
        .sb-module-var-badge {
            display: inline-block;
            padding: 4px 10px;
            background: var(--primary-50, #f1f5f9);
            border: 1px solid var(--primary-200, #e2e8f0);
            border-radius: 4px;
            font-size: 12px;
            color: var(--primary-700, #475569);
            cursor: pointer;
            font-family: monospace;
            transition: all 0.2s ease;
        }
        .sb-module-var-badge:hover {
            background: var(--primary-100, #e2e8f0);
            border-color: var(--primary-300, #cbd5e1);
        }
        .sb-module-var-badge-disabled .sb-tooltip,
        .switchboard-var-badge-disabled .sb-tooltip {
            visibility: hidden;
            opacity: 0;
            position: absolute;
            bottom: calc(100% + 8px);
            left: 50%;
            transform: translateX(-50%);
            background: #1e293b;
            color: #fff;
            padding: 8px 12px;
            border-radius: 6px;
            font-size: 11px;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            white-space: nowrap;
            z-index: 1000;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
            transition: opacity 0.2s ease, visibility 0.2s ease;
        }
        .sb-module-var-badge-disabled .sb-tooltip::after,
        .switchboard-var-badge-disabled .sb-tooltip::after {
            content: '';
            position: absolute;
            top: 100%;
            left: 50%;
            transform: translateX(-50%);
            border: 5px solid transparent;
            border-top-color: #1e293b;
        }
        .sb-module-var-badge-disabled:hover .sb-tooltip,
        .switchboard-var-badge-disabled:hover .sb-tooltip {
            visibility: visible;
            opacity: 1;
        }
        .sb-module-info-list {
            display: flex;
            flex-direction: column;
            gap: 20px;
        }
        .sb-module-info-item p {
            margin: 4px 0 0 0;
            font-size: 13px;
            color: #64748b;
        }
        .sb-module-form-hint {
            margin: 4px 0 0 0;
            font-size: 12px;
            color: #94a3b8;
        }
        .sb-module-badge {
            display: inline-block;
            padding: 2px 8px;
            background: var(--primary-100, #e0e7ff);
            color: var(--primary-700, #4338ca);
            border-radius: 10px;
            font-size: 12px;
            font-weight: 500;
        }
        .sb-module-checkbox-grid {
            display: grid;
            gap: 8px;
        }
        .sb-module-table th,
        .sb-module-table td {
            padding: 14px 12px;
        }
        .sb-module-table tbody tr {
            border-bottom: 1px solid #f1f5f9;
        }
        .sb-module-table tbody tr:last-child {
            border-bottom: none;
        }
        </style>

        <script>
        jQuery(document).ready(function($) {
            // Variable badge click
            $('.sb-module-var-badge').on('click', function() {
                var varText = $(this).data('var');
                var $input = $('#default_pattern');
                var cursorPos = $input[0].selectionStart || $input.val().length;
                var currentVal = $input.val();
                var newVal = currentVal.substring(0, cursorPos) + varText + currentVal.substring(cursorPos);
                $input.val(newVal);
                $input.focus();
            });

            // Delete single redirect
            $('.delete-redirect').on('click', function() {
                var $btn = $(this);
                var redirectId = $btn.data('id');

                if (!confirm('<?php esc_html_e('Delete this redirect?', 'wpswitchboard'); ?>')) return;

                $btn.prop('disabled', true);

                $.post(ajaxurl, {
                    action: 'switchboard_delete_redirect',
                    redirect_id: redirectId,
                    nonce: '<?php echo wp_create_nonce('switchboard_nonce'); ?>'
                }, function(response) {
                    if (response.success) {
                        $btn.closest('tr').fadeOut(200, function() { $(this).remove(); });
                    } else {
                        alert(response.data.message || 'Error');
                        $btn.prop('disabled', false);
                    }
                });
            });

            // Clear all redirects
            $('#clear-all-redirects').on('click', function() {
                if (!confirm('<?php esc_html_e('Delete ALL redirects? This cannot be undone.', 'wpswitchboard'); ?>')) return;

                var $btn = $(this);
                $btn.prop('disabled', true).text('<?php esc_html_e('Clearing...', 'wpswitchboard'); ?>');

                $.post(ajaxurl, {
                    action: 'switchboard_clear_all_redirects',
                    nonce: '<?php echo wp_create_nonce('switchboard_nonce'); ?>'
                }, function(response) {
                    if (response.success) {
                        location.reload();
                    } else {
                        alert(response.data.message || 'Error');
                        $btn.prop('disabled', false).text('<?php esc_html_e('Clear All', 'wpswitchboard'); ?>');
                    }
                });
            });
        });
        </script>
        <?php
    }

    /**
     * Get total redirect hits
     */
    private function get_total_hits() {
        global $wpdb;
        return (int) $wpdb->get_var("SELECT SUM(hits) FROM {$this->table_name}");
    }

    /**
     * Add meta box for single file rename
     */
    public function add_rename_meta_box($post) {
        add_meta_box(
            'switchboard-rename-box',
            __('Rename File', 'wpswitchboard') . ' <span class="switchboard-badge">SWITCHBOARD</span>',
            [$this, 'render_rename_meta_box'],
            'attachment',
            'side',
            'high'
        );
    }

    /**
     * Render meta box content
     */
    public function render_rename_meta_box($post) {
        $file_path = get_attached_file($post->ID);
        $file_name = basename($file_path);
        $file_info = pathinfo($file_name);
        $name_without_ext = $file_info['filename'];
        $extension = isset($file_info['extension']) ? $file_info['extension'] : '';

        $can_undo = get_post_meta($post->ID, '_switchboard_rename_undo', true);

        wp_nonce_field('switchboard_rename_nonce', 'switchboard_rename_nonce');
        ?>
        <div id="switchboard-rename-container">
            <p class="sb-sp-mb-3">
                <label class="sb-sp-label" for="switchboard-new-filename"><strong><?php esc_html_e('New Filename:', 'wpswitchboard'); ?></strong></label>
                <input type="text" id="switchboard-new-filename" value="<?php echo esc_attr($name_without_ext); ?>" class="sb-sp-input">
                <?php if ($extension): ?>
                    <span class="switchboard-extension">.<?php echo esc_html($extension); ?></span>
                <?php endif; ?>
            </p>

            <p class="sb-sp-description" id="switchboard-filename-preview">
                <?php esc_html_e('Current:', 'wpswitchboard'); ?> <code><?php echo esc_html($file_name); ?></code>
            </p>

            <div id="switchboard-rename-options" class="sb-sp-my-2">
                <div class="sb-sp-checkbox-field">
                    <input type="checkbox" id="switchboard-update-title" checked class="sb-sp-checkbox">
                    <div class="sb-sp-checkbox-label">
                        <?php esc_html_e('Update media title', 'wpswitchboard'); ?>
                    </div>
                </div>
                <div class="sb-sp-checkbox-field">
                    <input type="checkbox" id="switchboard-update-slug" checked class="sb-sp-checkbox">
                    <div class="sb-sp-checkbox-label">
                        <?php esc_html_e('Update permalink slug', 'wpswitchboard'); ?>
                    </div>
                </div>
                <div class="sb-sp-checkbox-field">
                    <input type="checkbox" id="switchboard-scan-before-rename" class="sb-sp-checkbox">
                    <div class="sb-sp-checkbox-label">
                        <?php esc_html_e('Scan for references first', 'wpswitchboard'); ?>
                    </div>
                </div>
            </div>

            <div id="switchboard-references-info" class="sb-sp-alert-info" style="display: none;">
                <p class="sb-sp-m-0"><strong><?php esc_html_e('References found:', 'wpswitchboard'); ?></strong> <span id="switchboard-ref-count">0</span></p>
                <ul id="switchboard-ref-list" class="sb-sp-list" style="margin: 5px 0;"></ul>
            </div>

            <p>
                <button type="button" id="switchboard-rename-btn" class="button button-primary button-large sb-sp-w-full" style="display: inline-flex; align-items: center; justify-content: center; gap: 6px;">
                    <span class="dashicons dashicons-edit" style="line-height: 1; font-size: 16px;"></span>
                    <?php esc_html_e('Rename File', 'wpswitchboard'); ?>
                </button>
            </p>

            <?php if ($can_undo): ?>
                <p>
                    <button type="button" id="switchboard-undo-btn" class="button button-secondary sb-sp-w-full" style="display: inline-flex; align-items: center; justify-content: center; gap: 6px;">
                        <span class="dashicons dashicons-undo" style="line-height: 1; font-size: 16px;"></span>
                        <?php esc_html_e('Undo Last Rename', 'wpswitchboard'); ?>
                    </button>
                </p>
            <?php endif; ?>

            <div id="switchboard-rename-status" class="sb-sp-mt-3"></div>
        </div>
        <?php
    }

    /**
     * Enqueue scripts and styles
     */
    public function enqueue_scripts($hook) {
        // Load on attachment edit page (post.php) and media library (upload.php)
        if (!in_array($hook, ['post.php', 'upload.php'])) {
            return;
        }

        // On post.php, only load for attachment post type
        if ($hook === 'post.php') {
            global $post;
            if (!$post || $post->post_type !== 'attachment') {
                return;
            }
        }

        wp_enqueue_script('jquery');

        // Localize nonce for JavaScript - works in both List and Grid views
        wp_localize_script('jquery', 'switchboardMediaRename', [
            'nonce' => wp_create_nonce('bulk-media'),
            'case_transform' => isset($this->settings['case_transform']) ? $this->settings['case_transform'] : 'lowercase'
        ]);

        $script = <<<'JAVASCRIPT'
(function($) {
    "use strict";

    var SwitchboardMediaRename = {
        mediaId: null,
        extension: "",

        init: function() {
            // Get media ID - works for both regular posts and attachment edit pages
            this.mediaId = $("#post_ID").val() || $("input[name='post_ID']").val();
            this.extension = $(".switchboard-extension").text();
            this.bindEvents();
            this.initBulkRename();
        },

        bindEvents: function() {
            var self = this;

            $("#switchboard-rename-btn").on("click", function(e) {
                e.preventDefault();
                if ($("#switchboard-scan-before-rename").is(":checked") && !$("#switchboard-references-info").is(":visible")) {
                    self.scanReferences();
                } else {
                    self.renameFile();
                }
            });

            $("#switchboard-undo-btn").on("click", function(e) {
                e.preventDefault();
                self.undoRename();
            });

            $("#switchboard-new-filename").on("input", function() {
                var newName = $(this).val();
                var sanitized = self.sanitizeFilename(newName);
                var preview = sanitized + self.extension;
                $("#switchboard-filename-preview").html("Preview: <code>" + $("<div>").text(preview).html() + "</code>");
            });

            $("#switchboard-scan-before-rename").on("change", function() {
                if ($(this).is(":checked")) {
                    $("#switchboard-references-info").show();
                } else {
                    $("#switchboard-references-info").hide();
                }
            });
        },

        sanitizeFilename: function(filename) {
            var caseTransform = (window.switchboardMediaRename && window.switchboardMediaRename.case_transform) || "lowercase";
            switch (caseTransform) {
                case "lowercase":
                    filename = filename.toLowerCase();
                    break;
                case "uppercase":
                    filename = filename.toUpperCase();
                    break;
                case "titlecase":
                    filename = filename.toLowerCase().replace(/(^|[-_])([a-z])/g, function(m, sep, letter) {
                        return sep + letter.toUpperCase();
                    });
                    break;
                // "none": no transformation
            }
            filename = filename.replace(/[^a-zA-Z0-9\-_.]/g, "-");
            filename = filename.replace(/-+/g, "-");
            filename = filename.replace(/^-+|-+$/g, "");
            return filename || "file";
        },

        scanReferences: function() {
            var self = this;
            $("#switchboard-rename-status").html('<p class="text-blue-600">Scanning for references...</p>');
            $("#switchboard-rename-btn").prop("disabled", true);

            $.ajax({
                url: ajaxurl,
                type: "POST",
                data: {
                    action: "switchboard_scan_references",
                    media_id: self.mediaId,
                    nonce: $("#switchboard_rename_nonce").val()
                },
                success: function(response) {
                    if (response.success) {
                        var count = response.data.count;
                        var references = response.data.references;

                        $("#switchboard-ref-count").text(count);

                        var list = $("#switchboard-ref-list");
                        list.empty();

                        $.each(references, function(type, items) {
                            if (items.length > 0) {
                                var typeEscaped = $("<div>").text(type).html();
                                list.append("<li><strong>" + typeEscaped + ":</strong> " + items.length + " found</li>");
                            }
                        });

                        $("#switchboard-references-info").show();
                        $("#switchboard-rename-status").html('<p class="text-green-600">Scan complete. Click "Rename File" to proceed.</p>');
                    } else {
                        $("#switchboard-rename-status").html('<p class="sb-sp-text-error">Error: ' + $('<div>').text(response.data.message).html() + '</p>');
                    }
                    $("#switchboard-rename-btn").prop("disabled", false);
                },
                error: function() {
                    $("#switchboard-rename-status").html('<p class="sb-sp-text-error">Error scanning references.</p>');
                    $("#switchboard-rename-btn").prop("disabled", false);
                }
            });
        },

        renameFile: function() {
            var self = this;
            var newFilename = $("#switchboard-new-filename").val();
            var updateTitle = $("#switchboard-update-title").is(":checked");
            var updateSlug = $("#switchboard-update-slug").is(":checked");

            if (!newFilename) {
                alert("Please enter a filename.");
                return;
            }

            $("#switchboard-rename-status").html('<p class="text-blue-600">Renaming file and updating references...</p>');
            $("#switchboard-rename-btn").prop("disabled", true);

            $.ajax({
                url: ajaxurl,
                type: "POST",
                data: {
                    action: "switchboard_rename_media",
                    media_id: self.mediaId,
                    new_filename: newFilename,
                    update_title: updateTitle ? "yes" : "no",
                    update_slug: updateSlug ? "yes" : "no",
                    nonce: $("#switchboard_rename_nonce").val()
                },
                success: function(response) {
                    if (response.success) {
                        $("#switchboard-rename-status").html('<p class="text-green-600">' + response.data.message + '</p>');

                        setTimeout(function() {
                            location.reload();
                        }, 1500);
                    } else {
                        $("#switchboard-rename-status").html('<p class="sb-sp-text-error">Error: ' + $('<div>').text(response.data.message).html() + '</p>');
                        $("#switchboard-rename-btn").prop("disabled", false);
                    }
                },
                error: function() {
                    $("#switchboard-rename-status").html('<p class="sb-sp-text-error">Error renaming file.</p>');
                    $("#switchboard-rename-btn").prop("disabled", false);
                }
            });
        },

        undoRename: function() {
            var self = this;

            if (!confirm("Are you sure you want to undo the last rename? This will restore the previous filename.")) {
                return;
            }

            $("#switchboard-rename-status").html('<p class="text-blue-600">Undoing rename...</p>');
            $("#switchboard-undo-btn").prop("disabled", true);

            $.ajax({
                url: ajaxurl,
                type: "POST",
                data: {
                    action: "switchboard_undo_rename",
                    media_id: self.mediaId,
                    nonce: $("#switchboard_rename_nonce").val()
                },
                success: function(response) {
                    if (response.success) {
                        $("#switchboard-rename-status").html('<p class="text-green-600">' + response.data.message + '</p>');
                        setTimeout(function() {
                            location.reload();
                        }, 1500);
                    } else {
                        $("#switchboard-rename-status").html('<p class="sb-sp-text-error">Error: ' + $('<div>').text(response.data.message).html() + '</p>');
                        $("#switchboard-undo-btn").prop("disabled", false);
                    }
                },
                error: function() {
                    $("#switchboard-rename-status").html('<p class="sb-sp-text-error">Error undoing rename.</p>');
                    $("#switchboard-undo-btn").prop("disabled", false);
                }
            });
        },

        initBulkRename: function() {
            var self = this;

            // Add Rename Files button right after Apply button (List View)
            var $applyBtn = $("#doaction").first();
            if ($applyBtn.length) {
                var $renameBtn = $('<button type="button" id="switchboard-rename-files-btn" class="button" style="margin-left: 8px;">Rename Files</button>');
                // Insert immediately after Apply button (before any other injected content)
                $applyBtn.after($renameBtn);

                $renameBtn.on("click", function(e) {
                    e.preventDefault();
                    self.openBulkRenameModal();
                });
            }

            // Also handle bulk actions dropdown
            $(document).on("click", "#doaction, #doaction2", function(e) {
                var action = $(this).siblings("select[name='action'], select[name='action2']").val();
                if (action === "bulk_rename_media") {
                    e.preventDefault();
                    self.openBulkRenameModal();
                    return false;
                }
            });

            // Grid View support - add button to toolbar
            self.initGridViewSupport();
        },

        initGridViewSupport: function() {
            var self = this;

            // Check if we're in grid view mode
            function addGridViewButton() {
                // Look for the grid view container
                var $gridContainer = $(".attachments-browser");
                if ($gridContainer.length === 0) return;

                // Check if button already exists
                if ($("#switchboard-grid-rename-btn").length > 0) return;

                // Find the toolbar area in grid view
                var $toolbar = $gridContainer.find(".media-toolbar-secondary");
                if ($toolbar.length === 0) {
                    $toolbar = $gridContainer.find(".media-toolbar");
                }

                if ($toolbar.length > 0) {
                    var $gridRenameBtn = $('<button type="button" id="switchboard-grid-rename-btn" class="button media-button" style="margin-left: 8px;">Bulk Rename</button>');
                    $toolbar.append($gridRenameBtn);

                    $gridRenameBtn.on("click", function(e) {
                        e.preventDefault();
                        self.openBulkRenameModal();
                    });
                }
            }

            // Try to add button immediately and also observe for grid view loading
            addGridViewButton();

            // Use MutationObserver to detect when grid view is loaded (async)
            if (typeof MutationObserver !== "undefined") {
                var observer = new MutationObserver(function(mutations) {
                    addGridViewButton();
                });

                // Observe the body for changes (grid view loads dynamically)
                var targetNode = document.body;
                if (targetNode) {
                    observer.observe(targetNode, { childList: true, subtree: true });
                }

                // Disconnect after 10 seconds to prevent memory leaks
                setTimeout(function() {
                    observer.disconnect();
                }, 10000);
            }

            // Also try after a delay for slower loading
            setTimeout(addGridViewButton, 1000);
            setTimeout(addGridViewButton, 2000);
        },

        openBulkRenameModal: function() {
            var selectedIds = [];

            // Try to get selected IDs from List View first
            $("input[name='media[]']:checked").each(function() {
                selectedIds.push($(this).val());
            });

            // If no selection in list view, try Grid View selection
            if (selectedIds.length === 0 && typeof wp !== "undefined" && wp.media && wp.media.frame) {
                try {
                    var state = wp.media.frame.state();
                    if (state) {
                        var selection = state.get("selection");
                        if (selection && selection.length > 0) {
                            selection.each(function(attachment) {
                                selectedIds.push(attachment.id.toString());
                            });
                        }
                    }
                } catch (e) {
                    // Grid view state not available, continue to fallback methods
                    console.log("Switchboard: Grid view state not available, using DOM fallback");
                }
            }

            // If still no selection, try to get selected items from grid view DOM
            if (selectedIds.length === 0) {
                $(".attachment.selected, .attachment.details").each(function() {
                    var id = $(this).data("id");
                    if (id) {
                        selectedIds.push(id.toString());
                    }
                });
            }

            // If no selection, get all visible media IDs on current page (List View)
            if (selectedIds.length === 0) {
                $(".wp-list-table.media tbody tr").each(function() {
                    var $checkbox = $(this).find("input[name='media[]']");
                    if ($checkbox.length) {
                        selectedIds.push($checkbox.val());
                    }
                });
            }

            // If still no selection, try to get all visible grid items
            if (selectedIds.length === 0) {
                $(".attachments-browser .attachment").each(function() {
                    var id = $(this).data("id");
                    if (id) {
                        selectedIds.push(id.toString());
                    }
                });
            }

            if (selectedIds.length === 0) {
                alert("No media files found. Please select files or ensure the media library has items.");
                return;
            }

            var self = this;

            // Get nonce - prefer localized script nonce, fallback to DOM
            var nonce = (typeof switchboardMediaRename !== "undefined" && switchboardMediaRename.nonce)
                ? switchboardMediaRename.nonce
                : $("#_wpnonce").val();

            $.ajax({
                url: ajaxurl,
                type: "POST",
                data: {
                    action: "switchboard_get_bulk_rename_form",
                    media_ids: selectedIds,
                    nonce: nonce
                },
                success: function(response) {
                    if (response.success) {
                        self.showBulkRenameModal(response.data.html, selectedIds);
                    } else {
                        alert("Error: " + $('<div>').text(response.data.message).html());
                    }
                },
                error: function() {
                    alert("Error loading bulk rename form.");
                }
            });
        },

        showBulkRenameModal: function(html, mediaIds) {
            var self = this;

            var modal = $('<div id="switchboard-bulk-rename-modal" style="display:none; position:fixed; z-index:160000; left:0; top:0; width:100%; height:100%; background:rgba(0,0,0,0.75); overflow-y:auto; padding:40px 20px;"></div>');
            var modalContent = $('<div class="switchboard-modal-content" style="background:#fff; border-radius:8px; max-width:500px; margin:0 auto; box-shadow:0 20px 50px rgba(0,0,0,0.3);"></div>');
            var modalHeader = $('<div style="padding:20px 24px; border-bottom:1px solid #e5e7eb;"><h2 style="margin:0; font-size:18px; font-weight:600; color:#1e293b;">Bulk Rename Media Files</h2></div>');
            var modalBody = $('<div style="padding:24px; max-height:calc(100vh - 300px); overflow-y:auto;"></div>');
            var modalFooter = $('<div class="switchboard-modal-footer" style="padding:16px 24px; border-top:1px solid #e5e7eb; display:flex; justify-content:flex-end; gap:8px;"></div>');

            modalBody.html(html);

            var cancelBtn = $('<button type="button" class="button button-large">Cancel</button>');
            var renameBtn = $('<button type="button" class="button button-primary button-large">Rename All</button>');

            cancelBtn.on("click", function() {
                modal.fadeOut(200, function() {
                    modal.remove();
                });
            });

            renameBtn.on("click", function() {
                self.processBulkRename(mediaIds, modalBody);
            });

            modalFooter.append(cancelBtn).append(renameBtn);
            modalContent.append(modalHeader).append(modalBody).append(modalFooter);
            modal.append(modalContent);

            $("body").append(modal);
            modal.fadeIn(200);

            // Handle variable badge clicks
            modal.on("click", ".switchboard-var-badge", function() {
                var varText = $(this).data("var");
                var input = $("#switchboard-bulk-pattern");
                var cursorPos = input[0].selectionStart;
                var currentVal = input.val();
                var newVal = currentVal.substring(0, cursorPos) + varText + currentVal.substring(cursorPos);
                input.val(newVal);
                input.focus();
                // Set cursor position after inserted text
                var newPos = cursorPos + varText.length;
                input[0].setSelectionRange(newPos, newPos);
            });
        },

        processBulkRename: function(mediaIds, modalBody) {
            var pattern = $("#switchboard-bulk-pattern").val();
            var updateTitles = $("#switchboard-bulk-update-titles").is(":checked");
            var updateSlugs = $("#switchboard-bulk-update-slugs").is(":checked");
            var addPrefix = $("#switchboard-bulk-prefix").val();
            var addSuffix = $("#switchboard-bulk-suffix").val();

            // Get nonce - prefer localized script nonce, fallback to DOM
            var nonce = (typeof switchboardMediaRename !== "undefined" && switchboardMediaRename.nonce)
                ? switchboardMediaRename.nonce
                : $("#_wpnonce").val();

            if (!pattern && !addPrefix && !addSuffix) {
                alert("Please enter a renaming pattern, prefix, or suffix.");
                return;
            }

            var progressDiv = $('<div class="sb-sp-my-5"><p><strong>Processing...</strong></p><div class="sb-sp-w-full" style="height:8px; background:#e5e7eb; border-radius:4px; overflow:hidden;"><div id="bulk-progress-bar" style="height:100%; background:#2271b1; width:0%; transition:width 0.3s;"></div></div><p id="bulk-status">Starting...</p></div>');
            modalBody.html(progressDiv);

            var processed = 0;
            var total = mediaIds.length;
            var results = [];

            function processNext(index) {
                if (index >= mediaIds.length) {
                    showResults();
                    return;
                }

                var mediaId = mediaIds[index];
                var progress = Math.round(((index + 1) / total) * 100);
                $("#bulk-progress-bar").css("width", progress + "%");
                $("#bulk-status").text("Processing " + (index + 1) + " of " + total + "...");

                $.ajax({
                    url: ajaxurl,
                    type: "POST",
                    data: {
                        action: "switchboard_process_bulk_rename",
                        media_id: mediaId,
                        pattern: pattern,
                        prefix: addPrefix,
                        suffix: addSuffix,
                        update_title: updateTitles ? "yes" : "no",
                        update_slug: updateSlugs ? "yes" : "no",
                        counter: index + 1,
                        nonce: nonce
                    },
                    success: function(response) {
                        if (response.success) {
                            if (response.data.skipped) {
                                results.push({success: true, skipped: true, id: mediaId, message: response.data.message, extension: response.data.extension});
                            } else {
                                results.push({success: true, skipped: false, id: mediaId, message: response.data.message});
                            }
                        } else {
                            results.push({success: false, skipped: false, id: mediaId, message: response.data.message});
                        }
                        processNext(index + 1);
                    },
                    error: function() {
                        results.push({success: false, skipped: false, id: mediaId, message: "Error processing file"});
                        processNext(index + 1);
                    }
                });
            }

            function showResults() {
                var renamedCount = results.filter(function(r) { return r.success && !r.skipped; }).length;
                var skippedCount = results.filter(function(r) { return r.success && r.skipped; }).length;
                var failCount = results.filter(function(r) { return !r.success; }).length;

                // Collect skipped extensions
                var skippedExtensions = {};
                results.filter(function(r) { return r.skipped && r.extension; }).forEach(function(r) {
                    skippedExtensions[r.extension] = (skippedExtensions[r.extension] || 0) + 1;
                });

                var resultHtml = '<div class="sb-sp-my-5"><h3 class="sb-sp-heading">Bulk Rename Complete</h3>';
                resultHtml += '<p style="color: #16a34a;"><strong>Renamed:</strong> ' + renamedCount + ' files</p>';
                if (skippedCount > 0) {
                    var extList = Object.keys(skippedExtensions).map(function(ext) {
                        return '.' + ext + ' (' + skippedExtensions[ext] + ')';
                    }).join(', ');
                    resultHtml += '<p style="color: #ca8a04;"><strong>⏭ Skipped:</strong> ' + skippedCount + ' files <span style="font-size: 12px; color: #9ca3af;">(' + extList + ' excluded)</span></p>';
                }
                if (failCount > 0) {
                    resultHtml += '<p class="sb-sp-text-error"><strong>Failed:</strong> ' + failCount + ' files</p>';
                }
                resultHtml += '<p class="sb-sp-mt-4"><button type="button" class="button button-primary" onclick="location.reload()">Reload Page</button></p>';
                resultHtml += '</div>';

                modalBody.html(resultHtml);

                // Hide footer buttons after completion
                modalBody.closest('.switchboard-modal-content').find('.switchboard-modal-footer').hide();
            }

            processNext(0);
        }
    };

    $(document).ready(function() {
        // Initialize on attachment edit page or any page with rename container
        if ($("#switchboard-rename-container").length > 0) {
            SwitchboardMediaRename.init();
        }

        // Initialize bulk rename on media library (both List and Grid views)
        // Check for list view table OR grid view container OR media library page
        var isMediaLibrary = $(".wp-list-table.media").length > 0 ||
                             $(".attachments-browser").length > 0 ||
                             $("body.upload-php").length > 0;

        if (isMediaLibrary) {
            SwitchboardMediaRename.initBulkRename();
        }
    });

})(jQuery);
JAVASCRIPT;

        wp_add_inline_script('jquery', $script);
    }

    /**
     * Add bulk rename action
     */
    public function add_bulk_rename_action($bulk_actions) {
        $bulk_actions['bulk_rename_media'] = __('Rename Files', 'wpswitchboard');
        return $bulk_actions;
    }

    /**
     * Handle bulk rename (this just triggers the modal via JS)
     */
    public function handle_bulk_rename($redirect_to, $action, $post_ids) {
        if ($action !== 'bulk_rename_media') {
            return $redirect_to;
        }

        // The actual bulk rename is handled via AJAX modal
        return $redirect_to;
    }

    /**
     * Show bulk rename notice
     */
    public function show_bulk_rename_notice() {
        if (!isset($_GET['bulk_rename_done'])) {
            return;
        }

        $count = isset($_GET['renamed']) ? intval($_GET['renamed']) : 0;
        ?>
        <div class="notice notice-success is-dismissible">
            <p><?php printf(_n('%s file renamed successfully.', '%s files renamed successfully.', $count, 'wpswitchboard'), number_format_i18n($count)); ?></p>
        </div>
        <?php
    }

    /**
     * AJAX: Scan for references
     */
    public function ajax_scan_references() {
        check_ajax_referer('switchboard_rename_nonce', 'nonce');

        if (!current_user_can('upload_files')) {
            wp_send_json_error(['message' => __('Permission denied', 'wpswitchboard')]);
        }

        $media_id = isset($_POST['media_id']) ? intval($_POST['media_id']) : 0;

        if (!$media_id) {
            wp_send_json_error(['message' => __('Invalid media ID', 'wpswitchboard')]);
        }

        // Verify user can edit this specific attachment
        if (!current_user_can('edit_post', $media_id)) {
            wp_send_json_error(['message' => __('You do not have permission to scan this file', 'wpswitchboard')]);
        }

        // Verify it's actually an attachment
        $post = get_post($media_id);
        if (!$post || $post->post_type !== 'attachment') {
            wp_send_json_error(['message' => __('Invalid media ID', 'wpswitchboard')]);
        }

        $references = $this->find_references($media_id);
        $count = array_sum(array_map('count', $references));

        wp_send_json_success([
            'count' => $count,
            'references' => $references
        ]);
    }

    /**
     * AJAX: Rename media file
     */
    public function ajax_rename_media() {
        check_ajax_referer('switchboard_rename_nonce', 'nonce');

        if (!current_user_can('upload_files')) {
            wp_send_json_error(['message' => __('Permission denied', 'wpswitchboard')]);
        }

        $media_id = isset($_POST['media_id']) ? intval($_POST['media_id']) : 0;
        $new_filename = isset($_POST['new_filename']) ? sanitize_text_field($_POST['new_filename']) : '';
        $update_title = isset($_POST['update_title']) && $_POST['update_title'] === 'yes';
        $update_slug = isset($_POST['update_slug']) && $_POST['update_slug'] === 'yes';

        if (!$media_id || !$new_filename) {
            wp_send_json_error(['message' => __('Invalid parameters', 'wpswitchboard')]);
        }

        // Verify user can edit this specific attachment
        if (!current_user_can('edit_post', $media_id)) {
            wp_send_json_error(['message' => __('You do not have permission to rename this file', 'wpswitchboard')]);
        }

        // Verify it's actually an attachment
        $post = get_post($media_id);
        if (!$post || $post->post_type !== 'attachment') {
            wp_send_json_error(['message' => __('Invalid media ID', 'wpswitchboard')]);
        }

        // Limit filename length to prevent abuse
        if (strlen($new_filename) > 200) {
            wp_send_json_error(['message' => __('Filename is too long', 'wpswitchboard')]);
        }

        $result = $this->rename_media_file($media_id, $new_filename, [
            'update_title' => $update_title,
            'update_slug' => $update_slug,
            'prefix' => '',
            'suffix' => '',
            'counter' => 0
        ]);

        if (is_wp_error($result)) {
            wp_send_json_error(['message' => $result->get_error_message()]);
        }

        wp_send_json_success(['message' => __('File renamed successfully!', 'wpswitchboard')]);
    }

    /**
     * AJAX: Undo rename
     */
    public function ajax_undo_rename() {
        check_ajax_referer('switchboard_rename_nonce', 'nonce');

        if (!current_user_can('upload_files')) {
            wp_send_json_error(['message' => __('Permission denied', 'wpswitchboard')]);
        }

        $media_id = isset($_POST['media_id']) ? intval($_POST['media_id']) : 0;

        if (!$media_id) {
            wp_send_json_error(['message' => __('Invalid media ID', 'wpswitchboard')]);
        }

        // Verify user can edit this specific attachment
        if (!current_user_can('edit_post', $media_id)) {
            wp_send_json_error(['message' => __('You do not have permission to undo rename for this file', 'wpswitchboard')]);
        }

        // Verify it's actually an attachment
        $post = get_post($media_id);
        if (!$post || $post->post_type !== 'attachment') {
            wp_send_json_error(['message' => __('Invalid media ID', 'wpswitchboard')]);
        }

        $result = $this->undo_rename($media_id);

        if (is_wp_error($result)) {
            wp_send_json_error(['message' => $result->get_error_message()]);
        }

        wp_send_json_success(['message' => __('Rename undone successfully!', 'wpswitchboard')]);
    }

    /**
     * AJAX: Get bulk rename form
     */
    public function ajax_get_bulk_rename_form() {
        // Verify nonce from bulk action
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'bulk-media')) {
            wp_send_json_error(['message' => __('Security check failed', 'wpswitchboard')]);
        }

        if (!current_user_can('upload_files')) {
            wp_send_json_error(['message' => __('Permission denied', 'wpswitchboard')]);
        }

        $media_ids = isset($_POST['media_ids']) && is_array($_POST['media_ids'])
            ? array_map('intval', $_POST['media_ids'])
            : [];

        if (empty($media_ids)) {
            wp_send_json_error(['message' => __('No media files selected', 'wpswitchboard')]);
        }

        // Verify user has permission for all selected media
        foreach ($media_ids as $media_id) {
            if (!current_user_can('edit_post', $media_id)) {
                wp_send_json_error(['message' => __('You do not have permission to rename some of the selected files', 'wpswitchboard')]);
            }
        }

        // Limit bulk operations to prevent abuse
        if (count($media_ids) > 200) {
            wp_send_json_error(['message' => __('Maximum 200 files can be renamed at once', 'wpswitchboard')]);
        }

        ob_start();
        ?>
        <div style="margin-bottom: 20px;">
            <p style="margin: 0; color: #1e293b;"><strong><?php printf(_n('Renaming %s file', 'Renaming %s files', count($media_ids), 'wpswitchboard'), count($media_ids)); ?></strong></p>
        </div>

        <div style="margin-bottom: 20px;">
            <label for="switchboard-bulk-pattern" style="display: block; font-weight: 600; margin-bottom: 6px; color: #1e293b;"><?php esc_html_e('Renaming Pattern:', 'wpswitchboard'); ?></label>
            <input type="text" id="switchboard-bulk-pattern" value="<?php echo esc_attr($this->settings['default_pattern']); ?>" style="width: 100%; padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px;">
            <?php $media_categories_enabled = in_array('media-categories', get_option('switchboard_active_modules', [])); ?>
            <div style="margin-top: 8px; display: flex; flex-wrap: wrap; gap: 6px;">
                <span class="switchboard-var-badge" data-var="{title}" style="display: inline-block; padding: 4px 10px; background: #f1f5f9; border: 1px solid #e2e8f0; border-radius: 4px; font-size: 12px; color: #475569; cursor: pointer; transition: all 0.15s;">{title}</span>
                <span class="switchboard-var-badge" data-var="{id}" style="display: inline-block; padding: 4px 10px; background: #f1f5f9; border: 1px solid #e2e8f0; border-radius: 4px; font-size: 12px; color: #475569; cursor: pointer; transition: all 0.15s;">{id}</span>
                <span class="switchboard-var-badge" data-var="{date}" style="display: inline-block; padding: 4px 10px; background: #f1f5f9; border: 1px solid #e2e8f0; border-radius: 4px; font-size: 12px; color: #475569; cursor: pointer; transition: all 0.15s;">{date}</span>
                <span class="switchboard-var-badge" data-var="{counter}" style="display: inline-block; padding: 4px 10px; background: #f1f5f9; border: 1px solid #e2e8f0; border-radius: 4px; font-size: 12px; color: #475569; cursor: pointer; transition: all 0.15s;">{counter}</span>
                <span class="switchboard-var-badge" data-var="{post-title}" style="display: inline-block; padding: 4px 10px; background: #f1f5f9; border: 1px solid #e2e8f0; border-radius: 4px; font-size: 12px; color: #475569; cursor: pointer; transition: all 0.15s;">{post-title}</span>
                <?php if ($media_categories_enabled): ?>
                    <span class="switchboard-var-badge" data-var="{category}" style="display: inline-block; padding: 4px 10px; background: #f1f5f9; border: 1px solid #e2e8f0; border-radius: 4px; font-size: 12px; color: #475569; cursor: pointer; transition: all 0.15s;">{category}</span>
                <?php else: ?>
                    <span class="switchboard-var-badge-disabled" style="display: inline-block; padding: 4px 10px; background: #f1f5f9; border: 1px solid #e2e8f0; border-radius: 4px; font-size: 12px; color: #475569; opacity: 0.5; cursor: not-allowed; position: relative;">{category}<span class="sb-tooltip"><?php esc_html_e('Enable Media Categories module', 'wpswitchboard'); ?></span></span>
                <?php endif; ?>
            </div>
            <p style="margin: 8px 0 0; font-size: 12px; color: #9ca3af;"><?php esc_html_e('Click to insert variable', 'wpswitchboard'); ?></p>
        </div>

        <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 20px;">
            <div>
                <label for="switchboard-bulk-prefix" style="display: block; font-weight: 600; margin-bottom: 6px; color: #1e293b;"><?php esc_html_e('Prefix:', 'wpswitchboard'); ?></label>
                <input type="text" id="switchboard-bulk-prefix" placeholder="e.g., product-" style="width: 100%; padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px;">
            </div>
            <div>
                <label for="switchboard-bulk-suffix" style="display: block; font-weight: 600; margin-bottom: 6px; color: #1e293b;"><?php esc_html_e('Suffix:', 'wpswitchboard'); ?></label>
                <input type="text" id="switchboard-bulk-suffix" placeholder="e.g., -thumb" style="width: 100%; padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px;">
            </div>
        </div>

        <div style="margin-bottom: 20px;">
            <label style="display: flex; align-items: center; gap: 8px; cursor: pointer; margin-bottom: 10px;">
                <input type="checkbox" id="switchboard-bulk-update-titles" checked style="width: 16px; height: 16px;">
                <span style="color: #374151;"><?php esc_html_e('Update media titles to match filenames', 'wpswitchboard'); ?></span>
            </label>
            <label style="display: flex; align-items: center; gap: 8px; cursor: pointer;">
                <input type="checkbox" id="switchboard-bulk-update-slugs" checked style="width: 16px; height: 16px;">
                <span style="color: #374151;"><?php esc_html_e('Update permalink slugs to match filenames', 'wpswitchboard'); ?></span>
            </label>
        </div>

        <div style="background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 6px; padding: 12px 16px;">
            <p style="margin: 0; font-size: 13px; color: #1e40af;">
                <strong><?php esc_html_e('Example:', 'wpswitchboard'); ?></strong><br>
                <?php esc_html_e('Pattern:', 'wpswitchboard'); ?> <code style="background: #dbeafe; padding: 2px 6px; border-radius: 3px;">product-{counter}</code><br>
                <?php esc_html_e('Result:', 'wpswitchboard'); ?> product-001.jpg, product-002.jpg, product-003.jpg
            </p>
        </div>
        <?php

        $html = ob_get_clean();
        wp_send_json_success(['html' => $html]);
    }

    /**
     * AJAX: Process bulk rename (one file at a time)
     */
    public function ajax_process_bulk_rename() {
        // Verify nonce from bulk action
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'bulk-media')) {
            wp_send_json_error(['message' => __('Security check failed', 'wpswitchboard')]);
        }

        if (!current_user_can('upload_files')) {
            wp_send_json_error(['message' => __('Permission denied', 'wpswitchboard')]);
        }

        $media_id = isset($_POST['media_id']) ? intval($_POST['media_id']) : 0;

        if (!$media_id) {
            wp_send_json_error(['message' => __('Invalid media ID', 'wpswitchboard')]);
        }

        // Verify user can edit this specific attachment
        if (!current_user_can('edit_post', $media_id)) {
            wp_send_json_error(['message' => __('You do not have permission to rename this file', 'wpswitchboard')]);
        }

        // Verify it's actually an attachment
        $post = get_post($media_id);
        if (!$post || $post->post_type !== 'attachment') {
            wp_send_json_error(['message' => __('Invalid media ID', 'wpswitchboard')]);
        }

        $pattern = isset($_POST['pattern']) ? sanitize_text_field($_POST['pattern']) : '';
        $prefix = isset($_POST['prefix']) ? sanitize_text_field($_POST['prefix']) : '';
        $suffix = isset($_POST['suffix']) ? sanitize_text_field($_POST['suffix']) : '';
        $update_title = isset($_POST['update_title']) && $_POST['update_title'] === 'yes';
        $update_slug = isset($_POST['update_slug']) && $_POST['update_slug'] === 'yes';
        $counter = isset($_POST['counter']) ? intval($_POST['counter']) : 0;

        // Limit pattern length to prevent abuse
        if (strlen($pattern) > 200 || strlen($prefix) > 50 || strlen($suffix) > 50) {
            wp_send_json_error(['message' => __('Pattern, prefix, or suffix is too long', 'wpswitchboard')]);
        }

        // Generate new filename from pattern
        $new_filename = $this->generate_filename_from_pattern($media_id, $pattern, $counter);

        // Add prefix and suffix
        if ($prefix) {
            $new_filename = $prefix . $new_filename;
        }
        if ($suffix) {
            $new_filename = $new_filename . $suffix;
        }

        $result = $this->rename_media_file($media_id, $new_filename, [
            'update_title' => $update_title,
            'update_slug' => $update_slug,
            'prefix' => $prefix,
            'suffix' => $suffix,
            'counter' => $counter,
            'is_bulk' => true
        ]);

        if (is_wp_error($result)) {
            wp_send_json_error(['message' => $result->get_error_message()]);
        }

        // Handle skipped files (excluded types)
        if (is_array($result) && isset($result['skipped']) && $result['skipped']) {
            wp_send_json_success([
                'message' => sprintf(__('Skipped (.%s excluded)', 'wpswitchboard'), $result['extension']),
                'skipped' => true,
                'reason' => $result['reason'],
                'extension' => $result['extension']
            ]);
        }

        wp_send_json_success(['message' => sprintf(__('Renamed to: %s', 'wpswitchboard'), $result['new_filename'])]);
    }

    /**
     * Generate filename from pattern
     */
    private function generate_filename_from_pattern($media_id, $pattern, $counter = 0) {
        if (empty($pattern)) {
            $pattern = '{title}';
        }

        $post = get_post($media_id);
        $title = get_the_title($media_id);

        // Get attached post title if available
        $parent_id = $post->post_parent;
        $post_title = '';
        if ($parent_id) {
            $post_title = get_the_title($parent_id);
        }

        // Get upload date (post_date of the attachment)
        $upload_date = get_the_date('Y-m-d', $media_id);
        if (!$upload_date) {
            $upload_date = date('Y-m-d'); // Fallback to current date if unavailable
        }

        // Get media category if available
        $category = '';
        $categories = wp_get_object_terms($media_id, 'media_category', ['fields' => 'names']);
        if (!is_wp_error($categories) && !empty($categories)) {
            $category = sanitize_title($categories[0]);
        }

        // Replace variables
        $filename = $pattern;
        $filename = str_replace('{title}', $title, $filename);
        $filename = str_replace('{id}', $media_id, $filename);
        $filename = str_replace('{date}', $upload_date, $filename);
        $filename = str_replace('{counter}', str_pad($counter, 3, '0', STR_PAD_LEFT), $filename);
        $filename = str_replace('{post-title}', $post_title, $filename);
        $filename = str_replace('{category}', $category, $filename);

        return $filename;
    }

    /**
     * Core rename functionality
     */
    private function rename_media_file($media_id, $new_filename, $options = []) {
        $options = wp_parse_args($options, [
            'update_title' => false,
            'update_slug' => false,
            'prefix' => '',
            'suffix' => '',
            'counter' => 0,
            'is_bulk' => false
        ]);

        // Get current file
        $old_file = get_attached_file($media_id);
        if (!$old_file || !file_exists($old_file)) {
            return new WP_Error('file_not_found', __('Original file not found', 'wpswitchboard'));
        }

        // Check file type exclusions
        $file_info = pathinfo($old_file);
        $extension = isset($file_info['extension']) ? strtolower($file_info['extension']) : '';

        // SECURITY: Block dangerous file extensions
        $dangerous_extensions = ['php', 'php3', 'php4', 'php5', 'phtml', 'phar', 'exe', 'com', 'bat', 'cmd', 'sh', 'cgi', 'pl', 'py', 'rb', 'js', 'vbs', 'asp', 'aspx', 'jsp'];
        if (in_array($extension, $dangerous_extensions)) {
            return new WP_Error('dangerous_type', __('Security error: Cannot rename executable or script files', 'wpswitchboard'));
        }

        $excluded_types = $this->settings['excluded_types'];
        if ($excluded_types) {
            $excluded = array_map('trim', array_map('strtolower', explode(',', $excluded_types)));
            if (in_array($extension, $excluded)) {
                // In bulk mode, silently skip excluded types
                if ($options['is_bulk']) {
                    return [
                        'skipped' => true,
                        'reason' => 'excluded_type',
                        'extension' => $extension
                    ];
                }
                return new WP_Error('excluded_type', __('This file type is excluded from renaming', 'wpswitchboard'));
            }
        }

        // Sanitize new filename
        $new_filename = $this->sanitize_filename($new_filename);

        // Apply case transformation
        $new_filename = $this->apply_case_transform($new_filename);

        // Limit length
        $max_length = intval($this->settings['max_filename_length']);
        if (strlen($new_filename) > $max_length) {
            $new_filename = substr($new_filename, 0, $max_length);
            $new_filename = trim($new_filename, '-');
        }

        // Add unique ID if enabled
        if ($this->settings['add_unique_id'] === 'yes') {
            $new_filename .= '-' . substr(md5(uniqid(mt_rand(), true)), 0, 3);
        }

        // Add extension
        if ($extension) {
            $new_filename .= '.' . $extension;
        }

        // Build new file path
        $upload_dir = dirname($old_file);
        $new_file = $upload_dir . '/' . $new_filename;

        // SECURITY: Validate that new file path is within uploads directory
        $wp_upload_dir = wp_upload_dir();
        $uploads_base = realpath($wp_upload_dir['basedir']);
        $new_file_real = realpath($upload_dir) . '/' . $new_filename;

        if (strpos($new_file_real, $uploads_base) !== 0) {
            return new WP_Error('invalid_path', __('Security error: Invalid file path', 'wpswitchboard'));
        }

        // SECURITY: Prevent directory traversal in filename
        if (strpos($new_filename, '..') !== false || strpos($new_filename, '/') !== false || strpos($new_filename, '\\') !== false) {
            return new WP_Error('invalid_filename', __('Security error: Invalid filename characters detected', 'wpswitchboard'));
        }

        // Check if file already exists
        if (file_exists($new_file) && $new_file !== $old_file) {
            // Auto-serialize if conflict
            $i = 1;
            $name_without_ext = pathinfo($new_filename, PATHINFO_FILENAME);
            while (file_exists($new_file)) {
                $new_filename = $name_without_ext . '-' . $i . ($extension ? '.' . $extension : '');
                $new_file = $upload_dir . '/' . $new_filename;
                $i++;
            }
        }

        // Store undo data
        $undo_data = [
            'old_file' => basename($old_file),
            'new_file' => $new_filename,
            'timestamp' => current_time('timestamp'),
            'old_url' => wp_get_attachment_url($media_id)
        ];
        update_post_meta($media_id, '_switchboard_rename_undo', $undo_data);

        // Get old URL before renaming
        $old_url = wp_get_attachment_url($media_id);

        // Rename the file
        if (!rename($old_file, $new_file)) {
            return new WP_Error('rename_failed', __('Failed to rename file on disk', 'wpswitchboard'));
        }

        // Update attachment file path
        update_attached_file($media_id, $new_file);

        // Regenerate thumbnails
        require_once(ABSPATH . 'wp-admin/includes/image.php');
        $metadata = wp_get_attachment_metadata($media_id);

        // Delete old thumbnails
        if (isset($metadata['sizes']) && is_array($metadata['sizes'])) {
            foreach ($metadata['sizes'] as $size) {
                if (isset($size['file'])) {
                    $thumb_file = $upload_dir . '/' . $size['file'];
                    if (file_exists($thumb_file)) {
                        @unlink($thumb_file);
                    }
                }
            }
        }

        // Generate new metadata
        $new_metadata = wp_generate_attachment_metadata($media_id, $new_file);
        wp_update_attachment_metadata($media_id, $new_metadata);

        // Update title and/or slug if requested
        $post_update = ['ID' => $media_id];
        $filename_without_ext = pathinfo($new_filename, PATHINFO_FILENAME);

        if ($options['update_title']) {
            $title = str_replace(['-', '_'], ' ', $filename_without_ext);
            $title = ucwords($title);
            $post_update['post_title'] = $title;
        }

        if ($options['update_slug']) {
            $post_update['post_name'] = sanitize_title($filename_without_ext);
        }

        if (count($post_update) > 1) {
            wp_update_post($post_update);
        }

        // Get new URL
        $new_url = wp_get_attachment_url($media_id);

        // Update all references
        $this->update_references($media_id, $old_url, $new_url);

        // Create redirect if enabled
        if ($this->settings['auto_redirect'] === 'yes') {
            $this->create_redirect($old_url, $new_url);
        }

        // Clear caches
        clean_attachment_cache($media_id);

        // Add to history
        $history = get_post_meta($media_id, '_switchboard_rename_history', true);
        if (!is_array($history)) {
            $history = [];
        }
        $history[] = [
            'from' => basename($old_file),
            'to' => $new_filename,
            'date' => current_time('mysql')
        ];
        update_post_meta($media_id, '_switchboard_rename_history', $history);

        // Fire action hook
        do_action('switchboard_media_renamed', $media_id, $old_file, $new_file);

        return [
            'old_filename' => basename($old_file),
            'new_filename' => $new_filename,
            'old_url' => $old_url,
            'new_url' => $new_url
        ];
    }

    /**
     * Sanitize filename
     */
    private function sanitize_filename($filename) {
        // SECURITY: Block null bytes
        $filename = str_replace(chr(0), '', $filename);

        // Remove extension if present
        $filename = pathinfo($filename, PATHINFO_FILENAME);

        // SECURITY: Remove any path separators
        $filename = str_replace(['/', '\\', '..'], '', $filename);

        // Handle accents
        if ($this->settings['preserve_accents'] !== 'yes') {
            $filename = remove_accents($filename);
        }

        // SECURITY: Strict whitelist - only allow alphanumeric, dash, underscore, dot
        $filename = preg_replace('/[^a-zA-Z0-9\-_.]/', '-', $filename);

        // Remove multiple dashes
        $filename = preg_replace('/-+/', '-', $filename);

        // Remove multiple dots to prevent double extension attacks
        $filename = preg_replace('/\.+/', '-', $filename);

        // Trim dashes and dots
        $filename = trim($filename, '-._');

        // SECURITY: Prevent reserved filenames (Windows)
        $reserved = ['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9'];
        if (in_array(strtoupper($filename), $reserved)) {
            $filename = 'file-' . $filename;
        }

        // Prevent empty
        if (empty($filename) || strlen($filename) < 1) {
            $filename = 'file-' . wp_generate_password(8, false);
        }

        return $filename;
    }

    /**
     * Apply case transformation
     */
    private function apply_case_transform($filename) {
        switch ($this->settings['case_transform']) {
            case 'lowercase':
                return strtolower($filename);
            case 'uppercase':
                return strtoupper($filename);
            case 'titlecase':
                // Title case: capitalize first letter of each word segment (separated by - or _)
                // Preserve the separators to maintain valid filename
                $parts = preg_split('/(-|_)/', $filename, -1, PREG_SPLIT_DELIM_CAPTURE);
                $result = '';
                foreach ($parts as $part) {
                    if ($part === '-' || $part === '_') {
                        $result .= $part;
                    } else {
                        $result .= ucfirst(strtolower($part));
                    }
                }
                return $result;
            default:
                return $filename;
        }
    }

    /**
     * Find all references to this media file
     */
    private function find_references($media_id) {
        global $wpdb;

        $url = wp_get_attachment_url($media_id);
        $filename = basename(get_attached_file($media_id));

        $references = [
            'posts' => [],
            'postmeta' => [],
            'options' => []
        ];

        // Search in post content
        $posts = $wpdb->get_results($wpdb->prepare(
            "SELECT ID, post_title FROM {$wpdb->posts} WHERE post_content LIKE %s AND post_type IN ('post', 'page')",
            '%' . $wpdb->esc_like($filename) . '%'
        ));

        foreach ($posts as $post) {
            $references['posts'][] = [
                'id' => absint($post->ID),
                'title' => sanitize_text_field($post->post_title)
            ];
        }

        // Search in post meta (limit to 50 for performance)
        $meta = $wpdb->get_results($wpdb->prepare(
            "SELECT post_id, meta_key FROM {$wpdb->postmeta} WHERE meta_value LIKE %s LIMIT 50",
            '%' . $wpdb->esc_like($filename) . '%'
        ));

        foreach ($meta as $m) {
            $references['postmeta'][] = [
                'post_id' => absint($m->post_id),
                'key' => sanitize_text_field($m->meta_key)
            ];
        }

        return $references;
    }

    /**
     * Update all references to the media file
     */
    private function update_references($media_id, $old_url, $new_url) {
        global $wpdb;

        // Get the path portion (e.g., /wp-content/uploads/2024/01/photo.jpg)
        $old_path = wp_parse_url($old_url, PHP_URL_PATH);
        $new_path = wp_parse_url($new_url, PHP_URL_PATH);

        // Update post content - full URL first (most precise)
        $wpdb->query($wpdb->prepare(
            "UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s) WHERE post_content LIKE %s",
            $old_url,
            $new_url,
            '%' . $wpdb->esc_like($old_url) . '%'
        ));

        // Update post content - path only (for protocol-relative or path-based references)
        if ($old_path && $new_path) {
            $wpdb->query($wpdb->prepare(
                "UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s) WHERE post_content LIKE %s",
                $old_path,
                $new_path,
                '%' . $wpdb->esc_like($old_path) . '%'
            ));
        }

        // Update post meta - full URL
        $wpdb->query($wpdb->prepare(
            "UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_value LIKE %s",
            $old_url,
            $new_url,
            '%' . $wpdb->esc_like($old_url) . '%'
        ));

        // Update post meta - path only
        if ($old_path && $new_path) {
            $wpdb->query($wpdb->prepare(
                "UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_value LIKE %s",
                $old_path,
                $new_path,
                '%' . $wpdb->esc_like($old_path) . '%'
            ));
        }

        // Update options (for widgets, theme settings, etc.) - full URL
        $wpdb->query($wpdb->prepare(
            "UPDATE {$wpdb->options} SET option_value = REPLACE(option_value, %s, %s) WHERE option_value LIKE %s AND option_name NOT LIKE %s",
            $old_url,
            $new_url,
            '%' . $wpdb->esc_like($old_url) . '%',
            '_transient_%'
        ));

        // Update options - path only
        if ($old_path && $new_path) {
            $wpdb->query($wpdb->prepare(
                "UPDATE {$wpdb->options} SET option_value = REPLACE(option_value, %s, %s) WHERE option_value LIKE %s AND option_name NOT LIKE %s",
                $old_path,
                $new_path,
                '%' . $wpdb->esc_like($old_path) . '%',
                '_transient_%'
            ));
        }

        // Clear all caches
        wp_cache_flush();
    }

    /**
     * Create 301 redirect (database storage)
     */
    private function create_redirect($old_url, $new_url, $media_id = null) {
        global $wpdb;

        // Parse URLs to get paths
        $old_path = wp_parse_url($old_url, PHP_URL_PATH);
        $new_path = wp_parse_url($new_url, PHP_URL_PATH);

        if (!$old_path || !$new_path || $old_path === $new_path) {
            return false;
        }

        // Check if redirect already exists for this old path
        $existing = $wpdb->get_var($wpdb->prepare(
            "SELECT id FROM {$this->table_name} WHERE old_path = %s",
            $old_path
        ));

        if ($existing) {
            // Update existing redirect
            $wpdb->update(
                $this->table_name,
                [
                    'new_path' => $new_path,
                    'media_id' => $media_id,
                    'created_at' => current_time('mysql')
                ],
                ['id' => $existing],
                ['%s', '%d', '%s'],
                ['%d']
            );
        } else {
            // Insert new redirect
            $wpdb->insert(
                $this->table_name,
                [
                    'old_path' => $old_path,
                    'new_path' => $new_path,
                    'media_id' => $media_id,
                    'hits' => 0,
                    'created_at' => current_time('mysql')
                ],
                ['%s', '%s', '%d', '%d', '%s']
            );
        }

        return true;
    }

    /**
     * Handle media redirects on frontend
     */
    public function handle_media_redirects() {
        global $wpdb;

        $request_uri = $_SERVER['REQUEST_URI'] ?? '';
        $request_path = wp_parse_url($request_uri, PHP_URL_PATH);

        if (!$request_path) {
            return;
        }

        // Check if this path has a redirect in the database
        $redirect = $wpdb->get_row($wpdb->prepare(
            "SELECT id, new_path FROM {$this->table_name} WHERE old_path = %s",
            $request_path
        ));

        if ($redirect) {
            $new_url = home_url($redirect->new_path);

            // Increment hit counter
            $wpdb->query($wpdb->prepare(
                "UPDATE {$this->table_name} SET hits = hits + 1 WHERE id = %d",
                $redirect->id
            ));

            // Perform 301 redirect
            wp_redirect($new_url, 301);
            exit;
        }
    }

    /**
     * AJAX: Delete a single redirect
     */
    public function ajax_delete_redirect() {
        check_ajax_referer('switchboard_nonce', 'nonce');

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

        global $wpdb;
        $redirect_id = absint($_POST['redirect_id'] ?? 0);

        if (!$redirect_id) {
            wp_send_json_error(['message' => __('Invalid redirect ID', 'wpswitchboard')]);
        }

        $deleted = $wpdb->delete($this->table_name, ['id' => $redirect_id], ['%d']);

        if ($deleted) {
            wp_send_json_success(['message' => __('Redirect deleted', 'wpswitchboard')]);
        } else {
            wp_send_json_error(['message' => __('Failed to delete redirect', 'wpswitchboard')]);
        }
    }

    /**
     * AJAX: Clear all redirects
     */
    public function ajax_clear_all_redirects() {
        check_ajax_referer('switchboard_nonce', 'nonce');

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

        global $wpdb;
        $wpdb->query("TRUNCATE TABLE {$this->table_name}");

        wp_send_json_success(['message' => __('All redirects cleared', 'wpswitchboard')]);
    }

    /**
     * Get all redirects for display
     */
    public function get_redirects($limit = 50, $offset = 0) {
        global $wpdb;

        return $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$this->table_name} ORDER BY created_at DESC LIMIT %d OFFSET %d",
            $limit,
            $offset
        ));
    }

    /**
     * Get total redirect count
     */
    public function get_redirect_count() {
        global $wpdb;
        return (int) $wpdb->get_var("SELECT COUNT(*) FROM {$this->table_name}");
    }

    /**
     * Undo last rename
     */
    private function undo_rename($media_id) {
        $undo_data = get_post_meta($media_id, '_switchboard_rename_undo', true);

        if (!$undo_data || !isset($undo_data['old_file'])) {
            return new WP_Error('no_undo_data', __('No undo data available', 'wpswitchboard'));
        }

        // Check if undo is still valid (within 24 hours)
        if ((current_time('timestamp') - $undo_data['timestamp']) > 86400) {
            delete_post_meta($media_id, '_switchboard_rename_undo');
            return new WP_Error('undo_expired', __('Undo data has expired (24 hours)', 'wpswitchboard'));
        }

        $current_file = get_attached_file($media_id);
        $upload_dir = dirname($current_file);
        $old_filename = basename($undo_data['old_file']);

        // SECURITY: Validate old filename
        if (strpos($old_filename, '..') !== false || strpos($old_filename, '/') !== false || strpos($old_filename, '\\') !== false) {
            return new WP_Error('invalid_filename', __('Security error: Invalid filename in undo data', 'wpswitchboard'));
        }

        $old_file = $upload_dir . '/' . $old_filename;

        // SECURITY: Validate that old file path is within uploads directory
        $wp_upload_dir = wp_upload_dir();
        $uploads_base = realpath($wp_upload_dir['basedir']);
        $old_file_real = realpath($upload_dir) . '/' . $old_filename;

        if (strpos($old_file_real, $uploads_base) !== 0) {
            return new WP_Error('invalid_path', __('Security error: Invalid file path', 'wpswitchboard'));
        }

        // Check if old filename is available
        if (file_exists($old_file)) {
            return new WP_Error('file_exists', __('Original filename is now in use by another file', 'wpswitchboard'));
        }

        // Rename back
        if (!rename($current_file, $old_file)) {
            return new WP_Error('rename_failed', __('Failed to restore original filename', 'wpswitchboard'));
        }

        // Update attachment
        update_attached_file($media_id, $old_file);

        // Regenerate metadata
        require_once(ABSPATH . 'wp-admin/includes/image.php');
        $metadata = wp_generate_attachment_metadata($media_id, $old_file);
        wp_update_attachment_metadata($media_id, $metadata);

        // Get URLs
        $new_url = wp_get_attachment_url($media_id);
        $old_url = $undo_data['old_url'];

        // Update references back
        $this->update_references($media_id, basename($undo_data['new_file']), basename($undo_data['old_file']));

        // Clear undo data
        delete_post_meta($media_id, '_switchboard_rename_undo');

        // Clear caches
        clean_attachment_cache($media_id);

        return true;
    }

    /**
     * Cleanup old undo data (runs daily)
     */
    public function cleanup_old_undo_data() {
        global $wpdb;

        // Find all attachments with undo data older than 24 hours
        $old_undos = $wpdb->get_results($wpdb->prepare(
            "SELECT post_id, meta_value FROM {$wpdb->postmeta} WHERE meta_key = %s",
            '_switchboard_rename_undo'
        ));

        foreach ($old_undos as $row) {
            $undo_data = maybe_unserialize($row->meta_value);
            if (isset($undo_data['timestamp']) && (current_time('timestamp') - $undo_data['timestamp']) > 86400) {
                delete_post_meta($row->post_id, '_switchboard_rename_undo');
            }
        }
    }
}

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