<?php
/**
 * Media Manager Page for StaticQ
 * 
 * Unified page combining Dashboard, Registration, and Health Check
 * 
 * @package suspended-cloud/cloudflare-media-offload
 * @since 2.0.0
 */

if (!defined('ABSPATH')) {
    exit;
}

class SQMedia_Media_Manager_Page {

    /**
     * Active registration batch (if any)
     */
    private $active_batch = null;

    /**
     * Parent admin instance
     */
    private $admin;

    public function __construct(SQMedia_Admin $admin) {
        $this->admin = $admin;
    }

    /**
     * Render the page
     */
    public function render(): void {
        $db = sqmedia()->get_database();
        
        // Get stats
        $total_wp_attachments = $this->count_wp_image_attachments();
        $registered_count = $db->get_total_record_count();
        $unregistered_count = max(0, $total_wp_attachments - $registered_count);
        
        // Check for running orphan scan
        $running_orphan_scan = $db->get_running_scan('orphan');
        $running_orphan_scan_id = null;
        
        // Debug logging
        if ($running_orphan_scan) {
        }
        
        if ($running_orphan_scan && $running_orphan_scan->status === 'running') {
            // Verify state file exists (file-based storage)
            $upload_dir = wp_upload_dir();
            $state_file = $upload_dir['basedir'] . '/staticq_scans/orphans/' . $running_orphan_scan->scan_id . '/state.json';
            $state_exists = file_exists($state_file);
            
            if ($state_exists) {
                $running_orphan_scan_id = $running_orphan_scan->scan_id;
            } else {
                // Zombie scan - mark as failed
                $db->update_scan($running_orphan_scan->scan_id, [
                    'status' => 'failed',
                    'scan_completed' => current_time('mysql'),
                ]);
                wp_clear_scheduled_hook('sqmedia_continue_orphan_scan');
            }
        }
        
        // Get registration history and active batch via unified handler
        $reg_handler = new SQMedia_Registration_Handler();
        $batch_history = $reg_handler->get_history(10);

        // Check for active registration (checks both new scan system and legacy batches)
        $active_batch = $reg_handler->get_active_registration_compat();
        $this->active_batch = $active_batch;
        
        // Settings info
        $storage_mode = SQMedia_Settings::get_storage_mode();
        $cloud_configured = SQMedia_Settings::is_cloud_storage_configured();
        $cloudflare_enabled = SQMedia_Settings::is_cloudflare_enabled();
        $enable_webp = SQMedia_Settings::is_webp_enabled();
        ?>
        <div class="wrap sqmedia-media-manager">
            <h1><?php esc_html_e('Media Manager', 'staticq-media'); ?></h1>
            
            <?php // Overview section removed - counters now in Media Registration ?>
            
            <?php $this->render_current_settings($storage_mode, $cloud_configured, $cloudflare_enabled, $enable_webp); ?>
            
            <?php $this->render_registration_section($total_wp_attachments, $registered_count, $unregistered_count, $batch_history, $active_batch); ?>

            <?php // Global operation mutex — defined before any section JS renders ?>
            <?php ob_start(); ?>
            window.sqActiveOperation = null;
            window.sqStartOperation = function(name) {
                if (window.sqActiveOperation) {
                    alert(<?php echo wp_json_encode(__('Please wait — ', 'staticq-media')); ?> + '"' + window.sqActiveOperation + '"' + <?php echo wp_json_encode(__(' is currently running.', 'staticq-media')); ?>);
                    return false;
                }
                window.sqActiveOperation = name;
                return true;
            };
            window.sqEndOperation = function() {
                window.sqActiveOperation = null;
            };
            <?php wp_add_inline_script('sqmedia-admin', ob_get_clean()); ?>

            <?php
            // Use clean file state scanner renderer
            $renderer = new SQMedia_File_State_Renderer();
            $renderer->render();
            ?>

            <?php
            // Post Content Scanner
            $pcs_renderer = new SQMedia_Post_Content_Scanner_Renderer();
            $pcs_renderer->render();
            ?>

            <?php $this->render_orphan_detection_section(); ?>
            
        </div>
        
        <?php $this->render_scripts(); ?>
        <?php
    }
    
    /**
     * Count WordPress image attachments
     */
    private function count_wp_image_attachments(): int {
        global $wpdb;
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        return (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = 'attachment' AND post_mime_type LIKE 'image/%'"
        );
    }
    
    /**
     * Render current settings summary
     */
    private function render_current_settings(string $storage_mode, bool $cloud_configured, bool $cloudflare_enabled, bool $enable_webp): void {
        $enabled_sizes = SQMedia_Settings::get_enabled_sizes();
        $picture_delivery_enabled = SQMedia_Settings::is_picture_delivery_enabled();
        ?>
        <div class="sqmedia-section sqmedia-settings-summary">
            <h2>&#9881; <?php esc_html_e('Current Settings', 'staticq-media'); ?></h2>
            <div class="sqmedia-settings-grid">
                <div class="sqmedia-setting-item">
                    <strong><?php esc_html_e('Storage Mode', 'staticq-media'); ?></strong>
                    <?php
                    switch ($storage_mode) {
                        case 'local_only':
                            echo '&#128190; ' . esc_html__('Local Only', 'staticq-media');
                            break;
                        case 'cloud_and_local':
                            echo '&#128190;&#9729; ' . esc_html__('Cloud + Local', 'staticq-media');
                            break;
                        case 'cloud_only':
                            echo '&#9729; ' . esc_html__('Cloud Only', 'staticq-media');
                            break;
                    }
                    ?>
                </div>
                <div class="sqmedia-setting-item">
                    <strong><?php esc_html_e('Front-end Delivery', 'staticq-media'); ?></strong>
                    <?php if ($picture_delivery_enabled): ?>
                        <span style="color: #00a32a;">&#10003; <?php esc_html_e('Active', 'staticq-media'); ?></span>
                    <?php else: ?>
                        <span style="color: #d63638;">&#10007; <?php esc_html_e('Inactive', 'staticq-media'); ?></span>
                    <?php endif; ?>
                </div>
                <div class="sqmedia-setting-item">
                    <strong><?php esc_html_e('Cloud Storage', 'staticq-media'); ?></strong>
                    <?php echo $cloud_configured ? '&#10003; ' . esc_html__('Configured', 'staticq-media') : '&#10007; ' . esc_html__('Not configured', 'staticq-media'); ?>
                </div>
                <div class="sqmedia-setting-item">
                    <strong><?php esc_html_e('Cloudflare Resizing', 'staticq-media'); ?></strong>
                    <?php echo $cloudflare_enabled ? '&#10003; ' . esc_html__('Enabled', 'staticq-media') : '&#10007; ' . esc_html__('Disabled', 'staticq-media'); ?>
                </div>
                <div class="sqmedia-setting-item">
                    <strong><?php esc_html_e('WebP Generation', 'staticq-media'); ?></strong>
                    <?php echo $enable_webp ? '&#10003; ' . esc_html__('Enabled', 'staticq-media') : '&#10007; ' . esc_html__('Disabled', 'staticq-media'); ?>
                </div>
            </div>
            
            <?php if (!empty($enabled_sizes)): ?>
            <div class="sqmedia-sizes-list">
                <h4 style="display: flex; align-items: center; flex-wrap: wrap; gap: 8px;">
                    &#128208; <?php esc_html_e('Enabled Image Sizes', 'staticq-media'); ?>
                    (<?php echo (int) count($enabled_sizes); ?>)
                    <span style="font-weight: normal; font-size: 12px; color: #646970;">—</span>
                    <a href="<?php echo esc_url(admin_url('admin.php?page=sqmedia-settings')); ?>" style="font-weight: normal; font-size: 12px;">
                        <?php esc_html_e('StaticQ Settings', 'staticq-media'); ?>
                    </a>
                    <span style="font-weight: normal; font-size: 12px; color: #646970;">|</span>
                    <a href="<?php echo esc_url(admin_url('options-media.php')); ?>" style="font-weight: normal; font-size: 12px;">
                        <?php esc_html_e('WordPress Settings', 'staticq-media'); ?>
                    </a>
                </h4>
                <div class="sqmedia-sizes-badges">
                    <?php foreach ($enabled_sizes as $size_name => $size_config): ?>
                        <span class="sqmedia-size-badge">
                            <?php echo esc_html($size_name); ?>
                            <small>(<?php echo esc_html($size_config['width'] . '×' . $size_config['height']); ?>)</small>
                        </span>
                    <?php endforeach; ?>
                </div>
            </div>
            <?php endif; ?>
        </div>
        <?php
    }
    
    /**
     * Render registration section (Media Registration)
     */
    private function render_registration_section(int $total_attachments, int $registered_count, int $unregistered_count, array $batch_history, ?object $active_batch = null): void {
        $has_active_batch = $active_batch && $active_batch->status === 'running';
        
        // Get detailed stats including other media types
        $db = sqmedia()->get_database();
        $stats = $db->get_library_stats();
        $total_images = $stats['total_images'] ?? $total_attachments;
        $total_other = $stats['total_other'] ?? 0;
        $registered_images = $stats['registered_images'] ?? 0;
        $registered_other = $stats['registered_other'] ?? 0;
        $manage_all_media = $stats['manage_all_media'] ?? false;
        
        // Calculate unregistered properly
        $unregistered_images = max(0, $total_images - $registered_images);
        $unregistered_other = $manage_all_media ? max(0, $total_other - $registered_other) : 0;
        $total_unregistered = $unregistered_images + $unregistered_other;
        ?>
        <div id="sqmedia-library-preview" class="sqmedia-section sqmedia-registration">
            <h2>📚 <?php esc_html_e('Media Registration', 'staticq-media'); ?></h2>
            
            <p style="color: #646970; margin-bottom: 20px;">
                <?php esc_html_e('Registration scans your WordPress media library and creates tracking records for each attachment. This allows StaticQ to manage cloud uploads, generate WebP versions, and monitor file health. Registration is quick and does not modify your files — it only reads metadata from the database.', 'staticq-media'); ?>
            </p>
            
            <!-- Library Stats -->
            <div class="sqmedia-library-stats" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; flex-wrap: wrap; gap: 15px;">
                <div style="display: flex; gap: 15px; flex-wrap: wrap;">
                    <!-- Images -->
                    <div style="background: #f0f0f1; padding: 12px 20px; border-radius: 4px; text-align: center; min-width: 100px;">
                        <div style="font-size: 24px; font-weight: 600; color: #1d2327;"><?php echo esc_html(number_format_i18n($total_images)); ?></div>
                        <div style="font-size: 12px; color: #646970; text-transform: uppercase;"><?php esc_html_e('Images', 'staticq-media'); ?></div>
                    </div>
                    <div style="background: #d4edda; padding: 12px 20px; border-radius: 4px; text-align: center; min-width: 100px;">
                        <div style="font-size: 24px; font-weight: 600; color: #155724;"><?php echo esc_html(number_format_i18n($registered_images)); ?></div>
                        <div style="font-size: 12px; color: #155724; text-transform: uppercase;"><?php esc_html_e('Registered', 'staticq-media'); ?></div>
                    </div>
                    
                    <?php if ($manage_all_media || $total_other > 0 || $registered_other > 0): ?>
                    <!-- Other Media -->
                    <div style="background: #e7f3ff; padding: 12px 20px; border-radius: 4px; text-align: center; min-width: 100px; border-left: 3px solid #2271b1;">
                        <div style="font-size: 24px; font-weight: 600; color: #1d2327;"><?php echo esc_html(number_format_i18n($total_other)); ?></div>
                        <div style="font-size: 12px; color: #646970; text-transform: uppercase;"><?php esc_html_e('Other Media', 'staticq-media'); ?></div>
                    </div>
                    <div style="background: <?php echo esc_attr($registered_other > 0 ? '#d4edda' : '#f0f0f1'); ?>; padding: 12px 20px; border-radius: 4px; text-align: center; min-width: 100px;">
                        <div style="font-size: 24px; font-weight: 600; color: <?php echo esc_attr($registered_other > 0 ? '#155724' : '#646970'); ?>;"><?php echo esc_html(number_format_i18n($registered_other)); ?></div>
                        <div style="font-size: 12px; color: <?php echo esc_attr($registered_other > 0 ? '#155724' : '#646970'); ?>; text-transform: uppercase;"><?php esc_html_e('Registered', 'staticq-media'); ?></div>
                    </div>
                    <?php endif; ?>
                </div>
                
                <?php if ($total_unregistered > 0): ?>
                <div style="display: flex; gap: 15px; align-items: center; flex-wrap: wrap;">
                    <div style="background: #fff3cd; padding: 12px 20px; border-radius: 4px; text-align: center; min-width: 100px; border: 2px solid #ffc107;">
                        <div style="font-size: 24px; font-weight: 600; color: #856404;"><?php echo esc_html(number_format_i18n($total_unregistered)); ?></div>
                        <div style="font-size: 12px; color: #856404; text-transform: uppercase;"><?php esc_html_e('Unregistered', 'staticq-media'); ?></div>
                    </div>
                    <button type="button" class="button button-primary button-hero" id="sqmedia-start-registration" style="height: auto; padding: 10px 25px;" <?php echo $has_active_batch ? 'disabled' : ''; /* Values are hardcoded strings, safe to output. */ ?>>
                        <?php if ($has_active_batch): ?>
                            <?php esc_html_e('Registration in Progress...', 'staticq-media'); ?>
                        <?php else: ?>
                            <?php esc_html_e('Register Unregistered Attachments', 'staticq-media'); ?>
                        <?php endif; ?>
                    </button>
                </div>
                <?php else: ?>
                <div style="background: #d4edda; padding: 12px 20px; border-radius: 4px; text-align: center; min-width: 100px;">
                    <div style="font-size: 24px; font-weight: 600; color: #155724;">✓</div>
                    <div style="font-size: 12px; color: #155724; text-transform: uppercase;"><?php esc_html_e('All Registered', 'staticq-media'); ?></div>
                </div>
                <?php endif; ?>
            </div>
            
            <!-- Registration progress (visible if active batch) -->
            <div id="sqmedia-registration-progress" style="<?php echo $has_active_batch ? '' : 'display: none;'; /* Values are hardcoded strings, safe to output. */ ?> margin-top: 20px;">
                <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 15px;">
                    <span class="sqmedia-spinner"></span>
                    <span id="sqmedia-reg-progress-text" style="color: #666;">
                        <?php if ($has_active_batch): ?>
                            <?php echo esc_html($active_batch->processed_attachments); ?> / <?php echo esc_html($active_batch->total_attachments); ?> 
                            (<?php echo (int) ($active_batch->total_attachments > 0 ? round(($active_batch->processed_attachments / $active_batch->total_attachments) * 100) : 0); ?>%)
                            — <?php esc_html_e('Resuming...', 'staticq-media'); ?>
                        <?php else: ?>
                            <?php esc_html_e('Starting...', 'staticq-media'); ?>
                        <?php endif; ?>
                    </span>
                </div>
                
                <!-- Live stats during registration -->
                <div id="sqmedia-reg-live-stats" style="display: flex; gap: 20px; flex-wrap: wrap;">
                    <div style="background: #d4edda; padding: 10px 15px; border-radius: 4px; min-width: 100px; text-align: center;">
                        <div id="sqmedia-reg-live-registered" style="font-size: 20px; font-weight: 600; color: #155724;">0</div>
                        <div style="font-size: 11px; color: #155724; text-transform: uppercase;"><?php esc_html_e('Registered', 'staticq-media'); ?></div>
                    </div>
                    <div style="background: #fff3cd; padding: 10px 15px; border-radius: 4px; min-width: 100px; text-align: center;">
                        <div id="sqmedia-reg-live-skipped" style="font-size: 20px; font-weight: 600; color: #856404;">0</div>
                        <div style="font-size: 11px; color: #856404; text-transform: uppercase;"><?php esc_html_e('Skipped', 'staticq-media'); ?></div>
                    </div>
                    <div id="sqmedia-reg-live-errors-box" style="background: #f8d7da; padding: 10px 15px; border-radius: 4px; min-width: 100px; text-align: center; display: none;">
                        <div id="sqmedia-reg-live-errors" style="font-size: 20px; font-weight: 600; color: #721c24;">0</div>
                        <div style="font-size: 11px; color: #721c24; text-transform: uppercase;"><?php esc_html_e('Errors', 'staticq-media'); ?></div>
                    </div>
                </div>
                
                <!-- Recent registrations list -->
                <div id="sqmedia-reg-recent" style="margin-top: 15px; display: none;">
                    <h5 style="margin: 0 0 8px 0; font-size: 12px; color: #666; text-transform: uppercase;"><?php esc_html_e('Recent Activity', 'staticq-media'); ?></h5>
                    <div id="sqmedia-reg-recent-list" style="background: #f9f9f9; border-radius: 4px; padding: 10px; max-height: 150px; overflow-y: auto; font-size: 13px;">
                        <!-- Recent items will be prepended here -->
                    </div>
                </div>
            </div>
            
            <!-- Batch history -->
            <?php if (!empty($batch_history)): ?>
                <div class="sqmedia-batch-history" style="margin-top: 20px;">
                    <h4><?php esc_html_e('Registration History', 'staticq-media'); ?></h4>
                    <div class="sqmedia-batch-history-list">
                        <?php foreach ($batch_history as $index => $batch): ?>
                            <?php
                            $results = is_array($batch->results) ? $batch->results : (json_decode($batch->results ?? '', true) ?: []);
                            $reg = $results['registration'] ?? [];
                            $registered = $batch->registered_attachments ?? ($reg['registered'] ?? 0);
                            $skipped = $batch->skipped_attachments ?? ($reg['skipped'] ?? 0);
                            $errors = $batch->error_attachments ?? ($reg['errors'] ?? 0);
                            $error_details = $results['errors'] ?? ($reg['error_details'] ?? []);
                            $skipped_details = $results['skipped'] ?? ($reg['skipped_details'] ?? []);
                            $date = date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($batch->started_at ?? $batch->created_at ?? 'now'));
                            $batch_id = $batch->batch_id ?? $index;
                            $has_details = ($errors > 0) || ($skipped > 0);
                            ?>
                            <div class="sqmedia-batch-item" style="padding: 12px; border-bottom: 1px solid #eee; display: block; overflow: hidden;">
                                <div class="sqmedia-batch-summary" <?php echo $has_details ? 'style="cursor: pointer;"' : ''; /* Values are hardcoded strings, safe to output. */ ?> <?php echo $has_details ? 'onclick="document.getElementById(\'sqmedia-batch-details-' . esc_attr($batch_id) . '\').classList.toggle(\'sqmedia-hidden\'); this.querySelector(\'.sqmedia-expand-icon\').classList.toggle(\'sqmedia-rotated\');"' : ''; ?>>
                                    <?php if ($has_details): ?>
                                        <span class="sqmedia-expand-icon" style="display: inline-block; transition: transform 0.2s; margin-right: 8px;">▶</span>
                                    <?php endif; ?>
                                    <span style="font-weight: 600;"><?php echo esc_html($date); ?></span>
                                    <?php if ($batch->status !== 'completed'): ?>
                                        <span style="color: #666; margin-left: 8px;">(<?php echo esc_html($batch->status); ?>)</span>
                                    <?php endif; ?>
                                    <?php if ($registered > 0): ?>
                                        <span style="color: #155724; margin-left: 15px;">✓ <?php echo esc_html(number_format_i18n($registered)); ?> <?php esc_html_e('registered', 'staticq-media'); ?></span>
                                    <?php endif; ?>
                                    <?php if ($skipped > 0): ?>
                                        <span style="color: #856404; margin-left: 15px;">▼  <?php echo esc_html(number_format_i18n($skipped)); ?> <?php esc_html_e('skipped', 'staticq-media'); ?></span>
                                    <?php endif; ?>
                                    <?php if ($errors > 0): ?>
                                        <span style="color: #721c24; margin-left: 15px;">✔ <?php echo esc_html(number_format_i18n($errors)); ?> <?php esc_html_e('errors', 'staticq-media'); ?></span>
                                    <?php endif; ?>
                                </div>
                                
                                <?php if ($has_details): ?>
                                    <div id="sqmedia-batch-details-<?php echo esc_attr($batch_id); ?>" class="sqmedia-batch-details-panel sqmedia-hidden" style="margin-top: 15px;">
                                        
                                        <?php if ($skipped > 0): ?>
                                            <div style="margin-bottom: 15px; padding: 12px; background: #fff8e6; border: 1px solid #ffeeba; border-radius: 4px;">
                                                <strong style="color: #856404; display: block; margin-bottom: 10px;">▼  <?php esc_html_e('Skipped Files', 'staticq-media'); ?> (<?php echo esc_html($skipped); ?>)</strong>
                                                <?php if (!empty($skipped_details)): ?>
                                                    <table style="width: 100%; border-collapse: collapse; font-size: 13px;">
                                                        <thead>
                                                            <tr style="background: #fff3cd;">
                                                                <th style="padding: 8px; text-align: left; border-bottom: 1px solid #ffeeba; width: 100px;"><?php esc_html_e('Attachment', 'staticq-media'); ?></th>
                                                                <th style="padding: 8px; text-align: left; border-bottom: 1px solid #ffeeba;"><?php esc_html_e('File', 'staticq-media'); ?></th>
                                                                <th style="padding: 8px; text-align: left; border-bottom: 1px solid #ffeeba; width: 220px;"><?php esc_html_e('Reason', 'staticq-media'); ?></th>
                                                            </tr>
                                                        </thead>
                                                        <tbody>
                                                            <?php foreach (array_slice($skipped_details, 0, 50) as $skip): ?>
                                                                <?php 
                                                                $att_id = $skip['attachment_id'] ?? 0;
                                                                $file = $skip['file'] ?? '';
                                                                if (!$file && $att_id) {
                                                                    $file = get_post_meta($att_id, '_wp_attached_file', true) ?: '';
                                                                }
                                                                ?>
                                                                <tr>
                                                                    <td style="padding: 6px 8px; border-bottom: 1px solid #ffeeba;">
                                                                        <?php if ($att_id): ?>
                                                                            <a href="<?php echo esc_url(admin_url('post.php?post=' . $att_id . '&action=edit')); ?>" target="_blank">#<?php echo esc_html($att_id); ?></a>
                                                                        <?php else: ?>
                                                                            —
                                                                        <?php endif; ?>
                                                                    </td>
                                                                    <td style="padding: 6px 8px; border-bottom: 1px solid #ffeeba; font-family: monospace; font-size: 12px; word-break: break-all;">
                                                                        <?php echo esc_html($file ?: '—'); ?>
                                                                    </td>
                                                                    <td style="padding: 6px 8px; border-bottom: 1px solid #ffeeba; color: #856404;">
                                                                        <?php echo esc_html($skip['reason'] ?? __('Unknown reason', 'staticq-media')); ?>
                                                                    </td>
                                                                </tr>
                                                            <?php endforeach; ?>
                                                        </tbody>
                                                    </table>
                                                    <?php if (count($skipped_details) > 50): ?>
                                                        <p style="margin: 10px 0 0; color: #666; font-style: italic;">
                                                            <?php
                                                            /* translators: %d: total number of skipped items */
                                                            echo esc_html(sprintf(__('Showing first 50 of %d skipped', 'staticq-media'), count($skipped_details)));
                                                            ?>
                                                        </p>
                                                    <?php endif; ?>
                                                <?php else: ?>
                                                    <p style="margin: 0; color: #856404; font-style: italic;">
                                                        <?php esc_html_e('Details not recorded for this batch. Run a new registration to see skip reasons.', 'staticq-media'); ?>
                                                    </p>
                                                <?php endif; ?>
                                            </div>
                                        <?php endif; ?>
                                        
                                        <?php if ($errors > 0): ?>
                                            <div style="padding: 12px; background: #fff5f5; border: 1px solid #f5c6cb; border-radius: 4px;">
                                                <strong style="color: #721c24; display: block; margin-bottom: 10px;">✔ <?php esc_html_e('Registration Errors', 'staticq-media'); ?> (<?php echo esc_html($errors); ?>)</strong>
                                                <?php if (!empty($error_details)): ?>
                                                    <table style="width: 100%; border-collapse: collapse; font-size: 13px;">
                                                        <thead>
                                                            <tr style="background: #f8d7da;">
                                                                <th style="padding: 8px; text-align: left; border-bottom: 1px solid #f5c6cb; width: 100px;"><?php esc_html_e('Attachment', 'staticq-media'); ?></th>
                                                                <th style="padding: 8px; text-align: left; border-bottom: 1px solid #f5c6cb;"><?php esc_html_e('File', 'staticq-media'); ?></th>
                                                                <th style="padding: 8px; text-align: left; border-bottom: 1px solid #f5c6cb; width: 220px;"><?php esc_html_e('Error', 'staticq-media'); ?></th>
                                                            </tr>
                                                        </thead>
                                                        <tbody>
                                                            <?php foreach (array_slice($error_details, 0, 50) as $err): ?>
                                                                <?php 
                                                                $att_id = $err['attachment_id'] ?? 0;
                                                                $file = $err['file'] ?? '';
                                                                if (!$file && $att_id) {
                                                                    $file = get_post_meta($att_id, '_wp_attached_file', true) ?: '';
                                                                }
                                                                ?>
                                                                <tr>
                                                                    <td style="padding: 6px 8px; border-bottom: 1px solid #f5c6cb;">
                                                                        <?php if ($att_id): ?>
                                                                            <a href="<?php echo esc_url(admin_url('post.php?post=' . $att_id . '&action=edit')); ?>" target="_blank">#<?php echo esc_html($att_id); ?></a>
                                                                        <?php else: ?>
                                                                            —
                                                                        <?php endif; ?>
                                                                    </td>
                                                                    <td style="padding: 6px 8px; border-bottom: 1px solid #f5c6cb; font-family: monospace; font-size: 12px; word-break: break-all;">
                                                                        <?php echo esc_html($file ?: '—'); ?>
                                                                    </td>
                                                                    <td style="padding: 6px 8px; border-bottom: 1px solid #f5c6cb; color: #721c24;">
                                                                        <?php echo esc_html($err['error'] ?? __('Unknown error', 'staticq-media')); ?>
                                                                    </td>
                                                                </tr>
                                                            <?php endforeach; ?>
                                                        </tbody>
                                                    </table>
                                                    <?php if (count($error_details) > 50): ?>
                                                        <p style="margin: 10px 0 0; color: #666; font-style: italic;">
                                                            <?php
                                                            /* translators: %d: total number of errors */
                                                            echo esc_html(sprintf(__('Showing first 50 of %d errors', 'staticq-media'), count($error_details)));
                                                            ?>
                                                        </p>
                                                    <?php endif; ?>
                                                <?php else: ?>
                                                    <p style="margin: 0; color: #721c24; font-style: italic;">
                                                        <?php esc_html_e('Details not recorded for this batch.', 'staticq-media'); ?>
                                                    </p>
                                                <?php endif; ?>
                                            </div>
                                        <?php endif; ?>
                                        
                                    </div>
                                <?php endif; ?>
                            </div>
                        <?php endforeach; ?>
                    </div>
                </div>
                <?php // CSS extracted to admin/css/sqmedia-media-manager.css ?>
            <?php endif; ?>
        </div>
        <?php
    }
    
    /**
     * Render orphan detection section
     */
    private function render_orphan_detection_section(): void {
        // Get orphan detector instance
        $db = sqmedia()->get_database();
        $s3_handler = sqmedia()->get_storage_handler();
        
        // Check for running orphan scan first
        $running_scan = $db->get_running_scan('orphan');
        $running_scan_id = null;
        
        if ($running_scan && $running_scan->status === 'running') {
            // Check if the state file exists (scan is actually running)
            $upload_dir = wp_upload_dir();
            $state_file = $upload_dir['basedir'] . '/staticq_scans/orphans/' . $running_scan->scan_id . '/state.json';
            
            if (file_exists($state_file)) {
                // Scan is actually running
                $running_scan_id = $running_scan->scan_id;
            } else {
                // Zombie scan - transient expired but DB still shows running
                // Mark it as failed in the database
                $db->update_scan($running_scan->scan_id, [
                    'status' => 'failed',
                    'scan_completed' => current_time('mysql'),
                ]);
                $running_scan = null;
            }
        }
        
        // Get latest completed orphan scan
        $latest_scan = $db->get_latest_scan('orphan');
        $scan_results = null;
        $total_local_orphans = 0;
        $total_bucket_orphans = 0;
        $local_files_scanned = 0;
        $bucket_files_scanned = 0;
        $scan_folder = null;
        $completed_scan_id = null;
        
        if ($latest_scan && $latest_scan->status === 'completed') {
            $scan_results = is_array($latest_scan->results) ? $latest_scan->results : json_decode($latest_scan->results ?? '', true);
            if ($scan_results) {
                $total_local_orphans = $scan_results['local_orphan_count'] ?? 0;
                $total_bucket_orphans = $scan_results['bucket_orphan_count'] ?? 0;
                $local_files_scanned = $scan_results['local_files_scanned'] ?? 0;
                $bucket_files_scanned = $scan_results['bucket_files_scanned'] ?? 0;
                $scan_folder = $scan_results['scan_folder'] ?? null;
                $completed_scan_id = $latest_scan->scan_id;
            }
        }
        
        $total_orphans = $total_local_orphans + $total_bucket_orphans;

        // Check for running quarantine-all process (also finalizes any stale ones)
        $running_quarantine_id = $this->admin->find_running_quarantine();

        // Check for running restore process
        $running_restore_id = $this->admin->find_running_restore();
        ?>
        <div class="sqmedia-section sqmedia-orphan-detection" style="margin-top: 40px; border-top: 3px solid #d63638;">
            <h2>🔘 <?php esc_html_e('Orphan File Detection', 'staticq-media'); ?></h2>
            
            <p class="sqmedia-section-description">
                <?php esc_html_e('Scan your local uploads folder and Google Cloud Storage bucket to find files that exist but have no corresponding WordPress attachment records. These orphaned files may be remnants of deleted posts, failed uploads, or manual file operations.', 'staticq-media'); ?>
            </p>
            
            <div class="notice notice-info inline" style="margin: 15px 0;">
                <p>
                    <strong><?php esc_html_e('Recommended:', 'staticq-media'); ?></strong>
                    <?php esc_html_e('Run the File State Scanner first and fix any issues before scanning for orphans. This ensures that files with corrupted metadata are properly linked to their attachments, preventing them from being incorrectly flagged as orphans.', 'staticq-media'); ?>
                </p>
            </div>
            
            <div class="notice notice-warning inline" style="margin: 15px 0;">
                <p>
                    <strong><?php esc_html_e('Warning:', 'staticq-media'); ?></strong>
                    <?php esc_html_e('Be careful when deleting orphaned files! While the scan attempts to identify files that truly have no attachment records, there may be edge cases. Always review the list carefully before deletion.', 'staticq-media'); ?>
                </p>
            </div>
            
            <!-- Scan Controls -->
            <div class="sqmedia-orphan-controls" style="background: #f6f7f7; padding: 15px; border-radius: 4px; margin-bottom: 20px; <?php echo $running_scan_id ? 'display: none;' : ''; /* Values are hardcoded strings, safe to output. */ ?>">
                <h4 style="margin-top: 0;"><?php esc_html_e('Start Orphan Scan', 'staticq-media'); ?></h4>
                
                <p>
                    <label style="display: block; margin-bottom: 5px;">
                        <input type="radio" name="sqmedia_orphan_scan_type" value="both" checked>
                        <?php esc_html_e('Scan both local files and bucket', 'staticq-media'); ?>
                    </label>
                    <label style="display: block; margin-bottom: 5px;">
                        <input type="radio" name="sqmedia_orphan_scan_type" value="local">
                        <?php esc_html_e('Scan local files only', 'staticq-media'); ?>
                    </label>
                    <label style="display: block;">
                        <input type="radio" name="sqmedia_orphan_scan_type" value="bucket">
                        <?php esc_html_e('Scan bucket only', 'staticq-media'); ?>
                    </label>
                </p>
                
                <p style="margin-top: 15px;">
                    <label for="sqmedia-orphan-exclude-folders" style="display: block; margin-bottom: 5px; font-weight: 500;">
                        <?php esc_html_e('Exclude folders (comma-separated):', 'staticq-media'); ?>
                    </label>
                    <input type="text" id="sqmedia-orphan-exclude-folders" 
                           style="width: 100%; max-width: 400px;" 
                           placeholder="<?php esc_attr_e('e.g., cache, backup, woocommerce_uploads', 'staticq-media'); ?>"
                           value="<?php echo esc_attr(get_option('sqmedia_orphan_exclude_folders', '')); ?>">
                    <span style="display: block; margin-top: 4px; color: #646970; font-size: 12px;">
                        <?php esc_html_e('Files containing these folder names anywhere in their path will be ignored.', 'staticq-media'); ?>
                    </span>
                </p>
                
                <button type="button" id="sqmedia-start-orphan-scan" class="button button-primary">
                    <?php esc_html_e('Start Orphan Scan', 'staticq-media'); ?>
                </button>
                
                <?php if ($latest_scan): ?>
                    <span style="margin-left: 15px; color: #646970;">
                        <?php
                        $scan_date = date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($latest_scan->scan_completed ?? $latest_scan->scan_started ?? 'now'));
                        /* translators: %s: date/time of last scan */
                        echo esc_html(sprintf(__('Last scan: %s', 'staticq-media'), $scan_date));
                        ?>
                    </span>
                <?php endif; ?>
            </div>
            
            <!-- Hidden fields for scan IDs -->
            <input type="hidden" id="sqmedia-running-orphan-scan-id" value="<?php echo esc_attr($running_scan_id ?? ''); ?>">
            <input type="hidden" id="sqmedia-completed-orphan-scan-id" value="<?php echo esc_attr($completed_scan_id ?? ''); ?>">
            <input type="hidden" id="sqmedia-running-quarantine-id" value="<?php echo esc_attr($running_quarantine_id ?? ''); ?>">
            <input type="hidden" id="sqmedia-running-restore-id" value="<?php echo esc_attr($running_restore_id ?? ''); ?>">

            <!-- Quarantine Progress (shown during quarantine-all) -->
            <div id="sqmedia-quarantine-progress" style="display:none; margin: 15px 0; padding: 15px; background: #fff8e5; border: 1px solid #dba617; border-radius: 4px;">
                <div style="display: flex; justify-content: space-between; align-items: center;">
                    <span id="sqmedia-quarantine-progress-text" style="font-weight: 500;">Moving to quarantine...</span>
                    <button type="button" class="button" id="sqmedia-cancel-quarantine" style="color: #d63638;">Cancel</button>
                </div>
                <div style="margin-top: 8px; background: #e0e0e0; border-radius: 3px; height: 6px;">
                    <div id="sqmedia-quarantine-progress-bar" style="background: #dba617; height: 100%; border-radius: 3px; width: 0%; transition: width 0.3s;"></div>
                </div>
            </div>

            <!-- Restore Progress (shown during batched restore) -->
            <div id="sqmedia-restore-progress" style="display:none; margin: 15px 0; padding: 15px; background: #e5f5e5; border: 1px solid #00a32a; border-radius: 4px;">
                <div style="display: flex; justify-content: space-between; align-items: center;">
                    <span id="sqmedia-restore-progress-text" style="font-weight: 500;"><?php esc_html_e('Restoring files...', 'staticq-media'); ?></span>
                    <button type="button" class="button" id="sqmedia-cancel-restore" style="color: #d63638;"><?php esc_html_e('Cancel', 'staticq-media'); ?></button>
                </div>
                <div style="margin-top: 8px; background: #e0e0e0; border-radius: 3px; height: 6px;">
                    <div id="sqmedia-restore-progress-bar" style="background: #00a32a; height: 100%; border-radius: 3px; width: 0%; transition: width 0.3s;"></div>
                </div>
            </div>

            <!-- Scan Progress (shown if scan is running) -->
            <div id="sqmedia-orphan-progress" style="<?php echo $running_scan_id ? '' : 'display: none;'; /* Values are hardcoded strings, safe to output. */ ?> margin: 20px 0;">
                <div style="padding: 15px; background: #f0f0f1; border-left: 4px solid #2271b1; margin-bottom: 15px;">
                    <div style="display: flex; align-items: center; gap: 12px;">
                        <span class="sqmedia-spinner"></span>
                        <div>
                            <strong><?php esc_html_e('Scanning for orphaned files...', 'staticq-media'); ?></strong>
                            <div id="sqmedia-orphan-progress-text" style="margin-top: 4px; color: #646970; font-size: 13px;">
                                <?php esc_html_e('Initializing...', 'staticq-media'); ?>
                            </div>
                        </div>
                    </div>
                    <button type="button" id="sqmedia-cancel-orphan-scan" class="button" style="margin-top: 12px;">
                        <?php esc_html_e('Cancel Scan', 'staticq-media'); ?>
                    </button>
                </div>
                
                <!-- Live Results (shown during scan, disabled buttons) -->
                <div id="sqmedia-orphan-live-results"></div>
            </div>
            
            <!-- Orphan Results (static, shown after page load if scan was completed) -->
            <div id="sqmedia-orphan-results">
            <?php if ($total_orphans > 0 && $scan_folder && is_dir($scan_folder)): ?>
                <div style="margin: 20px 0; padding: 15px; background: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;">
                    <div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;">
                        <div>
                            <h4 style="margin: 0 0 10px; color: #856404;">⚠️ <?php esc_html_e('Orphaned Files Found', 'staticq-media'); ?></h4>
                            <div style="display: flex; flex-wrap: wrap; gap: 20px; font-size: 13px;">
                                <?php if ($local_files_scanned > 0): ?>
                                <div>
                                    <strong><?php esc_html_e('Local:', 'staticq-media'); ?></strong>
                                    <?php
                                    echo esc_html(sprintf(
                                        /* translators: %1$s: number of files scanned, %2$s: number of orphans found */
                                        __('%1$s files scanned → %2$s orphans', 'staticq-media'),
                                        number_format($local_files_scanned),
                                        number_format($total_local_orphans)
                                    ));
                                    ?>
                                </div>
                                <?php endif; ?>
                                <?php if ($bucket_files_scanned > 0): ?>
                                <div>
                                    <strong><?php esc_html_e('Bucket:', 'staticq-media'); ?></strong>
                                    <?php
                                    echo esc_html(sprintf(
                                        /* translators: %1$s: number of files scanned, %2$s: number of orphans found */
                                        __('%1$s files scanned → %2$s orphans', 'staticq-media'),
                                        number_format($bucket_files_scanned),
                                        number_format($total_bucket_orphans)
                                    ));
                                    ?>
                                </div>
                                <?php endif; ?>
                            </div>
                            <p style="margin: 8px 0 0; font-size: 12px; color: #666;">
                                <?php esc_html_e('Review carefully. Files will be moved to quarantine for safe review before permanent deletion.', 'staticq-media'); ?>
                            </p>
                        </div>
                        <div style="display: flex; gap: 10px;">
                            <button type="button" class="button" id="sqmedia-export-orphans-csv">
                                📥 <?php esc_html_e('Export as CSV', 'staticq-media'); ?>
                            </button>
                            <button type="button" class="button" id="sqmedia-clear-orphan-results" style="color: #d63638;">
                                🔘 <?php esc_html_e('Clear Results', 'staticq-media'); ?>
                            </button>
                        </div>
                    </div>
                </div>
                
                <!-- Local Orphans Section -->
                <?php if ($total_local_orphans > 0): ?>
                    <div class="sqmedia-orphan-location" id="sqmedia-local-orphans-section" style="margin-bottom: 25px; border: 2px solid #5a8fb8; border-radius: 6px; overflow: hidden;">
                        <div style="background: #5a8fb8; color: white; padding: 15px 20px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;">
                            <h3 style="margin: 0; display: flex; align-items: center; gap: 10px; font-size: 16px;">
                                📁 <?php esc_html_e('Local Orphaned Files', 'staticq-media'); ?>
                                <span id="sqmedia-local-count-badge" style="background: rgba(255,255,255,0.2); color: white; padding: 4px 12px; border-radius: 12px; font-size: 14px;">
                                    <?php echo esc_html(number_format($total_local_orphans)); ?> <?php esc_html_e('orphans', 'staticq-media'); ?>
                                    <?php if ($local_files_scanned > 0): ?>
                                        / <?php echo esc_html(number_format($local_files_scanned)); ?> <?php esc_html_e('scanned', 'staticq-media'); ?>
                                    <?php endif; ?>
                                </span>
                                <span id="sqmedia-local-size-badge" style="background: rgba(255,255,255,0.2); color: white; padding: 4px 12px; border-radius: 12px; font-size: 14px;"></span>
                            </h3>
                            <div style="display: flex; gap: 10px;">
                                <button type="button" class="button sqmedia-quarantine-selected-orphans" data-type="local" style="background: white; border-color: white;">
                                    <?php esc_html_e('Quarantine Selected', 'staticq-media'); ?>
                                </button>
                                <button type="button" class="button sqmedia-quarantine-all-orphans" data-type="local" data-count="<?php echo (int) $total_local_orphans; ?>" style="background: #dba617; border-color: #dba617; color: white;">
                                    <?php esc_html_e('Quarantine All', 'staticq-media'); ?>
                                </button>
                            </div>
                        </div>
                        <div style="padding: 15px;">
                            <div class="sqmedia-orphan-list-controls" style="margin-bottom: 15px;">
                                <label style="display: flex; align-items: center; gap: 5px;">
                                    <input type="checkbox" class="sqmedia-select-all-orphans" data-type="local">
                                    <?php esc_html_e('Select all', 'staticq-media'); ?>
                                </label>
                            </div>
                            <div id="sqmedia-local-orphans-list" class="sqmedia-orphan-list">
                                <div style="text-align: center; padding: 20px; color: #646970;">
                                    <span class="sqmedia-spinner" style="display: inline-block; margin-right: 10px;"></span>
                                    <?php esc_html_e('Loading orphan files...', 'staticq-media'); ?>
                                </div>
                            </div>
                        </div>
                    </div>
                <?php endif; ?>
                
                <!-- Bucket Orphans Section -->
                <?php if ($total_bucket_orphans > 0): ?>
                    <div class="sqmedia-orphan-location" id="sqmedia-bucket-orphans-section" style="margin-bottom: 25px; border: 2px solid #8e7aa8; border-radius: 6px; overflow: hidden;">
                        <div style="background: #8e7aa8; color: white; padding: 15px 20px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;">
                            <h3 style="margin: 0; display: flex; align-items: center; gap: 10px; font-size: 16px;">
                                ☁️ <?php esc_html_e('Bucket Orphaned Files', 'staticq-media'); ?>
                                <span id="sqmedia-bucket-count-badge" style="background: rgba(255,255,255,0.2); color: white; padding: 4px 12px; border-radius: 12px; font-size: 14px;">
                                    <?php echo esc_html(number_format($total_bucket_orphans)); ?> <?php esc_html_e('orphans', 'staticq-media'); ?>
                                    <?php if ($bucket_files_scanned > 0): ?>
                                        / <?php echo esc_html(number_format($bucket_files_scanned)); ?> <?php esc_html_e('scanned', 'staticq-media'); ?>
                                    <?php endif; ?>
                                </span>
                                <span id="sqmedia-bucket-size-badge" style="background: rgba(255,255,255,0.2); color: white; padding: 4px 12px; border-radius: 12px; font-size: 14px;"></span>
                            </h3>
                            <div style="display: flex; gap: 10px;">
                                <button type="button" class="button sqmedia-quarantine-selected-orphans" data-type="bucket" style="background: white; border-color: white;">
                                    <?php esc_html_e('Quarantine Selected', 'staticq-media'); ?>
                                </button>
                                <button type="button" class="button sqmedia-quarantine-all-orphans" data-type="bucket" data-count="<?php echo (int) $total_bucket_orphans; ?>" style="background: #dba617; border-color: #dba617; color: white;">
                                    <?php esc_html_e('Quarantine All', 'staticq-media'); ?>
                                </button>
                            </div>
                        </div>
                        <div style="padding: 15px;">
                            <div class="sqmedia-orphan-list-controls" style="margin-bottom: 15px;">
                                <label style="display: flex; align-items: center; gap: 5px;">
                                    <input type="checkbox" class="sqmedia-select-all-orphans" data-type="bucket">
                                    <?php esc_html_e('Select all', 'staticq-media'); ?>
                                </label>
                            </div>
                            <div id="sqmedia-bucket-orphans-list" class="sqmedia-orphan-list">
                                <div style="text-align: center; padding: 20px; color: #646970;">
                                    <span class="sqmedia-spinner" style="display: inline-block; margin-right: 10px;"></span>
                                    <?php esc_html_e('Loading orphan files...', 'staticq-media'); ?>
                                </div>
                            </div>
                        </div>
                    </div>
                <?php endif; ?>
                
            <?php elseif ($latest_scan && $latest_scan->status === 'completed'): ?>
                <div style="margin: 20px 0; padding: 20px; text-align: center; background: #d4edda; border: 1px solid #c3e6cb; border-radius: 4px;">
                    <p style="font-size: 16px; margin: 0; color: #155724;">
                        ✓ <strong><?php esc_html_e('All Clear!', 'staticq-media'); ?></strong><br>
                        <span style="font-size: 14px;"><?php esc_html_e('No orphaned files were found. All files are connected to WordPress attachments.', 'staticq-media'); ?></span>
                    </p>
                    <?php if ($local_files_scanned > 0 || $bucket_files_scanned > 0): ?>
                    <p style="margin: 10px 0 0; font-size: 13px; color: #666;">
                        <?php 
                        $scan_summary = [];
                        if ($local_files_scanned > 0) {
                            /* translators: %s: number of local files scanned */
                            $scan_summary[] = sprintf(__('%s local files', 'staticq-media'), number_format($local_files_scanned));
                        }
                        if ($bucket_files_scanned > 0) {
                            /* translators: %s: number of bucket files scanned */
                            $scan_summary[] = sprintf(__('%s bucket files', 'staticq-media'), number_format($bucket_files_scanned));
                        }
                        /* translators: %s: comma-separated list of scan counts */
                        echo esc_html(sprintf(__('Scanned: %s', 'staticq-media'), implode(', ', $scan_summary)));
                        ?>
                    </p>
                    <?php endif; ?>
                </div>
            <?php endif; ?>
            </div><!-- end #sqmedia-orphan-results -->
            
            <!-- Store scan info for JavaScript -->
            <script type="application/json" id="sqmedia-orphan-scan-info">
                <?php echo wp_json_encode([
                    'scan_id' => $completed_scan_id,
                    'local_count' => $total_local_orphans,
                    'bucket_count' => $total_bucket_orphans,
                    'local_files_scanned' => $local_files_scanned,
                    'bucket_files_scanned' => $bucket_files_scanned,
                ]); ?>
            </script>

            <!-- Quarantine Management -->
            <div id="sqmedia-quarantine-management" style="margin-top: 20px; background: #fff; border: 1px solid #ccd0d4; padding: 20px; border-radius: 4px;">
                <h3 style="margin-top: 0; padding-bottom: 10px; border-bottom: 1px solid #eee;"><?php esc_html_e('Quarantine Management', 'staticq-media'); ?></h3>
                <p style="color: #666; font-size: 13px; margin-bottom: 15px;">
                    <?php esc_html_e('Quarantined files are safely stored and can be restored or permanently deleted.', 'staticq-media'); ?>
                </p>
                <div id="sqmedia-quarantine-list">
                    <div style="text-align: center; padding: 20px; color: #646970;">
                        <span class="sqmedia-spinner" style="display: inline-block; margin-right: 10px;"></span>
                        <?php esc_html_e('Loading quarantines...', 'staticq-media'); ?>
                    </div>
                </div>
            </div>
        </div>

        <?php // CSS extracted to admin/css/sqmedia-media-manager.css ?>
        <?php
    }
    
    /**
     * Group orphans by file extension
     */
    private function group_orphans_by_extension(array $orphans): array {
        $grouped = [];
        
        foreach ($orphans as $file) {
            $path = $file['path'] ?? '';
            $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
            if (empty($ext)) {
                $ext = 'unknown';
            }
            
            if (!isset($grouped[$ext])) {
                $grouped[$ext] = [];
            }
            $grouped[$ext][] = $file;
        }
        
        // Sort by extension, but put images first
        uksort($grouped, function($a, $b) {
            $image_exts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico'];
            $a_is_image = in_array($a, $image_exts);
            $b_is_image = in_array($b, $image_exts);
            
            if ($a_is_image && !$b_is_image) return -1;
            if (!$a_is_image && $b_is_image) return 1;
            return strcmp($a, $b);
        });
        
        return $grouped;
    }
    
    /**
     * Render orphans grouped by extension
     */
    private function render_orphans_by_extension(array $grouped, string $type): void {
        $ext_labels = [
            'jpg' => 'JPEG Images',
            'jpeg' => 'JPEG Images',
            'png' => 'PNG Images',
            'gif' => 'GIF Images',
            'webp' => 'WebP Images',
            'svg' => 'SVG Images',
            'bmp' => 'BMP Images',
            'ico' => 'Icon Files',
            'pdf' => 'PDF Documents',
            'mp4' => 'MP4 Videos',
            'mov' => 'MOV Videos',
            'avi' => 'AVI Videos',
            'mp3' => 'MP3 Audio',
            'wav' => 'WAV Audio',
            'zip' => 'ZIP Archives',
            'unknown' => 'Unknown Type',
        ];
        
        foreach ($grouped as $ext => $files):
            $label = $ext_labels[$ext] ?? strtoupper($ext) . ' Files';
            $count = count($files);
            $ext_total_size = array_sum(array_column($files, 'size'));
        ?>
            <div class="sqmedia-orphan-group sqmedia-expanded" style="margin-bottom: 15px; border: 1px solid #c3c4c7; border-radius: 4px;">
                <div class="sqmedia-orphan-header" style="background: #f6f7f7; padding: 10px 15px; cursor: pointer; display: flex; justify-content: space-between; align-items: center;" onclick="this.parentElement.classList.toggle('sqmedia-expanded')">
                    <div style="display: flex; align-items: center; flex-wrap: wrap; gap: 8px;">
                        <strong>.<?php echo esc_html($ext); ?></strong>
                        <span style="color: #646970;">(<?php echo esc_html($label); ?>)</span>
                        <span style="background: #dba617; color: white; padding: 2px 8px; border-radius: 10px; font-size: 11px;">
                            <?php echo esc_html($count); ?> <?php esc_html_e('files', 'staticq-media'); ?>
                        </span>
                        <span style="background: #2271b1; color: white; padding: 2px 8px; border-radius: 10px; font-size: 11px;">
                            <?php echo esc_html(size_format($ext_total_size)); ?>
                        </span>
                    </div>
                    <span class="sqmedia-toggle-icon dashicons dashicons-arrow-down-alt2"></span>
                </div>
                <div class="sqmedia-orphan-details" style="max-height: 250px; overflow-y: auto; border-top: 1px solid #c3c4c7;">
                    <table class="widefat striped" style="margin: 0; border: none;">
                        <thead>
                            <tr>
                                <th style="width: 40px;"><input type="checkbox" class="sqmedia-orphan-select-all" data-type="<?php echo esc_attr($type); ?>" data-ext="<?php echo esc_attr($ext); ?>"></th>
                                <th><?php esc_html_e('File Path', 'staticq-media'); ?></th>
                                <th style="width: 80px;"><?php esc_html_e('Size', 'staticq-media'); ?></th>
                                <th style="width: 120px;"><?php esc_html_e('Modified', 'staticq-media'); ?></th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php foreach ($files as $file): ?>
                                <tr>
                                    <td>
                                        <input type="checkbox" class="sqmedia-orphan-checkbox" data-type="<?php echo esc_attr($type); ?>" data-ext="<?php echo esc_attr($ext); ?>" data-file='<?php echo esc_attr(wp_json_encode($file)); ?>'>
                                    </td>
                                    <td><code style="font-size: 11px; word-break: break-all;"><?php echo esc_html($file['path'] ?? ''); ?></code></td>
                                    <td style="font-size: 12px;"><?php echo esc_html(size_format($file['size'] ?? 0, 1)); ?></td>
                                    <td style="font-size: 12px;"><?php 
                                        if (isset($file['modified'])) {
                                            $timestamp = is_numeric($file['modified']) ? $file['modified'] : strtotime($file['modified']);
                                            echo esc_html(gmdate('Y-m-d', $timestamp));
                                        } else {
                                            echo '—';
                                        }
                                    ?></td>
                                </tr>
                            <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>
            </div>
        <?php
        endforeach;
    }
    
    /**
     * Render scripts
     */
    private function render_scripts(): void {
        wp_enqueue_style('sqmedia-media-manager', SQMEDIA_PLUGIN_URL . 'admin/css/sqmedia-media-manager.css', [], SQMEDIA_VERSION);
        ob_start();
        ?>
        jQuery(document).ready(function($) {
            <?php
            $has_active_batch = $this->active_batch && $this->active_batch->status === 'running';
            ?>
            var isRunning = <?php echo $has_active_batch ? 'true' : 'false'; ?>;
            var batchId = <?php echo $has_active_batch ? "'" . esc_js($this->active_batch->batch_id) . "'" : 'null'; ?>;
            
            // Registration preview
            $('#sqmedia-preview-registration').on('click', function() {
                var $btn = $(this);
                $btn.prop('disabled', true).text('<?php esc_html_e('Loading...', 'staticq-media'); ?>');
                
                $.post(ajaxurl, {
                    action: 'sqmedia_preview_registration',
                    nonce: sqmediaAdmin.nonce,
                    start_date: $('#sqmedia-reg-start-date').val(),
                    end_date: $('#sqmedia-reg-end-date').val()
                }, function(response) {
                    $btn.prop('disabled', false).text('<?php esc_html_e('Preview What Will Be Registered', 'staticq-media'); ?>');
                    
                    if (response.success) {
                        $('#sqmedia-registration-preview-content').html(response.data.html);
                        $('#sqmedia-registration-preview').show();
                    } else {
                        alert(response.data || '<?php esc_html_e('Error loading preview', 'staticq-media'); ?>');
                    }
                });
            });
            
            // Start registration
            $('#sqmedia-start-registration').on('click', function() {
                if (isRunning) return;
                if (!window.sqStartOperation('<?php esc_html_e('Registration', 'staticq-media'); ?>')) return;
                if (!confirm('<?php esc_html_e('Start registering all unregistered attachments?', 'staticq-media'); ?>')) { window.sqEndOperation(); return; }

                isRunning = true;
                $(this).prop('disabled', true);
                $('#sqmedia-registration-progress').show();
                
                startRegistration();
            });
            
            function startRegistration() {
                $.post(ajaxurl, {
                    action: 'sqmedia_start_registration',
                    nonce: sqmediaAdmin.nonce,
                    start_date: $('#sqmedia-reg-start-date').val(),
                    end_date: $('#sqmedia-reg-end-date').val()
                }, function(response) {
                    if (response.success) {
                        batchId = response.data.batch_id;
                        processRegistrationBatch();
                    } else {
                        alert(response.data || '<?php esc_html_e('Error starting registration', 'staticq-media'); ?>');
                        resetRegistration();
                    }
                });
            }
            
            function processRegistrationBatch() {
                if (!isRunning || !batchId) return;
                
                $.post(ajaxurl, {
                    action: 'sqmedia_process_registration_batch',
                    nonce: sqmediaAdmin.nonce,
                    batch_id: batchId
                }, function(response) {
                    if (response.success) {
                        var data = response.data;
                        
                        // Update progress text
                        $('#sqmedia-reg-progress-text').text(data.processed + ' / ' + data.total + ' (' + data.progress + '%)');
                        
                        // Update live stats
                        $('#sqmedia-reg-live-registered').text(data.registered || 0);
                        $('#sqmedia-reg-live-skipped').text(data.skipped || 0);
                        if (data.errors > 0) {
                            $('#sqmedia-reg-live-errors').text(data.errors);
                            $('#sqmedia-reg-live-errors-box').show();
                        }
                        
                        // Update recent activity list
                        if (data.recent && data.recent.length > 0) {
                            $('#sqmedia-reg-recent').show();
                            var recentList = $('#sqmedia-reg-recent-list');
                            data.recent.forEach(function(item) {
                                var locationIcon = item.location === 'bucket' ? '☁️' : (item.location === 'both' ? '💾☁' : '💾');
                                var html = '<div style="padding: 4px 0; border-bottom: 1px solid #eee;">' +
                                    '<span style="color: #999; font-size: 11px;">#' + item.id + '</span> ' +
                                    locationIcon + ' ' +
                                    '<span style="color: #333;">' + (item.title || '<?php esc_html_e('Untitled', 'staticq-media'); ?>') + '</span>' +
                                '</div>';
                                recentList.prepend(html);
                            });
                            // Keep only last 20 items
                            recentList.children().slice(20).remove();
                        }
                        
                        if (data.status === 'completed') {
                            $('#sqmedia-registration-progress .sqmedia-spinner').hide();
                            $('#sqmedia-reg-progress-text').text('✓ <?php esc_html_e('Complete!', 'staticq-media'); ?> ' + data.registered + ' <?php esc_html_e('registered', 'staticq-media'); ?>');
                            window.sqEndOperation();
                            // Longer delay when recent activity is visible so user can see results
                            var reloadDelay = (data.recent && data.recent.length > 0) ? 4000 : 1500;
                            setTimeout(function() { location.reload(); }, reloadDelay);
                        } else {
                            setTimeout(processRegistrationBatch, 500);
                        }
                    } else {
                        alert(response.data || '<?php esc_html_e('Error during registration', 'staticq-media'); ?>');
                        resetRegistration();
                    }
                }).fail(function() {
                    setTimeout(processRegistrationBatch, 2000);
                });
            }
            
            function resetRegistration() {
                isRunning = false;
                window.sqEndOperation();
                batchId = null;
                $('#sqmedia-start-registration').prop('disabled', false);
                $('#sqmedia-registration-progress').hide();
                // Reset live stats
                $('#sqmedia-reg-live-registered').text('0');
                $('#sqmedia-reg-live-skipped').text('0');
                $('#sqmedia-reg-live-errors').text('0');
                $('#sqmedia-reg-live-errors-box').hide();
                $('#sqmedia-reg-recent').hide();
                $('#sqmedia-reg-recent-list').empty();
            }
            
            // Auto-resume registration if there's an active batch
            <?php if ($has_active_batch): ?>
            (function() {
                window.sqActiveOperation = '<?php esc_html_e('Registration', 'staticq-media'); ?>';
                $('#sqmedia-start-registration').prop('disabled', true);
                $('#sqmedia-reg-live-registered').text('<?php echo (int)$this->active_batch->registered_attachments; ?>');
                $('#sqmedia-reg-live-skipped').text('<?php echo (int)$this->active_batch->skipped_attachments; ?>');
                <?php if ($this->active_batch->error_attachments > 0): ?>
                $('#sqmedia-reg-live-errors').text('<?php echo (int)$this->active_batch->error_attachments; ?>');
                $('#sqmedia-reg-live-errors-box').show();
                <?php endif; ?>
                processRegistrationBatch();
            })();
            <?php endif; ?>
            
            // ============================================================
            // ORPHAN DETECTION
            // ============================================================
            
            var activeOrphanScan = null;
            var orphanScanInterval = null;
            
            // Check for running scan on page load
            (function() {
                var runningScanId = $('#sqmedia-running-orphan-scan-id').val();
                
                if (runningScanId) {
                    activeOrphanScan = runningScanId;
                    if (!window.sqActiveOperation) window.sqActiveOperation = '<?php esc_html_e('Orphan Scan', 'staticq-media'); ?>';

                    
                    // Show progress UI
                    $('#sqmedia-orphan-progress').show();
                    $('#sqmedia-orphan-results').hide();
                    $('.sqmedia-orphan-controls').hide();
                    $('#sqmedia-start-orphan-scan').prop('disabled', true).text('<?php esc_html_e('Scan in progress...', 'staticq-media'); ?>');
                    
                    // Immediately fetch current progress
                    $.post(ajaxurl, {
                        action: 'sqmedia_orphan_scan_progress',
                        nonce: sqmediaAdmin.nonce,
                        scan_id: runningScanId
                    }, function(response) {
                        
                        if (response.success && response.data) {
                            // Update progress text immediately
                            $('#sqmedia-orphan-progress-text').html(
                                '<?php esc_html_e('Phase:', 'staticq-media'); ?> <strong>' + (response.data.phase || 'scanning') + '</strong> &nbsp;|&nbsp; ' +
                                '<?php esc_html_e('Local orphans:', 'staticq-media'); ?> <strong>' + (response.data.local_orphans || 0) + '</strong> &nbsp;|&nbsp; ' +
                                '<?php esc_html_e('Bucket orphans:', 'staticq-media'); ?> <strong>' + (response.data.bucket_orphans || 0) + '</strong>'
                            );

                            // Show live results preview if we have any
                            if (response.data.results) {
                                updateLiveOrphanResults(response.data.results, true);
                            }
                            
                            // Check if already completed
                            if (response.data.status === 'completed') {
                                location.reload();
                                return;
                            } else if (response.data.status === 'cancelled' || response.data.status === 'failed') {
                                location.reload();
                                return;
                            }
                        }
                        
                        // Start polling and processing
                        startOrphanScanPolling();
                        processOrphanScanBatch();
                    }).fail(function(xhr, status, error) {
                        console.error('StaticQ Orphan Scan: Progress fetch failed', status, error);
                        // Try to resume anyway
                        startOrphanScanPolling();
                        processOrphanScanBatch();
                    });
                }
            })();
            
            // Start orphan scan
            $('#sqmedia-start-orphan-scan').on('click', function() {
                if (!window.sqStartOperation('<?php esc_html_e('Orphan Scan', 'staticq-media'); ?>')) return;
                var $btn = $(this);
                var scanType = $('input[name="sqmedia_orphan_scan_type"]:checked').val() || 'both';

                if (!confirm('<?php esc_html_e('Start orphan file scan? This will scan your uploads folder and/or bucket to find files with no attachment records.', 'staticq-media'); ?>')) {
                    window.sqEndOperation();
                    return;
                }
                
                $btn.prop('disabled', true).text('<?php esc_html_e('Starting...', 'staticq-media'); ?>');
                
                // Get exclude folders
                var excludeFolders = $('#sqmedia-orphan-exclude-folders').val() || '';
                
                // Hide static results, show progress
                $('#sqmedia-orphan-results').hide();
                $('#sqmedia-orphan-live-results').empty();
                
                $.post(ajaxurl, {
                    action: 'sqmedia_start_orphan_scan',
                    nonce: sqmediaAdmin.nonce,
                    scan_type: scanType,
                    exclude_folders: excludeFolders
                }, function(response) {
                    if (response.success) {
                        activeOrphanScan = response.data.scan_id;
                        $('#sqmedia-orphan-progress').show();
                        startOrphanScanPolling();
                        processOrphanScanBatch();
                    } else {
                        alert(response.data || '<?php esc_html_e('Failed to start scan', 'staticq-media'); ?>');
                        $btn.prop('disabled', false).text('<?php esc_html_e('Start Orphan Scan', 'staticq-media'); ?>');
                        $('#sqmedia-orphan-results').show();
                    }
                });
            });
            
            function startOrphanScanPolling() {
                if (orphanScanInterval) clearInterval(orphanScanInterval);
                // Poll every 5 seconds to update live results table
                orphanScanInterval = setInterval(checkOrphanScanProgress, 5000);
            }
            
            // Track consecutive failures for retry logic
            var orphanScanRetryCount = 0;
            var maxOrphanScanRetries = 5;
            
            function processOrphanScanBatch() {
                if (!activeOrphanScan) return;
                
                $.post(ajaxurl, {
                    action: 'sqmedia_process_orphan_scan_batch',
                    nonce: sqmediaAdmin.nonce,
                    scan_id: activeOrphanScan
                }, function(response) {
                    // Ignore response if cancelled
                    if (window.sqOrphanScanCancelled) return;
                    
                    
                    // Reset retry count on successful response
                    orphanScanRetryCount = 0;
                    
                    if (response.success) {
                        if (response.data.status === 'running') {
                            // Update progress display immediately from batch response
                            $('#sqmedia-orphan-progress-text').html(
                                '<?php esc_html_e('Phase:', 'staticq-media'); ?> <strong>' + (response.data.phase || 'scanning') + '</strong> &nbsp;|&nbsp; ' +
                                '<?php esc_html_e('Local orphans:', 'staticq-media'); ?> <strong>' + (response.data.local_orphans || 0) + '</strong> &nbsp;|&nbsp; ' +
                                '<?php esc_html_e('Bucket orphans:', 'staticq-media'); ?> <strong>' + (response.data.bucket_orphans || 0) + '</strong>'
                            );
                            
                            // Continue processing after short delay
                            setTimeout(processOrphanScanBatch, 250);
                        } else if (response.data.status === 'completed') {
                            // Scan finished - reload page to show results
                            window.sqEndOperation();
                            location.reload();
                        }
                    } else {
                        // Don't show error if cancelled
                        if (window.sqOrphanScanCancelled) return;
                        
                        // Error from server - log and retry
                        console.warn('Orphan scan server error:', response.data || 'Unknown error');
                        
                        orphanScanRetryCount++;
                        if (orphanScanRetryCount < maxOrphanScanRetries) {
                            setTimeout(processOrphanScanBatch, 2000); // Retry after 2 seconds
                        } else {
                            // Too many failures - stop silently
                            console.error('Orphan scan stopped after ' + maxOrphanScanRetries + ' consecutive errors');
                            clearInterval(orphanScanInterval);
                            activeOrphanScan = null;
                            window.sqEndOperation();

                            $('#sqmedia-orphan-progress').hide();
                            $('.sqmedia-orphan-controls').show();
                            $('#sqmedia-start-orphan-scan').prop('disabled', false).text('<?php esc_html_e('Start Orphan Scan', 'staticq-media'); ?>');

                            // Show error in progress text instead of alert
                            $('#sqmedia-orphan-progress-text').html('<span style="color:#dc3232;"><?php esc_html_e('Scan stopped due to repeated errors. Check browser console.', 'staticq-media'); ?></span>');
                        }
                    }
                }).fail(function(xhr, status, error) {
                    // Don't show error if cancelled
                    if (window.sqOrphanScanCancelled) return;

                    console.warn('Orphan scan AJAX failed:', status, error);

                    orphanScanRetryCount++;
                    if (orphanScanRetryCount < maxOrphanScanRetries) {
                        setTimeout(processOrphanScanBatch, 2000); // Retry after 2 seconds
                    } else {
                        // Too many failures - stop silently
                        console.error('Orphan scan stopped after ' + maxOrphanScanRetries + ' consecutive AJAX failures');
                        clearInterval(orphanScanInterval);
                        activeOrphanScan = null;
                        window.sqEndOperation();

                        $('#sqmedia-orphan-progress').hide();
                        $('.sqmedia-orphan-controls').show();
                        $('#sqmedia-start-orphan-scan').prop('disabled', false).text('<?php esc_html_e('Start Orphan Scan', 'staticq-media'); ?>');

                        // Show error in progress text instead of alert
                        $('#sqmedia-orphan-progress-text').html('<span style="color:#dc3232;"><?php esc_html_e('Scan stopped due to connection errors. Check browser console.', 'staticq-media'); ?></span>');
                    }
                });
            }
            
            function checkOrphanScanProgress() {
                if (!activeOrphanScan) return;
                
                // Don't check if scan was cancelled
                if (window.sqOrphanScanCancelled) return;
                
                $.post(ajaxurl, {
                    action: 'sqmedia_orphan_scan_progress',
                    nonce: sqmediaAdmin.nonce,
                    scan_id: activeOrphanScan
                }, function(response) {
                    // Ignore response if cancelled
                    if (window.sqOrphanScanCancelled) return;
                    
                    if (response.success) {
                        var data = response.data;
                        
                        // Handle cancelled or failed status
                        if (data.status === 'cancelled' || data.status === 'failed') {
                            clearInterval(orphanScanInterval);
                            activeOrphanScan = null;
                            window.sqEndOperation();
                            location.reload();
                            return;
                        }
                        
                        // Update progress text with phase-aware format (same as batch processing)
                        $('#sqmedia-orphan-progress-text').html(
                            '<?php esc_html_e('Phase:', 'staticq-media'); ?> <strong>' + (data.phase || 'scanning') + '</strong> &nbsp;|&nbsp; ' +
                            '<?php esc_html_e('Local orphans:', 'staticq-media'); ?> <strong>' + (data.local_orphans || 0) + '</strong> &nbsp;|&nbsp; ' +
                            '<?php esc_html_e('Bucket orphans:', 'staticq-media'); ?> <strong>' + (data.bucket_orphans || 0) + '</strong>'
                        );
                        
                        // Update live results preview if there are orphans
                        if (data.results && (data.results.local_orphans?.length > 0 || data.results.bucket_orphans?.length > 0)) {
                            updateLiveOrphanResults(data.results, data.status !== 'completed');
                        }
                        
                        if (data.status === 'completed') {
                            clearInterval(orphanScanInterval);
                            activeOrphanScan = null;
                            window.sqEndOperation();

                            // Reload page to show paginated results from files
                            location.reload();
                        }
                    }
                    // Don't show error on failure - might just be cancelled
                });
            }
            
            function updateLiveOrphanResults(results, isScanning) {
                var html = '';
                var disabledClass = isScanning ? 'sqmedia-orphan-scanning' : '';
                
                // Use actual totals if available, otherwise count displayed results
                var totalLocalOrphans = results.total_local_orphans || results.local_orphans?.length || 0;
                var totalBucketOrphans = results.total_bucket_orphans || results.bucket_orphans?.length || 0;
                var totalOrphans = totalLocalOrphans + totalBucketOrphans;
                var localFilesScanned = results.local_files_scanned || 0;
                var bucketFilesScanned = results.bucket_files_scanned || 0;
                var displayedLocal = results.local_orphans?.length || 0;
                var displayedBucket = results.bucket_orphans?.length || 0;
                var isTruncated = results.results_truncated || (totalLocalOrphans > displayedLocal) || (totalBucketOrphans > displayedBucket);
                
                if (isScanning) {
                    $('.sqmedia-orphan-detection').addClass('sqmedia-orphan-scanning');
                }
                
                // Add summary header with CSV button only when scan is complete
                if (!isScanning && totalOrphans > 0) {
                    html += '<div style="margin-bottom: 20px; padding: 15px; background: #fff8e5; border: 1px solid #ffcc00; border-radius: 4px;">';
                    html += '<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;">';
                    html += '<div>';
                    html += '<strong style="font-size: 14px;">⚠️ <?php esc_html_e('Orphaned Files Found', 'staticq-media'); ?></strong>';
                    html += '<div style="margin-top: 8px; font-size: 13px;">';
                    if (localFilesScanned > 0) {
                        html += '<div><strong><?php esc_html_e('Local:', 'staticq-media'); ?></strong> ' + localFilesScanned.toLocaleString() + ' <?php esc_html_e('files scanned', 'staticq-media'); ?> → ' + totalLocalOrphans.toLocaleString() + ' <?php esc_html_e('orphans', 'staticq-media'); ?></div>';
                    }
                    if (bucketFilesScanned > 0) {
                        html += '<div><strong><?php esc_html_e('Bucket:', 'staticq-media'); ?></strong> ' + bucketFilesScanned.toLocaleString() + ' <?php esc_html_e('files scanned', 'staticq-media'); ?> → ' + totalBucketOrphans.toLocaleString() + ' <?php esc_html_e('orphans', 'staticq-media'); ?></div>';
                    }
                    html += '</div>';
                    html += '</div>';
                    html += '<button type="button" class="button" id="sqmedia-export-orphans-csv-live"><?php esc_html_e('Export CSV', 'staticq-media'); ?></button>';
                    html += '</div>';
                    
                    // Show truncation warning if applicable
                    if (isTruncated) {
                        html += '<div style="margin-top: 10px; padding: 8px 12px; background: #f0f0f1; border-radius: 3px; font-size: 13px; color: #646970;">';
                        html += '⚠️ <?php esc_html_e('Showing first 500 files per location. Run another scan after cleaning up these to find more.', 'staticq-media'); ?>';
                        html += '</div>';
                    }
                    html += '</div>';
                }
                
                // Local orphans with colored header
                if (results.local_orphans && results.local_orphans.length > 0) {
                    var localTotalSize = results.local_orphans.reduce(function(sum, f) { return sum + (parseInt(f.size, 10) || 0); }, 0);
                    var localGrouped = groupByExtension(results.local_orphans);
                    
                    // Show total count in badge, add preview note separately if truncated
                    var localCountLabel = totalLocalOrphans + ' <?php esc_html_e('files', 'staticq-media'); ?>';
                    var isLocalPreview = (totalLocalOrphans > displayedLocal);
                    
                    html += '<div class="sqmedia-orphan-location ' + disabledClass + '" style="margin-bottom: 25px; border: 2px solid #5a8fb8; border-radius: 6px; overflow: hidden;">';
                    html += '<div style="background: #5a8fb8; color: white; padding: 15px 20px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;">';
                    html += '<h3 style="margin: 0; display: flex; align-items: center; gap: 10px; font-size: 16px;">';
                    html += '📁 <?php esc_html_e('Local Orphaned Files', 'staticq-media'); ?>';
                    html += '<span style="background: rgba(255,255,255,0.2); color: white; padding: 4px 12px; border-radius: 12px; font-size: 14px;">' + localCountLabel + '</span>';
                    // Only show size badge when not in preview mode (size would be incomplete)
                    if (!isLocalPreview) {
                        html += '<span style="background: rgba(255,255,255,0.2); color: white; padding: 4px 12px; border-radius: 12px; font-size: 14px;">' + formatSize(localTotalSize) + '</span>';
                    }
                    html += '</h3>';
                    html += '<button type="button" class="button sqmedia-quarantine-orphans" data-type="local" style="background: white; border-color: white;"><?php esc_html_e('Quarantine Selected', 'staticq-media'); ?></button>';
                    html += '</div>';
                    html += '<div style="padding: 15px;">';
                    if (isLocalPreview) {
                        html += '<div style="margin-bottom: 12px; padding: 8px 12px; background: #e7f3ff; border-radius: 4px; font-size: 13px; color: #0073aa;">';
                        html += '📊  <?php esc_html_e('Preview: showing first', 'staticq-media'); ?> ' + displayedLocal + ' <?php esc_html_e('files. Full list available after scan completes.', 'staticq-media'); ?>';
                        html += '</div>';
                    }
                    // Use server-provided complete tree if available, else build from preview items
                    var localFolderTree = results.local_folder_tree || buildFolderTree(results.local_orphans);
                    var localNestedTree = buildNestedTree(localFolderTree);
                    html += renderFolderTree(localNestedTree, 'local');
                    html += renderGroupedOrphans(localGrouped, 'local');
                    html += '</div></div>';
                }
                
                // Bucket orphans with colored header
                if (results.bucket_orphans && results.bucket_orphans.length > 0) {
                    var bucketTotalSize = results.bucket_orphans.reduce(function(sum, f) { return sum + (parseInt(f.size, 10) || 0); }, 0);
                    var bucketGrouped = groupByExtension(results.bucket_orphans);
                    
                    // Show total count in badge, add preview note separately if truncated
                    var bucketCountLabel = totalBucketOrphans + ' <?php esc_html_e('files', 'staticq-media'); ?>';
                    var isBucketPreview = (totalBucketOrphans > displayedBucket);
                    
                    html += '<div class="sqmedia-orphan-location ' + disabledClass + '" style="margin-bottom: 25px; border: 2px solid #8e7aa8; border-radius: 6px; overflow: hidden;">';
                    html += '<div style="background: #8e7aa8; color: white; padding: 15px 20px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;">';
                    html += '<h3 style="margin: 0; display: flex; align-items: center; gap: 10px; font-size: 16px;">';
                    html += '☁️ <?php esc_html_e('Bucket Orphaned Files', 'staticq-media'); ?>';
                    html += '<span style="background: rgba(255,255,255,0.2); color: white; padding: 4px 12px; border-radius: 12px; font-size: 14px;">' + bucketCountLabel + '</span>';
                    // Only show size badge when not in preview mode (size would be incomplete)
                    if (!isBucketPreview) {
                        html += '<span style="background: rgba(255,255,255,0.2); color: white; padding: 4px 12px; border-radius: 12px; font-size: 14px;">' + formatSize(bucketTotalSize) + '</span>';
                    }
                    html += '</h3>';
                    html += '<button type="button" class="button sqmedia-quarantine-orphans" data-type="bucket" style="background: white; border-color: white;"><?php esc_html_e('Quarantine Selected', 'staticq-media'); ?></button>';
                    html += '</div>';
                    html += '<div style="padding: 15px;">';
                    if (isBucketPreview) {
                        html += '<div style="margin-bottom: 12px; padding: 8px 12px; background: #f3e7ff; border-radius: 4px; font-size: 13px; color: #6b4c8c;">';
                        html += '📊  <?php esc_html_e('Preview: showing first', 'staticq-media'); ?> ' + displayedBucket + ' <?php esc_html_e('files. Full list available after scan completes.', 'staticq-media'); ?>';
                        html += '</div>';
                    }
                    // Use server-provided complete tree if available, else build from preview items
                    var bucketFolderTree = results.bucket_folder_tree || buildFolderTree(results.bucket_orphans);
                    var bucketNestedTree = buildNestedTree(bucketFolderTree);
                    html += renderFolderTree(bucketNestedTree, 'bucket');
                    html += renderGroupedOrphans(bucketGrouped, 'bucket');
                    html += '</div></div>';
                }
                
                $('#sqmedia-orphan-live-results').html(html);
                
                // Re-bind event handlers for dynamically created elements
                bindOrphanEventHandlers();
            }
            
            function groupByExtension(files) {
                var grouped = {};
                files.forEach(function(file) {
                    var path = file.path || file.relative_path || '';
                    var ext = path.split('.').pop().toLowerCase();
                    if (!ext || ext.length > 10) ext = 'unknown';
                    if (!grouped[ext]) grouped[ext] = [];
                    grouped[ext].push(file);
                });
                return grouped;
            }
            
            function renderGroupedOrphans(grouped, type) {
                var extLabels = {
                    'jpg': 'JPEG Images', 'jpeg': 'JPEG Images', 'png': 'PNG Images',
                    'gif': 'GIF Images', 'webp': 'WebP Images', 'svg': 'SVG Images',
                    'pdf': 'PDF Documents', 'mp4': 'MP4 Videos', 'mp3': 'MP3 Audio',
                    'unknown': 'Unknown Type'
                };
                var html = '';
                
                Object.keys(grouped).sort().forEach(function(ext) {
                    var files = grouped[ext];
                    var label = extLabels[ext] || ext.toUpperCase() + ' Files';
                    var extTotalSize = files.reduce(function(sum, f) { return sum + (parseInt(f.size, 10) || 0); }, 0);
                    
                    html += '<div class="sqmedia-orphan-group sqmedia-expanded" data-ext="' + ext + '" style="margin-bottom: 15px; border: 1px solid #c3c4c7; border-radius: 4px;">';
                    html += '<div class="sqmedia-orphan-header" style="background: #f6f7f7; padding: 10px 15px; cursor: pointer; display: flex; justify-content: space-between; align-items: center;" onclick="this.parentElement.classList.toggle(\'sqmedia-expanded\')">';
                    html += '<div style="display: flex; align-items: center; flex-wrap: wrap; gap: 8px;">';
                    html += '<strong>.' + escapeHtml(ext) + '</strong>';
                    html += '<span style="color: #646970;">(' + escapeHtml(label) + ')</span>';
                    html += '<span style="background: #dba617; color: white; padding: 2px 8px; border-radius: 10px; font-size: 11px;">' + files.length + ' <?php esc_html_e('files', 'staticq-media'); ?></span>';
                    html += '<span style="background: #2271b1; color: white; padding: 2px 8px; border-radius: 10px; font-size: 11px;">' + formatSize(extTotalSize) + '</span>';
                    html += '</div>';
                    html += '<span class="sqmedia-toggle-icon dashicons dashicons-arrow-down-alt2"></span>';
                    html += '</div>';
                    html += '<div class="sqmedia-orphan-details" style="max-height: 250px; overflow-y: auto; border-top: 1px solid #c3c4c7;">';
                    html += '<table class="widefat striped" style="margin: 0; border: none;"><thead><tr>';
                    html += '<th style="width: 40px;"><input type="checkbox" class="sqmedia-orphan-select-all" data-type="' + type + '" data-ext="' + ext + '"></th>';
                    html += '<th><?php esc_html_e('File Path', 'staticq-media'); ?></th>';
                    html += '<th style="width: 80px;"><?php esc_html_e('Size', 'staticq-media'); ?></th>';
                    html += '<th style="width: 120px;"><?php esc_html_e('Modified', 'staticq-media'); ?></th>';
                    html += '</tr></thead><tbody>';
                    
                    files.forEach(function(file) {
                        var modified = file.modified ? (new Date(file.modified)).toISOString().split('T')[0] : '—';
                        html += '<tr><td><input type="checkbox" class="sqmedia-orphan-checkbox" data-type="' + type + '" data-ext="' + ext + '" data-file=\'' + JSON.stringify(file).replace(/'/g, '&#39;') + '\'></td>';
                        html += '<td><code style="font-size: 11px; word-break: break-all;">' + escapeHtml(file.path || '') + '</code></td>';
                        html += '<td style="font-size: 12px;">' + escapeHtml(file.size_formatted || formatSize(file.size || 0)) + '</td>';
                        html += '<td style="font-size: 12px;">' + modified + '</td></tr>';
                    });
                    
                    html += '</tbody></table></div></div>';
                });
                
                return html;
            }
            
            function formatSize(bytes) {
                // Handle invalid values
                if (!bytes || isNaN(bytes) || bytes <= 0) return '0 B';
                bytes = Number(bytes);
                if (isNaN(bytes) || bytes <= 0) return '0 B';
                
                var k = 1024;
                var sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
                var i = Math.floor(Math.log(bytes) / Math.log(k));
                
                // Ensure index is valid
                if (i < 0 || i >= sizes.length || isNaN(i)) return bytes + ' B';
                
                return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
            }
            
            function escapeHtml(text) {
                var div = document.createElement('div');
                div.textContent = text;
                return div.innerHTML;
            }
            
            function bindOrphanEventHandlers() {
                // Orphan select all (use off/on to prevent double binding)
                $('#sqmedia-orphan-live-results .sqmedia-orphan-select-all').off('change').on('change', function() {
                    var isChecked = $(this).is(':checked');
                    var type = $(this).data('type');
                    $('#sqmedia-orphan-live-results .sqmedia-orphan-checkbox[data-type="' + type + '"]').prop('checked', isChecked);
                });

                // CSV export for live results (use off/on to prevent double binding)
                $('#sqmedia-export-orphans-csv-live').off('click').on('click', function() {
                    exportOrphansCsv();
                });

                // Whitelist folder buttons in live results
                $('#sqmedia-orphan-live-results .sqmedia-whitelist-folder').off('click').on('click', function() {
                    whitelistFolder($(this));
                });
            }
            
            // Cancel orphan scan
            $('#sqmedia-cancel-orphan-scan').on('click', function() {
                if (!confirm('<?php esc_html_e('Cancel the current orphan scan?', 'staticq-media'); ?>')) return;
                
                // Set flag to suppress any pending AJAX errors
                window.sqOrphanScanCancelled = true;
                
                $.post(ajaxurl, {
                    action: 'sqmedia_cancel_orphan_scan',
                    nonce: sqmediaAdmin.nonce,
                    scan_id: activeOrphanScan || ''
                }, function(response) {
                    clearInterval(orphanScanInterval);
                    activeOrphanScan = null;
                    window.sqEndOperation();
                    location.reload();
                });
            });
            
            // Orphan select all
            $('.sqmedia-orphan-select-all').on('change', function() {
                var isChecked = $(this).is(':checked');
                var type = $(this).data('type');
                $('.sqmedia-orphan-checkbox[data-type="' + type + '"]').prop('checked', isChecked);
            });
            
            // Quarantine selected orphans (live results)
            $(document).on('click', '.sqmedia-quarantine-orphans', function() {
                if (window.sqActiveOperation) { window.sqStartOperation(''); return; }
                var type = $(this).data('type');
                var files = [];

                $('.sqmedia-orphan-checkbox[data-type="' + type + '"]:checked').each(function() {
                    var fileData = $(this).attr('data-file');
                    if (fileData) {
                        try {
                            files.push(JSON.parse(fileData));
                        } catch(e) {}
                    }
                });

                if (files.length === 0) {
                    alert('<?php esc_html_e('Please select at least one file to quarantine.', 'staticq-media'); ?>');
                    return;
                }

                if (!confirm('<?php esc_html_e('Move', 'staticq-media'); ?> ' + files.length + ' <?php esc_html_e('files to quarantine?', 'staticq-media'); ?>')) {
                    return;
                }

                var $btn = $(this);
                $btn.prop('disabled', true).text('<?php esc_html_e('Moving to quarantine...', 'staticq-media'); ?>');

                // Capture checked rows before AJAX (so we can remove them on success)
                var $checkedRows = $('.sqmedia-orphan-checkbox[data-type="' + type + '"]:checked').closest('tr');

                $.post(ajaxurl, {
                    action: 'sqmedia_quarantine_orphan_files',
                    nonce: sqmediaAdmin.nonce,
                    files: JSON.stringify(files),
                    quarantine_type: type,
                    scan_id: orphanScanId || ''
                }, function(response) {
                    if (response.success) {
                        $checkedRows.fadeOut(300, function() { $(this).remove(); });

                        var msg = response.data.message || '<?php esc_html_e('Files moved to quarantine.', 'staticq-media'); ?>';
                        if (response.data.errors && response.data.errors.length > 0) {
                            msg += '\n<?php esc_html_e('Some files could not be quarantined.', 'staticq-media'); ?>';
                        }
                        alert(msg);

                        $btn.prop('disabled', false).text('<?php esc_html_e('Quarantine Selected', 'staticq-media'); ?>');
                        loadQuarantineList();
                    } else {
                        alert(response.data || '<?php esc_html_e('Error quarantining files.', 'staticq-media'); ?>');
                        $btn.prop('disabled', false).text('<?php esc_html_e('Quarantine Selected', 'staticq-media'); ?>');
                    }
                });
            });
            
            // Reusable CSV export function
            function exportOrphansCsv() {
                var localOrphans = [];
                var bucketOrphans = [];
                
                // Try to get data from hidden JSON element (static results)
                var dataEl = document.getElementById('sqmedia-orphan-data');
                if (dataEl) {
                    try {
                        var data = JSON.parse(dataEl.textContent);
                        localOrphans = data.local || [];
                        bucketOrphans = data.bucket || [];
                    } catch(e) {}
                }
                
                // If no static data, collect from checkboxes (live results)
                if (localOrphans.length === 0 && bucketOrphans.length === 0) {
                    $('.sqmedia-orphan-checkbox[data-type="local"]').each(function() {
                        try {
                            var fileData = JSON.parse($(this).attr('data-file'));
                            localOrphans.push(fileData);
                        } catch(e) {}
                    });
                    $('.sqmedia-orphan-checkbox[data-type="bucket"]').each(function() {
                        try {
                            var fileData = JSON.parse($(this).attr('data-file'));
                            bucketOrphans.push(fileData);
                        } catch(e) {}
                    });
                }
                
                if (localOrphans.length === 0 && bucketOrphans.length === 0) {
                    alert('<?php esc_html_e('No orphan data available', 'staticq-media'); ?>');
                    return;
                }
                
                var csv = 'Location,File Path,Extension,Size (bytes),Modified\n';
                
                // Local orphans
                localOrphans.forEach(function(file) {
                    var path = (file.path || file.relative_path || '').replace(/"/g, '""');
                    var ext = path.split('.').pop() || 'unknown';
                    csv += '"local","' + path + '","' + ext + '",' + (file.size || 0) + ',"' + (file.modified || '') + '"\n';
                });
                
                // Bucket orphans
                bucketOrphans.forEach(function(file) {
                    var path = (file.path || file.relative_path || '').replace(/"/g, '""');
                    var ext = path.split('.').pop() || 'unknown';
                    csv += '"bucket","' + path + '","' + ext + '",' + (file.size || 0) + ',"' + (file.modified || '') + '"\n';
                });
                
                // Download
                var blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
                var link = document.createElement('a');
                link.href = URL.createObjectURL(blob);
                link.download = 'orphan-files-' + new Date().toISOString().slice(0,10) + '.csv';
                link.click();
            }
            
            // Export orphans as CSV (static button)
            $('#sqmedia-export-orphans-csv').on('click', function() {
                exportOrphansCsv();
            });
            
            // Select all for extension groups
            $(document).on('change', '.sqmedia-orphan-select-all[data-ext]', function() {
                var type = $(this).data('type');
                var ext = $(this).data('ext');
                var checked = $(this).prop('checked');
                
                $('.sqmedia-orphan-checkbox[data-type="' + type + '"][data-ext="' + ext + '"]').prop('checked', checked);
            });
            
            // ============================================================
            // ORPHAN LOADING (file-based storage)
            // ============================================================
            
            var orphanScanId = null;
            
            // Load all orphans on page load
            (function loadOrphansOnPageLoad() {
                var scanInfo = document.getElementById('sqmedia-orphan-scan-info');
                if (!scanInfo) return;
                
                try {
                    var info = JSON.parse(scanInfo.textContent);
                    orphanScanId = info.scan_id;
                    
                    if (orphanScanId && info.local_count > 0) {
                        loadAllOrphans('local');
                    }
                    if (orphanScanId && info.bucket_count > 0) {
                        loadAllOrphans('bucket');
                    }
                } catch(e) {
                    console.error('Failed to parse orphan scan info:', e);
                }
            })();
            
            function loadAllOrphans(type, offset) {
                if (!orphanScanId) return;
                offset = offset || 0;

                var listEl = $('#sqmedia-' + type + '-orphans-list');
                if (offset === 0) {
                    listEl.html('<div style="text-align: center; padding: 20px; color: #646970;"><span class="sqmedia-spinner" style="display: inline-block; margin-right: 10px;"></span><?php esc_html_e('Loading...', 'staticq-media'); ?></div>');
                }

                $.post(ajaxurl, {
                    action: 'sqmedia_get_orphans_paginated',
                    nonce: sqmediaAdmin.nonce,
                    scan_id: orphanScanId,
                    type: type,
                    offset: offset,
                    limit: 500
                }, function(response) {
                    if (response.success && response.data) {
                        renderAllOrphans(type, response.data, offset > 0);
                    } else {
                        listEl.html('<div style="text-align: center; padding: 20px; color: #d63638;"><?php esc_html_e('Failed to load orphan files.', 'staticq-media'); ?></div>');
                    }
                }).fail(function() {
                    listEl.html('<div style="text-align: center; padding: 20px; color: #d63638;"><?php esc_html_e('Connection error.', 'staticq-media'); ?></div>');
                });
            }
            
            function renderAllOrphans(type, data, isAppend) {
                var listEl = $('#sqmedia-' + type + '-orphans-list');
                var sizeBadge = $('#sqmedia-' + type + '-size-badge');

                if (!data.items || data.items.length === 0) {
                    if (!isAppend) {
                        listEl.html('<div style="text-align: center; padding: 20px; color: #646970;"><?php esc_html_e('No orphan files found.', 'staticq-media'); ?></div>');
                    }
                    return;
                }

                // Update size badge in header (only on first page, which calculates total_size)
                if (!isAppend && data.total_size) {
                    sizeBadge.text(formatSize(data.total_size));
                }

                if (isAppend) {
                    // Remove existing "Load more" button before appending
                    listEl.find('.sqmedia-load-more-orphans').remove();

                    // Update the folder tree if the server sent the complete one
                    if (data.folder_tree) {
                        var nestedTree = buildNestedTree(data.folder_tree);
                        var treeHtml = renderFolderTree(nestedTree, type);
                        var $existingTree = listEl.find('.sqmedia-folder-tree-container');
                        if ($existingTree.length) {
                            $existingTree.replaceWith(treeHtml);
                        } else {
                            listEl.prepend(treeHtml);
                        }
                    }

                    // Group new items by extension and merge into existing groups
                    var grouped = groupByExtension(data.items);
                    Object.keys(grouped).forEach(function(ext) {
                        var files = grouped[ext];
                        var $group = listEl.find('.sqmedia-orphan-group[data-ext="' + ext + '"]');

                        if ($group.length) {
                            // Append rows to existing group's tbody
                            var $tbody = $group.find('tbody');
                            files.forEach(function(file) {
                                var modified = file.modified ? (new Date(file.modified)).toISOString().split('T')[0] : '—';
                                $tbody.append(
                                    '<tr><td><input type="checkbox" class="sqmedia-orphan-checkbox" data-type="' + type + '" data-ext="' + ext + '" data-file=\'' + JSON.stringify(file).replace(/'/g, '&#39;') + '\'></td>' +
                                    '<td><code style="font-size: 11px; word-break: break-all;">' + escapeHtml(file.path || '') + '</code></td>' +
                                    '<td style="font-size: 12px;">' + escapeHtml(file.size_formatted || formatSize(file.size || 0)) + '</td>' +
                                    '<td style="font-size: 12px;">' + modified + '</td></tr>'
                                );
                            });
                            // Update file count badge
                            var currentCount = $tbody.find('tr').length;
                            $group.find('.sqmedia-orphan-header span[style*="dba617"]').text(currentCount + ' <?php esc_html_e('files', 'staticq-media'); ?>');
                        } else {
                            // New extension not seen before — create new group
                            var newGroupHtml = renderGroupedOrphans(grouped, type);
                            // Only render this specific extension
                            var singleGroup = {};
                            singleGroup[ext] = files;
                            listEl.append(renderGroupedOrphans(singleGroup, type));
                        }
                    });
                } else {
                    // Use server-provided complete folder tree (built during scan)
                    var html = '';
                    if (data.folder_tree) {
                        var nestedTree = buildNestedTree(data.folder_tree);
                        html = renderFolderTree(nestedTree, type);
                    } else {
                        // Fallback: build from current page items
                        var folderTree = buildFolderTree(data.items);
                        var nestedTree = buildNestedTree(folderTree);
                        html = renderFolderTree(nestedTree, type);
                    }

                    // Group by extension and render
                    var grouped = groupByExtension(data.items);
                    html += renderGroupedOrphans(grouped, type);
                    listEl.html(html);
                }

                // Show "Load more" if there are more items
                if (data.has_more) {
                    var nextOffset = (data.offset || 0) + (data.limit || 500);
                    var remaining = data.total - nextOffset;
                    listEl.append(
                        '<div class="sqmedia-load-more-orphans" style="text-align: center; padding: 15px;">' +
                            '<button type="button" class="button sqmedia-load-more-btn" data-type="' + type + '" data-offset="' + nextOffset + '">' +
                            '<?php esc_html_e('Load more', 'staticq-media'); ?> (' + remaining.toLocaleString() + ' <?php esc_html_e('remaining', 'staticq-media'); ?>)' +
                            '</button>' +
                            '<div style="margin-top: 5px; color: #646970; font-size: 12px;">' +
                            '<?php esc_html_e('Showing', 'staticq-media'); ?> ' + nextOffset.toLocaleString() + ' <?php esc_html_e('of', 'staticq-media'); ?> ' + data.total.toLocaleString() + ' <?php esc_html_e('orphan files', 'staticq-media'); ?>' +
                            '</div>' +
                        '</div>'
                    );
                }
            }
            
            // Load more orphans
            $(document).on('click', '.sqmedia-load-more-btn', function() {
                var type = $(this).data('type');
                var offset = $(this).data('offset');
                $(this).prop('disabled', true).text('<?php esc_html_e('Loading...', 'staticq-media'); ?>');
                loadAllOrphans(type, offset);
            });

            // Select all orphans
            $(document).on('change', '.sqmedia-select-all-orphans', function() {
                var type = $(this).data('type');
                var checked = $(this).prop('checked');
                $('#sqmedia-' + type + '-orphans-list .sqmedia-orphan-checkbox').prop('checked', checked);
            });
            
            // Quarantine selected orphans — uses same batched flow as quarantine-all
            $(document).on('click', '.sqmedia-quarantine-selected-orphans', function() {
                if (quarantineRunning) return;
                if (!window.sqStartOperation('<?php esc_html_e('Quarantine', 'staticq-media'); ?>')) return;

                var type = $(this).data('type');
                var files = [];

                $('#sqmedia-' + type + '-orphans-list .sqmedia-orphan-checkbox:checked').each(function() {
                    try {
                        files.push(JSON.parse($(this).attr('data-file')));
                    } catch(e) {}
                });

                if (files.length === 0) {
                    window.sqEndOperation();
                    alert('<?php esc_html_e('Please select files to quarantine.', 'staticq-media'); ?>');
                    return;
                }

                if (!confirm('<?php esc_html_e('Move', 'staticq-media'); ?> ' + files.length + ' <?php esc_html_e('files to quarantine?', 'staticq-media'); ?>')) {
                    window.sqEndOperation();
                    return;
                }

                quarantineRunning = true;
                quarantineSelectedFiles = JSON.stringify(files);
                activeQuarantineType = type;
                activeQuarantineId = '';

                $('.sqmedia-quarantine-all-orphans, .sqmedia-quarantine-selected-orphans').prop('disabled', true);
                showQuarantineProgress();

                quarantineBatchLoop();
                startQuarantinePoll();
            });

            // =====================================================================
            // Quarantine-All with progress bar, cron fallback, page-reload resume
            // =====================================================================
            var quarantineRunning = false;
            var activeQuarantineId = $('#sqmedia-running-quarantine-id').val() || '';
            var activeQuarantineType = '';
            var quarantineSelectedFiles = '';
            var quarantinePollTimer = null;

            // Resume on page load if a quarantine is already running
            if (activeQuarantineId) {
                quarantineRunning = true;
                if (!window.sqActiveOperation) window.sqActiveOperation = '<?php esc_html_e('Quarantine', 'staticq-media'); ?>';
                showQuarantineProgress();
                $('.sqmedia-quarantine-all-orphans, .sqmedia-quarantine-selected-orphans').prop('disabled', true);
                quarantineBatchLoop();
                startQuarantinePoll();
            }

            // Click handler for Quarantine All
            $(document).on('click', '.sqmedia-quarantine-all-orphans', function() {
                if (quarantineRunning) return;
                if (!window.sqStartOperation('<?php esc_html_e('Quarantine', 'staticq-media'); ?>')) return;

                var type = $(this).data('type');
                // Read directly from data-count on the button (set server-side from
                // $total_local_orphans / $total_bucket_orphans). The earlier approach
                // parsed the H3 badge text and concatenated digits from "356 orphans
                // / 1,495 scanned" into "3561495".
                var count = parseInt($(this).attr('data-count'), 10) || 0;

                if (!confirm('<?php esc_html_e('Move all', 'staticq-media'); ?> ' + count + ' ' + type + ' <?php esc_html_e('orphan files to quarantine?', 'staticq-media'); ?>')) {
                    window.sqEndOperation();
                    return;
                }

                quarantineRunning = true;
                activeQuarantineType = type;
                activeQuarantineId = ''; // Will be set by first response

                // Disable all quarantine buttons
                $('.sqmedia-quarantine-all-orphans, .sqmedia-quarantine-selected-orphans').prop('disabled', true);
                showQuarantineProgress();

                // Fire first batch (creates state + schedules cron)
                quarantineBatchLoop();
                startQuarantinePoll();
            });

            function showQuarantineProgress() {
                $('#sqmedia-quarantine-progress').show();
            }

            function hideQuarantineProgress() {
                $('#sqmedia-quarantine-progress').hide();
                $('#sqmedia-quarantine-progress-bar').css('width', '0%');
                $('#sqmedia-quarantine-progress-text').text('');
            }

            function updateQuarantineUI(data) {
                var pct = parseFloat(data.progress_pct) || 0;
                var total = parseInt(data.total) || 0;
                var quarantined = parseInt(data.quarantined) || 0;
                var errors = parseInt(data.errors) || 0;
                var typeLabel = (activeQuarantineType || data.type || 'bucket');

                $('#sqmedia-quarantine-progress-bar').css('width', pct + '%');

                var text = '<?php esc_html_e('Quarantining', 'staticq-media'); ?> ' + typeLabel + ' <?php esc_html_e('files:', 'staticq-media'); ?> '
                    + quarantined.toLocaleString() + ' / ' + total.toLocaleString()
                    + ' (' + pct + '%)';
                if (errors > 0) {
                    text += ' — ' + errors.toLocaleString() + ' <?php esc_html_e('errors', 'staticq-media'); ?>';
                }
                $('#sqmedia-quarantine-progress-text').text(text);
            }

            function quarantineComplete() {
                quarantineRunning = false;
                window.sqEndOperation();
                stopQuarantinePoll();
                hideQuarantineProgress();
                // Reload the page to show updated quarantine management + cleared orphan list
                location.reload();
            }

            function quarantineBatchLoop() {
                if (!quarantineRunning) return;

                var postData = {
                    action: 'sqmedia_quarantine_all_orphans',
                    nonce: sqmediaAdmin.nonce,
                    scan_id: orphanScanId,
                    type: activeQuarantineType || 'bucket'
                };
                if (activeQuarantineId) {
                    postData.quarantine_id = activeQuarantineId;
                }
                // Pass selected files on first call only — state file takes over after that
                if (quarantineSelectedFiles && !activeQuarantineId) {
                    postData.files = quarantineSelectedFiles;
                    quarantineSelectedFiles = '';
                }

                $.post(ajaxurl, postData, function(response) {
                    if (!response.success) {
                        // Error — stop batching but keep polling (cron may continue)
                        return;
                    }

                    var d = response.data;
                    activeQuarantineId = d.quarantine_id || activeQuarantineId;
                    if (d.type) activeQuarantineType = d.type;

                    // Another process is already handling this batch — wait and retry
                    if (d.busy) {
                        setTimeout(quarantineBatchLoop, 2000);
                        return;
                    }

                    updateQuarantineUI(d);

                    if (d.status === 'completed') {
                        quarantineComplete();
                    } else if (d.status === 'cancelled') {
                        quarantineRunning = false;
                        window.sqEndOperation();
                        stopQuarantinePoll();
                        hideQuarantineProgress();
                        location.reload();
                    } else {
                        // Continue with next batch after short delay
                        setTimeout(quarantineBatchLoop, 250);
                    }
                }).fail(function() {
                    // Network error — stop batching, polling will track cron progress
                });
            }

            function startQuarantinePoll() {
                stopQuarantinePoll();
                quarantinePollTimer = setInterval(function() {
                    if (!activeQuarantineId) return;
                    $.post(ajaxurl, {
                        action: 'sqmedia_quarantine_progress',
                        nonce: sqmediaAdmin.nonce,
                        quarantine_id: activeQuarantineId
                    }, function(response) {
                        if (!response.success) return;
                        var d = response.data;
                        updateQuarantineUI(d);
                        if (d.status === 'completed') {
                            quarantineComplete();
                        } else if (d.status === 'cancelled') {
                            quarantineRunning = false;
                            window.sqEndOperation();
                            stopQuarantinePoll();
                            hideQuarantineProgress();
                            location.reload();
                        }
                    });
                }, 5000);
            }

            function stopQuarantinePoll() {
                if (quarantinePollTimer) {
                    clearInterval(quarantinePollTimer);
                    quarantinePollTimer = null;
                }
            }

            // Cancel quarantine
            $('#sqmedia-cancel-quarantine').on('click', function() {
                if (!activeQuarantineId) return;
                if (!confirm('<?php esc_html_e('Cancel the quarantine process? Files already moved will stay in quarantine.', 'staticq-media'); ?>')) return;

                $(this).prop('disabled', true).text('<?php esc_html_e('Cancelling...', 'staticq-media'); ?>');
                quarantineRunning = false;
                window.sqEndOperation();

                $.post(ajaxurl, {
                    action: 'sqmedia_cancel_quarantine',
                    nonce: sqmediaAdmin.nonce,
                    quarantine_id: activeQuarantineId
                }, function() {
                    stopQuarantinePoll();
                    location.reload();
                });
            });
            
            // Update orphan count badges after deletion
            function updateOrphanCounts(type) {
                var $list = $('#sqmedia-' + type + '-orphans-list');
                var remainingCount = $list.find('tr').length;
                var $countBadge = $('#sqmedia-' + type + '-count-badge');
                
                // Update count badge
                $countBadge.text(remainingCount + ' <?php esc_html_e('files', 'staticq-media'); ?>');
                
                // Update size badge by summing remaining file sizes
                var totalSize = 0;
                $list.find('.sqmedia-orphan-checkbox').each(function() {
                    try {
                        var fileData = JSON.parse($(this).attr('data-file'));
                        totalSize += parseInt(fileData.size, 10) || 0;
                    } catch(e) {}
                });
                
                var $sizeBadge = $('#sqmedia-' + type + '-size-badge');
                if (totalSize > 0) {
                    $sizeBadge.text(formatBytes(totalSize));
                } else {
                    $sizeBadge.text('');
                }
                
                // If no files left, hide the entire section
                if (remainingCount === 0) {
                    $('#sqmedia-' + type + '-orphans-section').fadeOut(300, function() {
                        var localVisible = $('#sqmedia-local-orphans-section').is(':visible');
                        var bucketVisible = $('#sqmedia-bucket-orphans-section').is(':visible');

                        if (!localVisible && !bucketVisible) {
                            $('#sqmedia-orphan-results').html('<div style="text-align: center; padding: 40px; color: #00a32a;"><strong>&#10003; <?php esc_html_e('All orphan files moved to quarantine.', 'staticq-media'); ?></strong></div>');
                        }
                    });
                }
            }
            
            // Helper function to format bytes
            function formatBytes(bytes) {
                if (bytes === 0) return '0 B';
                var k = 1024;
                var sizes = ['B', 'KB', 'MB', 'GB'];
                var i = Math.floor(Math.log(bytes) / Math.log(k));
                return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
            }
            
            // Clear orphan results
            $('#sqmedia-clear-orphan-results').on('click', function() {
                if (!orphanScanId) return;

                if (!confirm('<?php esc_html_e('Clear orphan scan results? You will need to run a new scan to see orphan files again.', 'staticq-media'); ?>')) {
                    return;
                }

                $.post(ajaxurl, {
                    action: 'sqmedia_clear_orphan_results',
                    nonce: sqmediaAdmin.nonce,
                    scan_id: orphanScanId
                }, function(response) {
                    location.reload();
                });
            });

            // ============================================================
            // QUARANTINE MANAGEMENT
            // ============================================================

            function loadQuarantineList() {
                var $list = $('#sqmedia-quarantine-list');

                $.post(ajaxurl, {
                    action: 'sqmedia_get_quarantine_list',
                    nonce: sqmediaAdmin.nonce
                }, function(response) {
                    if (response.success) {
                        var quarantines = response.data.quarantines || [];

                        if (quarantines.length === 0) {
                            $list.html('<div style="text-align: center; padding: 20px; color: #646970;"><?php esc_html_e('No quarantined files.', 'staticq-media'); ?></div>');
                            return;
                        }

                        var html = '<table class="widefat striped" style="margin: 0;"><thead><tr>';
                        html += '<th><?php esc_html_e('Date', 'staticq-media'); ?></th>';
                        html += '<th><?php esc_html_e('Type', 'staticq-media'); ?></th>';
                        html += '<th><?php esc_html_e('Files', 'staticq-media'); ?></th>';
                        html += '<th><?php esc_html_e('Size', 'staticq-media'); ?></th>';
                        html += '<th style="width: 200px;"><?php esc_html_e('Actions', 'staticq-media'); ?></th>';
                        html += '</tr></thead><tbody>';

                        quarantines.forEach(function(q) {
                            var typeIcon = q.type === 'bucket' ? '☁️' : '📁';
                            var typeLabel = q.type === 'bucket' ? '<?php esc_html_e('Bucket', 'staticq-media'); ?>' : '<?php esc_html_e('Local', 'staticq-media'); ?>';
                            var isRunning = (q.status === 'running');
                            var isRestoring = (q.restore_status === 'enumerating' || q.restore_status === 'restoring');

                            var rowBg = isRunning ? ' style="background: #fff8e5;"' : (isRestoring ? ' style="background: #e5f5e5;"' : '');
                            html += '<tr' + rowBg + '>';
                            html += '<td title="' + escapeHtml(q.quarantine_id) + '" style="cursor: help;">';
                            html += escapeHtml(q.created || '');
                            if (isRunning) {
                                html += ' <span style="background: #dba617; color: white; padding: 1px 8px; border-radius: 8px; font-size: 11px; margin-left: 6px;"><?php esc_html_e('In progress', 'staticq-media'); ?></span>';
                            } else if (isRestoring) {
                                html += ' <span style="background: #00a32a; color: white; padding: 1px 8px; border-radius: 8px; font-size: 11px; margin-left: 6px;"><?php esc_html_e('Restoring', 'staticq-media'); ?></span>';
                            }
                            html += ' <span class="sqmedia-copy-qid dashicons dashicons-clipboard" data-qid="' + escapeHtml(q.quarantine_id) + '" title="<?php esc_attr_e('Copy ID', 'staticq-media'); ?>" style="font-size: 14px; width: 14px; height: 14px; color: #999; cursor: pointer; vertical-align: middle;"></span>';
                            html += '</td>';
                            html += '<td>' + typeIcon + ' ' + typeLabel + '</td>';
                            html += '<td>' + (q.file_count || 0).toLocaleString() + '</td>';
                            html += '<td>' + formatSize(q.total_size || 0) + '</td>';
                            html += '<td style="display: flex; gap: 8px;">';
                            var anyOperationRunning = !!window.sqActiveOperation;
                            if (isRunning) {
                                html += '<span style="color: #646970; font-size: 12px; font-style: italic;"><?php esc_html_e('Quarantine in progress...', 'staticq-media'); ?></span>';
                            } else if (isRestoring) {
                                html += '<span style="color: #646970; font-size: 12px; font-style: italic;"><?php esc_html_e('Restore in progress...', 'staticq-media'); ?></span>';
                            } else {
                                html += '<button type="button" class="button sqmedia-restore-quarantine" data-id="' + escapeHtml(q.quarantine_id) + '" style="font-size: 12px;"' + (anyOperationRunning ? ' disabled' : '') + '><?php esc_html_e('Restore', 'staticq-media'); ?></button>';
                                html += '<button type="button" class="button sqmedia-purge-quarantine" data-id="' + escapeHtml(q.quarantine_id) + '" style="font-size: 12px; color: #d63638; border-color: #d63638;"' + (anyOperationRunning ? ' disabled' : '') + '><?php esc_html_e('Purge', 'staticq-media'); ?></button>';
                            }
                            html += '</td>';
                            html += '</tr>';
                        });

                        html += '</tbody></table>';
                        $list.html(html);
                    } else {
                        $list.html('<div style="text-align: center; padding: 20px; color: #d63638;"><?php esc_html_e('Failed to load quarantine list.', 'staticq-media'); ?></div>');
                    }
                }).fail(function() {
                    $list.html('<div style="text-align: center; padding: 20px; color: #d63638;"><?php esc_html_e('Connection error.', 'staticq-media'); ?></div>');
                });
            }

            // Load quarantine list on page load
            loadQuarantineList();

            // Purge quarantine
            $(document).on('click', '.sqmedia-purge-quarantine', function() {
                if (window.sqActiveOperation) { window.sqStartOperation(''); return; }
                var id = $(this).data('id');
                if (!confirm('<?php esc_html_e('Permanently delete all quarantined files? This cannot be undone.', 'staticq-media'); ?>')) {
                    return;
                }

                var $btn = $(this);
                $btn.prop('disabled', true).text('<?php esc_html_e('Purging...', 'staticq-media'); ?>');

                $.post(ajaxurl, {
                    action: 'sqmedia_purge_quarantine',
                    nonce: sqmediaAdmin.nonce,
                    quarantine_id: id
                }, function(response) {
                    if (response.success) {
                        alert(response.data.message || '<?php esc_html_e('Quarantine purged.', 'staticq-media'); ?>');
                        loadQuarantineList();
                    } else {
                        alert(response.data || '<?php esc_html_e('Error purging quarantine.', 'staticq-media'); ?>');
                        $btn.prop('disabled', false).text('<?php esc_html_e('Purge', 'staticq-media'); ?>');
                    }
                });
            });

            // =====================================================================
            // Batched Restore with progress bar, cron fallback, page-reload resume
            // =====================================================================
            var restoreRunning = false;
            var activeRestoreQuarantineId = $('#sqmedia-running-restore-id').val() || '';
            var restorePollTimer = null;

            // Resume on page load if a restore is already running
            if (activeRestoreQuarantineId) {
                restoreRunning = true;
                if (!window.sqActiveOperation) window.sqActiveOperation = '<?php esc_html_e('Restore', 'staticq-media'); ?>';
                showRestoreProgress();
                restoreBatchLoop();
                startRestorePoll();
            }

            $(document).on('click', '.sqmedia-restore-quarantine', function() {
                if (!window.sqStartOperation('<?php esc_html_e('Restore', 'staticq-media'); ?>')) return;

                var id = $(this).data('id');
                if (!confirm('<?php esc_html_e('Restore all quarantined files to their original locations?', 'staticq-media'); ?>')) {
                    window.sqEndOperation();
                    return;
                }

                restoreRunning = true;
                activeRestoreQuarantineId = id;
                showRestoreProgress();
                restoreBatchLoop();
                startRestorePoll();
            });

            function showRestoreProgress() {
                $('#sqmedia-restore-progress').show();
            }

            function hideRestoreProgress() {
                $('#sqmedia-restore-progress').hide();
                $('#sqmedia-restore-progress-bar').css('width', '0%');
                $('#sqmedia-restore-progress-text').text('');
            }

            function updateRestoreUI(data) {
                var pct = parseFloat(data.progress_pct) || 0;
                var total = parseInt(data.total) || 0;
                var restored = parseInt(data.restored) || 0;
                var skipped = parseInt(data.skipped) || 0;
                var errors = parseInt(data.errors) || 0;
                var status = data.status || '';

                $('#sqmedia-restore-progress-bar').css('width', pct + '%');

                var text = '';
                if (status === 'enumerating') {
                    text = '<?php esc_html_e('Enumerating files...', 'staticq-media'); ?>';
                    if (total > 0) {
                        text += ' ' + total.toLocaleString() + ' <?php esc_html_e('found', 'staticq-media'); ?>';
                    }
                } else {
                    text = '<?php esc_html_e('Restoring files:', 'staticq-media'); ?> '
                        + restored.toLocaleString() + ' / ' + total.toLocaleString()
                        + ' (' + pct + '%)';
                    if (skipped > 0) {
                        text += ' — ' + skipped.toLocaleString() + ' <?php esc_html_e('skipped', 'staticq-media'); ?>';
                    }
                    if (errors > 0) {
                        text += ' — ' + errors.toLocaleString() + ' <?php esc_html_e('errors', 'staticq-media'); ?>';
                    }
                }
                $('#sqmedia-restore-progress-text').text(text);
            }

            function restoreComplete() {
                restoreRunning = false;
                window.sqEndOperation();
                stopRestorePoll();
                hideRestoreProgress();
                location.reload();
            }

            function restoreBatchLoop() {
                if (!restoreRunning) return;

                $.post(ajaxurl, {
                    action: 'sqmedia_restore_quarantine',
                    nonce: sqmediaAdmin.nonce,
                    quarantine_id: activeRestoreQuarantineId
                }, function(response) {
                    if (!response.success) {
                        // Error — stop batching but keep polling (cron may continue)
                        return;
                    }

                    var d = response.data;
                    activeRestoreQuarantineId = d.quarantine_id || activeRestoreQuarantineId;

                    if (d.busy) {
                        setTimeout(restoreBatchLoop, 2000);
                        return;
                    }

                    updateRestoreUI(d);

                    if (d.status === 'completed') {
                        restoreComplete();
                    } else if (d.status === 'cancelled' || d.status === 'stalled') {
                        restoreRunning = false;
                        window.sqEndOperation();
                        stopRestorePoll();
                        hideRestoreProgress();
                        if (d.status === 'stalled') {
                            alert('<?php esc_html_e('Restore stalled — check error log for details.', 'staticq-media'); ?>');
                        }
                        location.reload();
                    } else {
                        setTimeout(restoreBatchLoop, 250);
                    }
                }).fail(function() {
                    // Network error — stop batching, polling will track cron progress
                });
            }

            function startRestorePoll() {
                stopRestorePoll();
                restorePollTimer = setInterval(function() {
                    if (!activeRestoreQuarantineId) return;
                    $.post(ajaxurl, {
                        action: 'sqmedia_restore_progress',
                        nonce: sqmediaAdmin.nonce,
                        quarantine_id: activeRestoreQuarantineId
                    }, function(response) {
                        if (!response.success) return;
                        var d = response.data;
                        updateRestoreUI(d);
                        if (d.status === 'completed') {
                            restoreComplete();
                        } else if (d.status === 'cancelled' || d.status === 'stalled') {
                            restoreRunning = false;
                            window.sqEndOperation();
                            stopRestorePoll();
                            hideRestoreProgress();
                            location.reload();
                        }
                    });
                }, 5000);
            }

            function stopRestorePoll() {
                if (restorePollTimer) {
                    clearInterval(restorePollTimer);
                    restorePollTimer = null;
                }
            }

            // Cancel restore
            $('#sqmedia-cancel-restore').on('click', function() {
                if (!activeRestoreQuarantineId) return;
                if (!confirm('<?php esc_html_e('Cancel the restore process? Files already restored will stay in their original locations.', 'staticq-media'); ?>')) return;

                $(this).prop('disabled', true).text('<?php esc_html_e('Cancelling...', 'staticq-media'); ?>');
                restoreRunning = false;
                window.sqEndOperation();

                $.post(ajaxurl, {
                    action: 'sqmedia_cancel_restore',
                    nonce: sqmediaAdmin.nonce,
                    quarantine_id: activeRestoreQuarantineId
                }, function() {
                    stopRestorePoll();
                    location.reload();
                });
            });

            // Copy quarantine ID to clipboard
            $(document).on('click', '.sqmedia-copy-qid', function(e) {
                e.stopPropagation();
                var qid = $(this).data('qid');
                var $icon = $(this);
                var tmp = document.createElement('textarea');
                tmp.value = qid;
                tmp.style.position = 'fixed';
                tmp.style.opacity = '0';
                document.body.appendChild(tmp);
                tmp.select();
                document.execCommand('copy');
                document.body.removeChild(tmp);
                $icon.css('color', '#00a32a');
                setTimeout(function() { $icon.css('color', '#999'); }, 1500);
            });

            // ============================================================
            // FOLDER TREE VIEW
            // ============================================================

            function buildFolderTree(files) {
                var tree = {};
                if (!files || !files.length) return tree;

                files.forEach(function(file) {
                    var path = file.relative_path || file.path || '';
                    var dir = path.replace(/\\/g, '/');
                    var lastSlash = dir.lastIndexOf('/');
                    dir = lastSlash > -1 ? dir.substring(0, lastSlash) : '.';

                    if (!tree[dir]) {
                        tree[dir] = { count: 0, size: 0 };
                    }
                    tree[dir].count++;
                    tree[dir].size += parseInt(file.size, 10) || 0;
                });

                return tree;
            }

            function buildNestedTree(flatTree) {
                var root = { name: '', children: {}, count: 0, size: 0, totalCount: 0, totalSize: 0, path: '' };

                Object.keys(flatTree).sort().forEach(function(path) {
                    var parts = path.split('/');
                    var current = root;

                    parts.forEach(function(part, i) {
                        if (!current.children[part]) {
                            current.children[part] = {
                                name: part,
                                children: {},
                                count: 0,
                                size: 0,
                                totalCount: 0,
                                totalSize: 0,
                                path: parts.slice(0, i + 1).join('/')
                            };
                        }
                        current = current.children[part];
                    });

                    current.count = flatTree[path].count;
                    current.size = flatTree[path].size;
                });

                // Propagate totals upward
                function propagate(node) {
                    node.totalCount = node.count;
                    node.totalSize = node.size;

                    Object.keys(node.children).forEach(function(key) {
                        var child = node.children[key];
                        propagate(child);
                        node.totalCount += child.totalCount;
                        node.totalSize += child.totalSize;
                    });
                }

                propagate(root);
                return root;
            }

            function renderFolderTree(nestedTree, type) {
                var childKeys = Object.keys(nestedTree.children);
                if (childKeys.length === 0) return '';

                var html = '<div class="sqmedia-folder-tree-container" style="margin-bottom: 20px;">';
                html += '<div class="sqmedia-folder-tree-header" onclick="jQuery(this).next().toggle(); jQuery(this).find(\'.sqmedia-tree-toggle\').toggleClass(\'sqmedia-rotated\')">';
                html += '<div style="display: flex; align-items: center; gap: 8px;">';
                html += '<span class="sqmedia-tree-toggle dashicons dashicons-arrow-right-alt2"></span>';
                html += '<strong><?php esc_html_e('Folder Tree', 'staticq-media'); ?></strong>';
                html += '<span class="sqmedia-tree-badge">' + nestedTree.totalCount + ' <?php esc_html_e('files', 'staticq-media'); ?></span>';
                html += '<span class="sqmedia-tree-size">' + formatSize(nestedTree.totalSize) + '</span>';
                html += '</div>';
                html += '</div>';
                html += '<div style="display: none; padding: 10px;">';

                childKeys.sort().forEach(function(key) {
                    html += renderTreeNode(nestedTree.children[key], type, 0);
                });

                html += '</div></div>';
                return html;
            }

            function renderTreeNode(node, type, depth) {
                var hasChildren = Object.keys(node.children).length > 0;
                var indent = depth * 20;
                var nodeId = 'sqmedia-tree-' + type + '-' + node.path.replace(/[^a-zA-Z0-9]/g, '-');

                var html = '<div class="sqmedia-tree-item" style="padding-left: ' + (10 + indent) + 'px;">';

                if (hasChildren) {
                    html += '<span class="sqmedia-tree-toggle dashicons dashicons-arrow-right-alt2" onclick="var c=document.getElementById(\'' + nodeId + '\'); c.style.display = c.style.display===\'none\' ? \'block\' : \'none\'; this.classList.toggle(\'sqmedia-rotated\')"></span>';
                } else {
                    html += '<span style="width: 16px; display: inline-block;"></span>';
                }

                html += '<span class="dashicons dashicons-open-folder" style="color: #dba617; font-size: 16px;"></span>';
                html += '<span>' + escapeHtml(node.name || './') + '</span>';

                if (node.totalCount > 0) {
                    html += '<span class="sqmedia-tree-badge">' + node.totalCount + '</span>';
                }

                html += '<span class="sqmedia-tree-size">' + formatSize(node.totalSize) + '</span>';
                if (node.path) {
                    html += '<button type="button" class="button sqmedia-whitelist-folder" data-folder="' + escapeHtml(node.path) + '" data-type="' + type + '"><?php esc_html_e('Hide from scanner', 'staticq-media'); ?></button>';
                }
                html += '</div>';

                if (hasChildren) {
                    html += '<div id="' + nodeId + '" style="display: none;">';
                    Object.keys(node.children).sort().forEach(function(key) {
                        html += renderTreeNode(node.children[key], type, depth + 1);
                    });
                    html += '</div>';
                }

                return html;
            }

            // Whitelist folder button
            $(document).on('click', '.sqmedia-whitelist-folder', function() {
                whitelistFolder($(this));
            });

            function whitelistFolder($btn) {
                if ($btn.hasClass('sqmedia-whitelisted')) return;

                var folder = $btn.data('folder');
                var $input = $('#sqmedia-orphan-exclude-folders');

                if ($input.length) {
                    var current = $input.val() || '';
                    var folders = current.split(',').map(function(f) { return f.trim(); }).filter(function(f) { return f; });

                    if (folders.indexOf(folder) === -1) {
                        folders.push(folder);
                        $input.val(folders.join(', '));
                    }
                }

                $btn.addClass('sqmedia-whitelisted').text('<?php esc_html_e('Hidden from scanner', 'staticq-media'); ?>').prop('disabled', true);
            }
        });
        <?php
        wp_add_inline_script('sqmedia-admin', ob_get_clean());
    }
}