<?php
/**
 * Name: Image Watermark
 * Description: Automatically add watermarks to uploaded images with customizable position, opacity, and scale. Includes bulk processing with backup/restore functionality
 * Category: Media
 * Icon: -
 * Tags: watermark, images, media, protection, branding, copyright
 * Added: 2025-12-24
 * UseCases: [
 *   {
 *     "title": "Protect Your Images",
 *     "description": "Add watermarks to all uploaded images to protect your intellectual property and discourage unauthorized use."
 *   },
 *   {
 *     "title": "Brand Your Media",
 *     "description": "Automatically add your logo or company name to images for consistent branding across your website."
 *   },
 *   {
 *     "title": "Bulk Watermark Existing Images",
 *     "description": "Apply watermarks to your entire media library with progress tracking and automatic backup of originals."
 *   }
 * ]
 * WhereToFind: {
 *   "location": "Switchboard → Image Watermark",
 *   "url": "admin.php?page=switchboard-image-watermark",
 *   "instructions": "Configure watermark settings, upload your watermark image, and apply to new or existing images.",
 *   "newTab": false
 * }
 */

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

class SwitchboardImageWatermark {

    /**
     * Option name for settings
     */
    private $option_name = 'switchboard_image-watermark_settings';

    /**
     * Current settings
     */
    private $settings;

    /**
     * Backup directory path
     */
    private $backup_dir;

    /**
     * Supported image types
     */
    private $supported_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];

    /**
     * Exclusion suffix
     */
    private $exclusion_suffix = '-sbwe';

    /**
     * Meta key to track watermarked images
     */
    private $watermarked_meta_key = '_switchboard_watermarked';

    /**
     * Security: Maximum image dimensions to process (prevents memory exhaustion)
     */
    private $max_image_dimension = 8000;

    /**
     * Security: Allowed position values (whitelist)
     */
    private $allowed_positions = [
        'top-left', 'top-center', 'top-right',
        'center-left', 'center', 'center-right',
        'bottom-left', 'bottom-center', 'bottom-right'
    ];

    /**
     * Security: Allowed format values (whitelist)
     */
    private $allowed_formats = ['jpeg', 'png', 'webp', 'gif'];

    /**
     * Security: Rate limiting - max requests per minute
     */
    private $rate_limit_max = 30;

    /**
     * Security: Rate limiting transient prefix
     */
    private $rate_limit_prefix = 'switchboard_wm_rate_';

    /**
     * Configuration constants for settings bounds and defaults
     */
    // Opacity settings (percentage)
    private const OPACITY_MIN = 10;
    private const OPACITY_MAX = 100;
    private const OPACITY_DEFAULT = 70;

    // Scale settings (percentage of image width)
    private const SCALE_MIN = 5;
    private const SCALE_MAX = 50;
    private const SCALE_DEFAULT = 20;

    // Margin settings (pixels)
    private const MARGIN_MIN = 0;
    private const MARGIN_MAX = 100;
    private const MARGIN_DEFAULT = 10;

    // Minimum image dimensions (pixels)
    private const MIN_IMAGE_SIZE_DEFAULT = 200;
    private const MIN_IMAGE_SIZE_MAX = 5000;

    // Batch processing limits
    private const BATCH_SIZE_DEFAULT = 5;
    private const BATCH_SIZE_MAX = 20;
    private const RESTORE_BATCH_SIZE_DEFAULT = 10;
    private const RESTORE_BATCH_SIZE_MAX = 50;

    // Rate limiting (seconds)
    private const RATE_LIMIT_WINDOW = 60;

    // Image quality for JPEG/WebP output
    private const IMAGE_QUALITY = 90;

    // Memory buffer percentage (keep 20% free)
    private const MEMORY_BUFFER_PERCENT = 0.8;

    /**
     * Constructor
     */
    public function __construct() {
        $this->settings = $this->get_settings();
        $this->backup_dir = WP_CONTENT_DIR . '/switchboard-watermark-backups';

        // Admin menu
        add_action('admin_menu', [$this, 'register_admin_page']);

        // Enqueue admin assets
        add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']);

        // Auto-watermark on upload (if enabled)
        if (!empty($this->settings['auto_watermark']) && !empty($this->settings['watermark_image_id'])) {
            add_filter('wp_generate_attachment_metadata', [$this, 'watermark_on_upload'], 10, 2);
        }

        // AJAX handlers
        add_action('wp_ajax_switchboard_watermark_get_stats', [$this, 'ajax_get_stats']);
        add_action('wp_ajax_switchboard_watermark_process_batch', [$this, 'ajax_process_batch']);
        add_action('wp_ajax_switchboard_watermark_restore_batch', [$this, 'ajax_restore_batch']);
        add_action('wp_ajax_switchboard_watermark_delete_backups', [$this, 'ajax_delete_backups']);
        add_action('wp_ajax_switchboard_watermark_save_settings', [$this, 'ajax_save_settings']);
        add_action('wp_ajax_switchboard_watermark_preview', [$this, 'ajax_preview']);
        add_action('wp_ajax_switchboard_watermark_reset_status', [$this, 'ajax_reset_status']);

        // Settings save handler
        add_action('switchboard_save_module_settings', [$this, 'save_settings'], 10, 2);

        // Backup cleanup cron
        add_action('switchboard_watermark_cleanup_backups', [$this, 'cleanup_old_backups']);
        $this->schedule_backup_cleanup();
    }

    /**
     * Schedule backup cleanup cron job
     */
    private function schedule_backup_cleanup() {
        if (!wp_next_scheduled('switchboard_watermark_cleanup_backups')) {
            wp_schedule_event(time(), 'daily', 'switchboard_watermark_cleanup_backups');
        }
    }

    /**
     * Cleanup old backups based on retention policy
     * Runs daily via cron
     */
    public function cleanup_old_backups() {
        if (!is_dir($this->backup_dir)) {
            return;
        }

        // Get retention days from settings (default 30 days)
        $retention_days = isset($this->settings['backup_retention_days']) ? intval($this->settings['backup_retention_days']) : 30;
        if ($retention_days <= 0) {
            return; // Retention disabled
        }

        $cutoff_time = time() - ($retention_days * DAY_IN_SECONDS);
        $cleaned_count = 0;
        $cleaned_size = 0;

        // Iterate through backup subdirectories
        $subdirs = glob($this->backup_dir . '/*', GLOB_ONLYDIR);
        if (!$subdirs) {
            return;
        }

        foreach ($subdirs as $subdir) {
            $files = glob($subdir . '/*');
            if (!$files) {
                continue;
            }

            foreach ($files as $file) {
                if (is_file($file)) {
                    $file_time = filemtime($file);
                    if ($file_time && $file_time < $cutoff_time) {
                        $file_size = filesize($file);
                        if (@unlink($file)) {
                            $cleaned_count++;
                            $cleaned_size += $file_size;
                        }
                    }
                }
            }

            // Remove empty subdirectory
            if (count(glob($subdir . '/*')) === 0) {
                @rmdir($subdir);
            }
        }

        // Log cleanup if any files were removed
        if ($cleaned_count > 0) {
            error_log(sprintf(
                'Switchboard Watermark: Cleaned up %d backup files (%s) older than %d days',
                $cleaned_count,
                size_format($cleaned_size),
                $retention_days
            ));
        }
    }

    /**
     * Get settings with defaults
     */
    private function get_settings() {
        $defaults = [
            // Watermark type: 'image' or 'text'
            'watermark_type' => 'image',
            'watermark_image_id' => 0,
            // Text watermark settings
            'watermark_text' => '',
            'text_font_size' => 24,
            'text_color' => '#ffffff',
            'text_shadow' => true,
            // Position & layout
            'position' => 'bottom-right',
            'opacity' => self::OPACITY_DEFAULT,
            'scale' => self::SCALE_DEFAULT,
            // Separate margin controls
            'margin_top' => self::MARGIN_DEFAULT,
            'margin_right' => self::MARGIN_DEFAULT,
            'margin_bottom' => self::MARGIN_DEFAULT,
            'margin_left' => self::MARGIN_DEFAULT,
            'margin' => self::MARGIN_DEFAULT, // Legacy single margin (for backwards compatibility)
            // Image filters
            'grayscale' => false,
            // Category-based watermarking
            'apply_mode' => 'all', // 'all', 'categories', 'single'
            'apply_categories' => [], // Array of media_category term IDs
            // General settings
            'min_image_width' => self::MIN_IMAGE_SIZE_DEFAULT,
            'min_image_height' => self::MIN_IMAGE_SIZE_DEFAULT,
            'auto_watermark' => false,
            'backup_originals' => true,
            'backup_retention_days' => 30, // Days to keep backups (0 = keep forever)
            'image_quality' => self::IMAGE_QUALITY, // JPEG/WebP quality (1-100)
            'formats' => $this->allowed_formats,
            // Preview settings
            'preview_category' => 0,
            'preview_image_id' => 0,
        ];

        $options = get_option($this->option_name, []);
        return wp_parse_args($options, $defaults);
    }

    /**
     * Register admin page
     */
    public function register_admin_page() {
        add_submenu_page(
            'switchboard',
            __('Image Watermark', 'wpswitchboard'),
            __('Image Watermark', 'wpswitchboard'),
            'manage_options',
            'switchboard-image-watermark',
            [$this, 'render_admin_page']
        );
    }

    /**
     * Enqueue admin assets
     */
    public function enqueue_admin_assets($hook) {
        if (strpos($hook, 'switchboard-image-watermark') === false) {
            return;
        }

        // Enqueue main Switchboard admin CSS
        if (defined('SWITCHBOARD_URL') && defined('SWITCHBOARD_VERSION')) {
            wp_enqueue_style(
                'switchboard-admin',
                SWITCHBOARD_URL . 'switchboard-admin.css',
                [],
                SWITCHBOARD_VERSION
            );
        }

        // WordPress media uploader
        wp_enqueue_media();

        // Inline CSS for this module
        wp_add_inline_style('switchboard-admin', $this->get_inline_css());

        // Inline JS
        wp_enqueue_script('jquery');
        add_action('admin_footer', [$this, 'output_inline_js']);
    }

    /**
     * Render admin page
     */
    public function render_admin_page() {
        $settings = $this->settings;
        $watermark_image_url = '';
        $watermark_image_id = !empty($settings['watermark_image_id']) ? intval($settings['watermark_image_id']) : 0;

        if ($watermark_image_id) {
            $watermark_image_url = wp_get_attachment_image_url($watermark_image_id, 'medium');
        }

        // Get stats
        $stats = $this->get_stats();
        ?>
        <div class="wrap sb-module-wrap">
            <!-- Header -->
            <div class="sb-module-header">
                <div class="sb-module-header-left">
                    <svg class="sb-module-title-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
                    </svg>
                    <div>
                        <h1 class="sb-module-title">
                            <?php esc_html_e('Image Watermark', 'wpswitchboard'); ?>
                            <span class="switchboard-badge">SWITCHBOARD</span>
                        </h1>
                        <p class="sb-module-subtitle"><?php esc_html_e('Add watermarks to protect and brand your images', 'wpswitchboard'); ?></p>
                    </div>
                </div>
                <div class="sb-module-header-right">
                    <a href="https://docs.wpswitchboard.com/modules/media/image-watermark" target="_blank" class="sb-module-help-link">
                        <span class="sb-module-help-link-icon dashicons dashicons-editor-help"></span>
                        Help
                    </a>
                </div>
            </div>

            <!-- Main Content -->
            <div class="sb-module-container">
                <!-- Stats Cards -->
                <div class="sb-module-stats">
                    <div class="sb-module-stat-card">
                        <div class="sb-module-stat-label"><?php esc_html_e('Total Images', 'wpswitchboard'); ?></div>
                        <div class="sb-module-stat-content">
                            <div class="sb-module-stat-value" id="stat-total-images"><?php echo esc_html(number_format($stats['total_images'])); ?></div>
                        </div>
                    </div>
                    <div class="sb-module-stat-card">
                        <div class="sb-module-stat-label"><?php esc_html_e('Watermarked', 'wpswitchboard'); ?></div>
                        <div class="sb-module-stat-content">
                            <div class="sb-module-stat-value" id="stat-watermarked"><?php echo esc_html(number_format($stats['watermarked'])); ?></div>
                        </div>
                    </div>
                    <div class="sb-module-stat-card">
                        <div class="sb-module-stat-label"><?php esc_html_e('Pending', 'wpswitchboard'); ?></div>
                        <div class="sb-module-stat-content">
                            <div class="sb-module-stat-value" id="stat-pending"><?php echo esc_html(number_format($stats['pending'])); ?></div>
                        </div>
                    </div>
                    <div class="sb-module-stat-card">
                        <div class="sb-module-stat-label"><?php esc_html_e('Backups', 'wpswitchboard'); ?></div>
                        <div class="sb-module-stat-content">
                            <div class="sb-module-stat-value" id="stat-backup-size"><?php echo esc_html($stats['backup_size_formatted']); ?></div>
                        </div>
                    </div>
                </div>

                <!-- Settings & Preview - Two Column -->
                <div class="sb-module-layout-2col" style="align-items: stretch;">
                    <!-- Settings Card -->
                    <div class="sb-module-card" style="display: flex; flex-direction: column;">
                        <div class="sb-module-card-header">
                            <div class="sb-module-card-header-left">
                                <h2 class="sb-module-card-title"><?php esc_html_e('Watermark Settings', 'wpswitchboard'); ?></h2>
                            </div>
                            <div class="sb-module-card-header-right">
                                <div class="watermark-type-tabs-header">
                                    <button type="button" class="watermark-type-tab-header <?php echo $settings['watermark_type'] === 'image' ? 'active' : ''; ?>" data-type="image">
                                        <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
                                        <?php esc_html_e('Image', 'wpswitchboard'); ?>
                                    </button>
                                    <button type="button" class="watermark-type-tab-header <?php echo $settings['watermark_type'] === 'text' ? 'active' : ''; ?>" data-type="text">
                                        <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="4 7 4 4 20 4 20 7"/><line x1="9" y1="20" x2="15" y2="20"/><line x1="12" y1="4" x2="12" y2="20"/></svg>
                                        <?php esc_html_e('Text', 'wpswitchboard'); ?>
                                    </button>
                                </div>
                                <input type="hidden" name="watermark_type" id="watermark_type" value="<?php echo esc_attr($settings['watermark_type']); ?>">
                            </div>
                        </div>
                        <div class="sb-module-card-body" style="flex: 1;">
                            <form id="watermark-settings-form">
                                <?php wp_nonce_field('switchboard_watermark_nonce', 'watermark_nonce'); ?>
                                <?php
                                $media_categories_active = class_exists('SwitchboardMediaCategories') || taxonomy_exists('media_category');
                                $categories = [];
                                if ($media_categories_active) {
                                    $categories = get_terms(['taxonomy' => 'media_category', 'hide_empty' => false]);
                                    if (is_wp_error($categories)) $categories = [];
                                }
                                ?>

                                <!-- IMAGE WATERMARK SETTINGS -->
                                <div id="image-watermark-settings" <?php echo $settings['watermark_type'] !== 'image' ? 'style="display:none;"' : ''; ?>>
                                    <!-- Row 2a: Image | Position | Margins -->
                                    <div class="wm-row wm-row-3col">
                                        <div class="wm-col">
                                            <label class="sb-module-form-label"><?php esc_html_e('Watermark Image', 'wpswitchboard'); ?></label>
                                            <div class="watermark-image-upload">
                                                <div class="watermark-image-preview" id="watermark-preview">
                                                    <?php if ($watermark_image_url): ?>
                                                        <img src="<?php echo esc_url($watermark_image_url); ?>" alt="Watermark">
                                                    <?php else: ?>
                                                        <span class="no-image"><?php esc_html_e('No image', 'wpswitchboard'); ?></span>
                                                    <?php endif; ?>
                                                </div>
                                                <input type="hidden" name="watermark_image_id" id="watermark_image_id" value="<?php echo esc_attr($watermark_image_id); ?>">
                                                <div class="watermark-image-buttons">
                                                    <button type="button" id="select-watermark-btn" class="sb-module-button sb-module-button-secondary sb-module-button-sm">
                                                        <?php esc_html_e('Select', 'wpswitchboard'); ?>
                                                    </button>
                                                    <button type="button" id="remove-watermark-btn" class="sb-module-button sb-module-button-danger sb-module-button-sm" <?php echo !$watermark_image_id ? 'style="display:none;"' : ''; ?>>
                                                        <?php esc_html_e('Remove', 'wpswitchboard'); ?>
                                                    </button>
                                                </div>
                                            </div>
                                        </div>
                                        <div class="wm-col">
                                            <label class="sb-module-form-label"><?php esc_html_e('Position', 'wpswitchboard'); ?></label>
                                            <div class="position-grid">
                                                <?php
                                                $positions = ['top-left', 'top-center', 'top-right', 'center-left', 'center', 'center-right', 'bottom-left', 'bottom-center', 'bottom-right'];
                                                foreach ($positions as $value):
                                                ?>
                                                <label class="position-option <?php echo $settings['position'] === $value ? 'active' : ''; ?>">
                                                    <input type="radio" name="position" value="<?php echo esc_attr($value); ?>" <?php checked($settings['position'], $value); ?>>
                                                    <span class="position-dot"></span>
                                                </label>
                                                <?php endforeach; ?>
                                            </div>
                                        </div>
                                        <div class="wm-col">
                                            <label class="sb-module-form-label"><?php esc_html_e('Margins (px)', 'wpswitchboard'); ?></label>
                                            <div class="margin-grid">
                                                <div class="margin-grid-cell margin-grid-corner"></div>
                                                <div class="margin-grid-cell margin-grid-edge margin-grid-top">
                                                    <input type="number" name="margin_top" id="margin_top" value="<?php echo esc_attr($settings['margin_top']); ?>" min="0" max="100" class="margin-grid-input">
                                                </div>
                                                <div class="margin-grid-cell margin-grid-corner"></div>
                                                <div class="margin-grid-cell margin-grid-edge margin-grid-left">
                                                    <input type="number" name="margin_left" id="margin_left" value="<?php echo esc_attr($settings['margin_left']); ?>" min="0" max="100" class="margin-grid-input">
                                                </div>
                                                <div class="margin-grid-cell margin-grid-center"></div>
                                                <div class="margin-grid-cell margin-grid-edge margin-grid-right">
                                                    <input type="number" name="margin_right" id="margin_right" value="<?php echo esc_attr($settings['margin_right']); ?>" min="0" max="100" class="margin-grid-input">
                                                </div>
                                                <div class="margin-grid-cell margin-grid-corner"></div>
                                                <div class="margin-grid-cell margin-grid-edge margin-grid-bottom">
                                                    <input type="number" name="margin_bottom" id="margin_bottom" value="<?php echo esc_attr($settings['margin_bottom']); ?>" min="0" max="100" class="margin-grid-input">
                                                </div>
                                                <div class="margin-grid-cell margin-grid-corner"></div>
                                            </div>
                                            <input type="hidden" name="margin" id="margin" value="<?php echo esc_attr($settings['margin']); ?>">
                                        </div>
                                    </div>

                                    <!-- Row 2b: Grayscale | Opacity | Scale -->
                                    <div class="wm-row wm-row-3col">
                                        <div class="wm-col">
                                            <label class="sb-module-form-label"><?php esc_html_e('Filter', 'wpswitchboard'); ?></label>
                                            <label class="sb-module-checkbox-item wm-checkbox-large">
                                                <input type="checkbox" name="grayscale" value="1" <?php checked($settings['grayscale']); ?>>
                                                <span><?php esc_html_e('Grayscale', 'wpswitchboard'); ?></span>
                                            </label>
                                        </div>
                                        <div class="wm-col">
                                            <label class="sb-module-form-label">
                                                <?php esc_html_e('Opacity', 'wpswitchboard'); ?>
                                                <span class="range-value" id="opacity-value"><?php echo esc_html($settings['opacity']); ?>%</span>
                                            </label>
                                            <input type="range" name="opacity" id="opacity" min="10" max="100" value="<?php echo esc_attr($settings['opacity']); ?>" class="sb-module-range">
                                        </div>
                                        <div class="wm-col">
                                            <label class="sb-module-form-label">
                                                <?php esc_html_e('Scale', 'wpswitchboard'); ?>
                                                <span class="range-value" id="scale-value"><?php echo esc_html($settings['scale']); ?>%</span>
                                            </label>
                                            <input type="range" name="scale" id="scale" min="5" max="50" value="<?php echo esc_attr($settings['scale']); ?>" class="sb-module-range">
                                        </div>
                                    </div>
                                </div>

                                <!-- TEXT WATERMARK SETTINGS -->
                                <div id="text-watermark-settings" <?php echo $settings['watermark_type'] !== 'text' ? 'style="display:none;"' : ''; ?>>
                                    <!-- Row 2a: Text | Size | Color | Shadow -->
                                    <div class="wm-row wm-row-4col">
                                        <div class="wm-col wm-col-flex2">
                                            <label class="sb-module-form-label"><?php esc_html_e('Watermark Text', 'wpswitchboard'); ?></label>
                                            <input type="text" name="watermark_text" id="watermark_text" value="<?php echo esc_attr($settings['watermark_text']); ?>" class="sb-module-input" placeholder="<?php esc_attr_e('© Your Company', 'wpswitchboard'); ?>">
                                        </div>
                                        <div class="wm-col">
                                            <label class="sb-module-form-label"><?php esc_html_e('Size', 'wpswitchboard'); ?></label>
                                            <div class="wm-input-unit">
                                                <input type="number" name="text_font_size" id="text_font_size" value="<?php echo esc_attr($settings['text_font_size']); ?>" min="12" max="200" class="sb-module-input">
                                                <span class="wm-unit">px</span>
                                            </div>
                                        </div>
                                        <div class="wm-col">
                                            <label class="sb-module-form-label"><?php esc_html_e('Color', 'wpswitchboard'); ?></label>
                                            <input type="color" name="text_color" id="text_color" value="<?php echo esc_attr($settings['text_color']); ?>" class="wm-color-input">
                                        </div>
                                        <div class="wm-col">
                                            <label class="sb-module-form-label"><?php esc_html_e('Shadow', 'wpswitchboard'); ?></label>
                                            <label class="sb-module-checkbox-item wm-checkbox-large">
                                                <input type="checkbox" name="text_shadow" value="1" <?php checked($settings['text_shadow']); ?>>
                                                <span><?php esc_html_e('Enable', 'wpswitchboard'); ?></span>
                                            </label>
                                        </div>
                                    </div>

                                    <!-- Row 2b: Position | Opacity+Scale | Margins -->
                                    <div class="wm-row wm-row-3col">
                                        <div class="wm-col">
                                            <label class="sb-module-form-label"><?php esc_html_e('Position', 'wpswitchboard'); ?></label>
                                            <div class="position-grid position-grid-text">
                                                <?php foreach ($positions as $value): ?>
                                                <label class="position-option <?php echo $settings['position'] === $value ? 'active' : ''; ?>">
                                                    <input type="radio" name="position_text" value="<?php echo esc_attr($value); ?>" <?php checked($settings['position'], $value); ?>>
                                                    <span class="position-dot"></span>
                                                </label>
                                                <?php endforeach; ?>
                                            </div>
                                        </div>
                                        <div class="wm-col">
                                            <label class="sb-module-form-label">
                                                <?php esc_html_e('Opacity', 'wpswitchboard'); ?>
                                                <span class="range-value" id="opacity-value-text"><?php echo esc_html($settings['opacity']); ?>%</span>
                                            </label>
                                            <input type="range" name="opacity_text" id="opacity_text" min="10" max="100" value="<?php echo esc_attr($settings['opacity']); ?>" class="sb-module-range">
                                            <div style="margin-top: 12px;">
                                                <label class="sb-module-form-label">
                                                    <?php esc_html_e('Scale', 'wpswitchboard'); ?>
                                                    <span class="range-value" id="scale-value-text"><?php echo esc_html($settings['scale']); ?>%</span>
                                                </label>
                                                <input type="range" name="scale_text" id="scale_text" min="5" max="100" value="<?php echo esc_attr($settings['scale']); ?>" class="sb-module-range">
                                            </div>
                                        </div>
                                        <div class="wm-col">
                                            <label class="sb-module-form-label"><?php esc_html_e('Margins (px)', 'wpswitchboard'); ?></label>
                                            <div class="margin-grid">
                                                <div class="margin-grid-cell margin-grid-corner"></div>
                                                <div class="margin-grid-cell margin-grid-edge margin-grid-top">
                                                    <input type="number" name="margin_top_text" id="margin_top_text" value="<?php echo esc_attr($settings['margin_top']); ?>" min="0" max="100" class="margin-grid-input">
                                                </div>
                                                <div class="margin-grid-cell margin-grid-corner"></div>
                                                <div class="margin-grid-cell margin-grid-edge margin-grid-left">
                                                    <input type="number" name="margin_left_text" id="margin_left_text" value="<?php echo esc_attr($settings['margin_left']); ?>" min="0" max="100" class="margin-grid-input">
                                                </div>
                                                <div class="margin-grid-cell margin-grid-center"></div>
                                                <div class="margin-grid-cell margin-grid-edge margin-grid-right">
                                                    <input type="number" name="margin_right_text" id="margin_right_text" value="<?php echo esc_attr($settings['margin_right']); ?>" min="0" max="100" class="margin-grid-input">
                                                </div>
                                                <div class="margin-grid-cell margin-grid-corner"></div>
                                                <div class="margin-grid-cell margin-grid-edge margin-grid-bottom">
                                                    <input type="number" name="margin_bottom_text" id="margin_bottom_text" value="<?php echo esc_attr($settings['margin_bottom']); ?>" min="0" max="100" class="margin-grid-input">
                                                </div>
                                                <div class="margin-grid-cell margin-grid-corner"></div>
                                            </div>
                                        </div>
                                    </div>
                                </div>

                                <!-- COMMON SETTINGS: Apply To -->
                                <div class="wm-section wm-section-divider">
                                    <label class="sb-module-form-label"><?php esc_html_e('Apply Watermark To', 'wpswitchboard'); ?></label>
                                    <div class="apply-mode-options-compact">
                                        <label class="apply-mode-option-compact <?php echo $settings['apply_mode'] === 'all' ? 'active' : ''; ?>">
                                            <input type="radio" name="apply_mode" value="all" <?php checked($settings['apply_mode'], 'all'); ?>>
                                            <span><?php esc_html_e('All Images', 'wpswitchboard'); ?></span>
                                        </label>
                                        <?php if ($media_categories_active && !empty($categories)): ?>
                                        <label class="apply-mode-option-compact <?php echo $settings['apply_mode'] === 'categories' ? 'active' : ''; ?>">
                                            <input type="radio" name="apply_mode" value="categories" <?php checked($settings['apply_mode'], 'categories'); ?>>
                                            <span><?php esc_html_e('Specific Categories', 'wpswitchboard'); ?></span>
                                        </label>
                                        <?php endif; ?>
                                    </div>
                                    <?php if ($media_categories_active && !empty($categories)): ?>
                                    <div id="category-selector" style="margin-top: 12px; <?php echo $settings['apply_mode'] !== 'categories' ? 'display:none;' : ''; ?>">
                                        <div class="category-checkboxes-compact">
                                            <?php foreach ($categories as $cat): ?>
                                            <label class="category-checkbox-item">
                                                <input type="checkbox" name="apply_categories[]" value="<?php echo esc_attr($cat->term_id); ?>" <?php checked(in_array($cat->term_id, (array)$settings['apply_categories'])); ?>>
                                                <span><?php echo esc_html($cat->name); ?></span>
                                                <span class="category-count"><?php echo intval($cat->count); ?></span>
                                            </label>
                                            <?php endforeach; ?>
                                        </div>
                                    </div>
                                    <?php endif; ?>
                                </div>

                                <!-- COMMON SETTINGS: Min Size | Quality | Formats | Options -->
                                <div class="wm-row wm-row-4col">
                                    <div class="wm-col">
                                        <label class="sb-module-form-label"><?php esc_html_e('Min Size', 'wpswitchboard'); ?></label>
                                        <div class="wm-size-inputs">
                                            <input type="number" name="min_image_width" id="min_image_width" value="<?php echo esc_attr($settings['min_image_width']); ?>" min="0" max="2000" class="sb-module-input">
                                            <span class="wm-size-x">×</span>
                                            <input type="number" name="min_image_height" id="min_image_height" value="<?php echo esc_attr($settings['min_image_height']); ?>" min="0" max="2000" class="sb-module-input">
                                        </div>
                                    </div>
                                    <div class="wm-col">
                                        <label class="sb-module-form-label"><?php esc_html_e('Quality', 'wpswitchboard'); ?></label>
                                        <div class="wm-quality-input">
                                            <input type="range" name="image_quality" id="image_quality" value="<?php echo esc_attr($settings['image_quality']); ?>" min="50" max="100" step="5" class="sb-module-range">
                                            <span id="quality-value"><?php echo esc_html($settings['image_quality']); ?>%</span>
                                        </div>
                                    </div>
                                    <div class="wm-col">
                                        <label class="sb-module-form-label"><?php esc_html_e('Formats', 'wpswitchboard'); ?></label>
                                        <div class="wm-format-checkboxes">
                                            <?php
                                            $formats = ['jpeg' => 'JPG', 'png' => 'PNG', 'webp' => 'WebP', 'gif' => 'GIF'];
                                            foreach ($formats as $value => $label):
                                            ?>
                                            <label class="format-checkbox-compact">
                                                <input type="checkbox" name="formats[]" value="<?php echo esc_attr($value); ?>" <?php checked(in_array($value, $settings['formats'])); ?>>
                                                <span><?php echo esc_html($label); ?></span>
                                            </label>
                                            <?php endforeach; ?>
                                        </div>
                                    </div>
                                    <div class="wm-col">
                                        <label class="sb-module-form-label"><?php esc_html_e('Options', 'wpswitchboard'); ?></label>
                                        <div class="wm-options-checkboxes">
                                            <label class="sb-module-checkbox-item">
                                                <input type="checkbox" name="auto_watermark" value="1" <?php checked($settings['auto_watermark']); ?>>
                                                <span><?php esc_html_e('Auto-watermark uploads', 'wpswitchboard'); ?></span>
                                            </label>
                                            <label class="sb-module-checkbox-item">
                                                <input type="checkbox" name="backup_originals" value="1" <?php checked($settings['backup_originals']); ?>>
                                                <span><?php esc_html_e('Backup originals', 'wpswitchboard'); ?></span>
                                            </label>
                                        </div>
                                    </div>
                                </div>
                            </form>
                        </div>
                        <div class="sb-module-card-footer">
                            <div></div>
                            <button type="submit" form="watermark-settings-form" class="sb-module-button sb-module-button-primary" id="save-settings-btn">
                                <?php esc_html_e('Save Settings', 'wpswitchboard'); ?>
                            </button>
                        </div>
                    </div>

                    <!-- Preview Card -->
                    <div class="sb-module-card" style="display: flex; flex-direction: column;">
                        <div class="sb-module-card-header">
                            <div class="sb-module-card-header-left">
                                <h2 class="sb-module-card-title"><?php esc_html_e('Preview', 'wpswitchboard'); ?></h2>
                            </div>
                            <div class="sb-module-card-header-right" style="display: flex; align-items: center; gap: 12px;">
                                <?php if ($media_categories_active && !empty($categories)): ?>
                                <select id="preview_category" name="preview_category" class="preview-category-select">
                                    <option value="0"><?php esc_html_e('Default Sample', 'wpswitchboard'); ?></option>
                                    <?php foreach ($categories as $cat):
                                        if ($cat->count > 0):
                                    ?>
                                    <option value="<?php echo esc_attr($cat->term_id); ?>" <?php selected($settings['preview_category'], $cat->term_id); ?>>
                                        <?php echo esc_html($cat->name); ?> (<?php echo intval($cat->count); ?>)
                                    </option>
                                    <?php
                                        endif;
                                    endforeach; ?>
                                </select>
                                <?php endif; ?>
                                <button type="button" id="refresh-preview-btn" class="sb-module-button sb-module-button-secondary sb-module-button-sm" disabled>
                                    <?php esc_html_e('Refresh', 'wpswitchboard'); ?>
                                </button>
                            </div>
                        </div>
                        <div class="sb-module-card-body" style="flex: 1; padding: 0; display: flex; flex-direction: column;">
                            <div class="watermark-preview-area" id="preview-area" style="flex: 1;">
                                <div class="preview-placeholder">
                                    <span class="dashicons dashicons-format-image"></span>
                                    <p><?php esc_html_e('Select a watermark to see preview', 'wpswitchboard'); ?></p>
                                </div>
                            </div>
                            <div class="preview-help-notice" style="padding: 16px 20px;">
                                <span class="dashicons dashicons-info"></span>
                                <?php printf(esc_html__('Add %s to filename to exclude from watermarking', 'wpswitchboard'), '<code>-sbwe</code>'); ?>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- Bulk Watermarking & Backup Management - Two Column -->
                <div class="sb-module-layout-2col" style="margin-top: 24px; align-items: stretch;">
                    <!-- Bulk Processing Card -->
                    <div class="sb-module-card" style="display: flex; flex-direction: column;">
                        <div class="sb-module-card-header">
                            <div class="sb-module-card-header-left">
                                <h2 class="sb-module-card-title"><?php esc_html_e('Bulk Watermarking', 'wpswitchboard'); ?></h2>
                                <p class="sb-module-card-desc" id="bulk-pending-count"><?php printf(esc_html__('%d eligible images', 'wpswitchboard'), $stats['eligible_pending']); ?></p>
                            </div>
                        </div>
                        <div class="sb-module-card-body" style="flex: 1;">
                            <p class="sb-module-form-help" style="margin: 0 0 16px 0;">
                                <?php esc_html_e('Apply watermarks to all existing images in your media library. Images smaller than the minimum size or excluded formats will be skipped.', 'wpswitchboard'); ?>
                            </p>

                            <!-- Progress Area (hidden initially) -->
                            <div id="bulk-progress-area" style="display: none; margin-bottom: 16px;">
                                <div class="progress-header">
                                    <span id="progress-status"><?php esc_html_e('Processing...', 'wpswitchboard'); ?></span>
                                    <span id="progress-count">0 / 0</span>
                                </div>
                                <div class="progress-bar-container">
                                    <div class="progress-bar" id="progress-bar" style="width: 0%;"></div>
                                </div>
                                <div class="progress-log" id="progress-log"></div>
                            </div>

                            <!-- Already Processed Info (shown when eligible=0 but processed>0) -->
                            <div id="already-processed-info" style="display: <?php echo ($stats['eligible_pending'] === 0 && $stats['eligible_processed'] > 0) ? 'block' : 'none'; ?>; margin-bottom: 16px; padding: 12px 16px; background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 8px;">
                                <p style="margin: 0 0 8px 0; font-size: 14px; color: #1e40af;">
                                    <strong><?php esc_html_e('All eligible images have been processed', 'wpswitchboard'); ?></strong>
                                </p>
                                <p style="margin: 0 0 12px 0; font-size: 13px; color: #3b82f6;">
                                    <?php printf(
                                        esc_html__('%d images in the selected categories have already been watermarked. To re-apply watermarks with different settings, you can either restore from backups or reset the processing status.', 'wpswitchboard'),
                                        $stats['eligible_processed']
                                    ); ?>
                                </p>
                                <button type="button" id="reset-status-btn" class="sb-module-button sb-module-button-secondary" style="font-size: 13px; padding: 6px 12px;">
                                    <?php esc_html_e('Reset Status to Re-watermark', 'wpswitchboard'); ?>
                                </button>
                            </div>

                            <!-- Confirmation -->
                            <label class="sb-module-checkbox-item" style="background: #fffbeb; border-color: #fcd34d;">
                                <input type="checkbox" id="bulk-confirm-checkbox">
                                <span><?php esc_html_e('I understand this permanently modifies images', 'wpswitchboard'); ?></span>
                            </label>

                            <div style="margin-top: 16px; display: flex; gap: 8px;">
                                <button type="button" id="start-bulk-btn" class="sb-module-button sb-module-button-primary" disabled>
                                    <?php esc_html_e('Start Watermarking', 'wpswitchboard'); ?>
                                </button>
                                <button type="button" id="stop-bulk-btn" class="sb-module-button sb-module-button-secondary" style="display: none;">
                                    <?php esc_html_e('Stop', 'wpswitchboard'); ?>
                                </button>
                            </div>
                        </div>
                    </div>

                    <!-- Backup Management Card -->
                    <div class="sb-module-card" style="display: flex; flex-direction: column;">
                        <div class="sb-module-card-header">
                            <div class="sb-module-card-header-left">
                                <h2 class="sb-module-card-title"><?php esc_html_e('Backup Management', 'wpswitchboard'); ?></h2>
                                <p class="sb-module-card-desc"><?php printf(esc_html__('%d files (%s)', 'wpswitchboard'), $stats['backup_count'], $stats['backup_size_formatted']); ?></p>
                            </div>
                        </div>
                        <div class="sb-module-card-body" style="flex: 1;">
                            <p class="sb-module-form-help" style="margin: 0 0 16px 0;">
                                <?php esc_html_e('Original images are backed up before watermarking. You can restore them anytime or delete backups to free up disk space.', 'wpswitchboard'); ?>
                            </p>

                            <!-- Restore Progress (hidden initially) -->
                            <div id="restore-progress-area" style="display: none; margin-bottom: 16px;">
                                <div class="progress-header">
                                    <span id="restore-progress-status"><?php esc_html_e('Restoring...', 'wpswitchboard'); ?></span>
                                    <span id="restore-progress-count">0 / 0</span>
                                </div>
                                <div class="progress-bar-container">
                                    <div class="progress-bar" id="restore-progress-bar" style="width: 0%;"></div>
                                </div>
                            </div>

                            <p style="margin: 0 0 16px 0; font-size: 13px; color: #64748b;">
                                <?php esc_html_e('Location:', 'wpswitchboard'); ?>
                                <code style="background: #f1f5f9; padding: 4px 8px; border-radius: 4px; font-size: 12px;">/wp-content/switchboard-watermark-backups/</code>
                            </p>

                            <div style="display: flex; gap: 8px;">
                                <button type="button" id="restore-all-btn" class="sb-module-button sb-module-button-secondary" <?php echo $stats['backup_count'] === 0 ? 'disabled' : ''; ?>>
                                    <?php esc_html_e('Restore Originals', 'wpswitchboard'); ?>
                                </button>
                                <button type="button" id="delete-backups-btn" class="sb-module-button sb-module-button-danger" <?php echo $stats['backup_count'] === 0 ? 'disabled' : ''; ?>>
                                    <?php esc_html_e('Delete Backups', 'wpswitchboard'); ?>
                                </button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <?php
    }

    /**
     * Get inline CSS
     */
    private function get_inline_css() {
        return '
        /* Watermark Module Styles */
        .watermark-image-upload {
            display: flex;
            flex-direction: column;
            gap: 12px;
        }

        .watermark-image-preview {
            width: 200px;
            height: 150px;
            border: 2px dashed #e2e8f0;
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            background: #f8fafc;
            overflow: hidden;
        }

        .watermark-image-preview img {
            max-width: 100%;
            max-height: 100%;
            object-fit: contain;
        }

        .watermark-image-preview .no-image {
            color: #94a3b8;
            font-size: 13px;
        }

        .watermark-image-buttons {
            display: flex;
            gap: 8px;
        }

        /* Position Grid */
        .position-grid {
            display: grid;
            grid-template-columns: repeat(3, 40px);
            gap: 8px;
            background: #f8fafc;
            padding: 12px;
            border-radius: 8px;
            width: fit-content;
        }

        .position-option {
            width: 40px;
            height: 40px;
            border: 2px solid #e2e8f0;
            border-radius: 6px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.15s ease;
            background: white;
        }

        .position-option:hover {
            border-color: #cbd5e1;
        }

        .position-option.active {
            border-color: #3b82f6;
            background: #eff6ff;
        }

        .position-option input {
            display: none;
        }

        .position-dot {
            width: 10px;
            height: 10px;
            border-radius: 50%;
            background: #cbd5e1;
        }

        .position-option.active .position-dot {
            background: #3b82f6;
        }

        /* Range Inputs */
        .sb-module-range {
            width: 100%;
            height: 6px;
            border-radius: 3px;
            background: #e2e8f0;
            outline: none;
            -webkit-appearance: none;
        }

        .sb-module-range::-webkit-slider-thumb {
            -webkit-appearance: none;
            width: 18px;
            height: 18px;
            border-radius: 50%;
            background: #3b82f6;
            cursor: pointer;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }

        .range-value {
            float: right;
            font-weight: 500;
            color: #3b82f6;
        }

        /* Size Inputs */
        .size-inputs {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .size-input-group {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .size-input-group .sb-module-input {
            width: 80px;
        }

        /* Format Checkboxes */
        .format-checkboxes {
            display: flex;
            gap: 16px;
            flex-wrap: wrap;
        }

        .format-checkbox {
            display: flex;
            align-items: center;
            gap: 6px;
            cursor: pointer;
        }

        .format-checkbox input[type="checkbox"] {
            width: 16px;
            height: 16px;
        }

        /* Toggle Labels */
        .sb-module-toggle-label {
            display: flex;
            justify-content: space-between;
            align-items: flex-start;
            gap: 16px;
            padding: 16px;
            background: #f8fafc;
            border-radius: 8px;
            cursor: pointer;
        }

        .sb-module-toggle-text {
            flex: 1;
        }

        .sb-module-toggle-text .sb-module-form-desc {
            margin-top: 4px;
        }

        /* Preview Category Select (in header) */
        .preview-category-select {
            padding: 6px 28px 6px 10px;
            border: 1px solid #d1d5db;
            border-radius: 6px;
            font-size: 12px;
            background: white;
            color: #374151;
            cursor: pointer;
            min-width: 140px;
            appearance: none;
            -webkit-appearance: none;
            -moz-appearance: none;
            background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http%3A//www.w3.org/2000/svg%27 width=%2712%27 height=%2712%27 viewBox=%270 0 12 12%27%3E%3Cpath fill=%27%236b7280%27 d=%27M2.5 4.5L6 8l3.5-3.5%27/%3E%3C/svg%3E");
            background-repeat: no-repeat;
            background-position: right 8px center;
        }

        .preview-category-select:hover {
            border-color: #9ca3af;
        }

        .preview-category-select:focus {
            outline: none;
            border-color: #3b82f6;
            box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
        }

        /* Preview Area */
        .watermark-preview-area {
            background: #f8fafc;
            border: 2px dashed #e2e8f0;
            border-radius: 6px;
            min-height: 300px;
            display: flex;
            align-items: center;
            justify-content: center;
            overflow: hidden;
            position: relative;
            margin: 16px;
            padding: 24px;
        }

        .watermark-preview-area img {
            max-width: 100%;
            max-height: 400px;
            object-fit: contain;
            border-radius: 4px;
        }

        .preview-placeholder {
            text-align: center;
            color: #94a3b8;
        }

        .preview-placeholder .dashicons {
            font-size: 48px;
            width: 48px;
            height: 48px;
            margin-bottom: 12px;
        }

        /* Progress Bar */
        .progress-header {
            display: flex;
            justify-content: space-between;
            margin-bottom: 8px;
            font-weight: 500;
        }

        .progress-bar-container {
            height: 24px;
            background: #e2e8f0;
            border-radius: 12px;
            overflow: hidden;
        }

        .progress-bar {
            height: 100%;
            background: linear-gradient(90deg, #3b82f6, #2563eb);
            border-radius: 12px;
            transition: width 0.3s ease;
        }

        .progress-details {
            margin-top: 8px;
            font-size: 13px;
            color: #64748b;
        }

        .progress-log {
            margin-top: 12px;
            max-height: 150px;
            overflow-y: auto;
            font-size: 12px;
            font-family: monospace;
            background: #1e293b;
            color: #94a3b8;
            padding: 12px;
            border-radius: 6px;
        }

        .progress-log .log-success {
            color: #4ade80;
        }

        .progress-log .log-error {
            color: #f87171;
        }

        .progress-log .log-skip {
            color: #fbbf24;
        }

        /* Confirmation */
        .confirmation-checkbox {
            display: flex;
            align-items: flex-start;
            gap: 10px;
            cursor: pointer;
            padding: 16px;
            background: #fef3c7;
            border-radius: 8px;
            border: 1px solid #fcd34d;
        }

        .confirmation-checkbox input {
            margin-top: 3px;
            width: 18px;
            height: 18px;
        }

        /* Backup Stats */
        .backup-stats {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }

        .backup-stat {
            display: flex;
            gap: 8px;
            font-size: 14px;
        }

        .backup-stat-label {
            font-weight: 500;
            color: #64748b;
        }

        .backup-stat code {
            background: #f1f5f9;
            padding: 2px 6px;
            border-radius: 4px;
            font-size: 12px;
        }

        /* Info Boxes */
        .sb-module-info-box {
            display: flex;
            gap: 12px;
            padding: 16px;
            border-radius: 8px;
        }

        .sb-module-info-box-info {
            background: #eff6ff;
            border: 1px solid #bfdbfe;
        }

        .sb-module-info-box-warning {
            background: #fffbeb;
            border: 1px solid #fcd34d;
        }

        .sb-module-info-box-tip {
            background: #f0fdf4;
            border: 1px solid #86efac;
        }

        .sb-module-info-box-icon {
            font-size: 20px;
            flex-shrink: 0;
        }

        .sb-module-info-box-content strong {
            display: block;
            margin-bottom: 4px;
        }

        .sb-module-info-box-content p {
            margin: 0;
            font-size: 14px;
            color: #475569;
        }

        /* Form Groups */
        .sb-module-form-group {
            margin-bottom: 24px;
        }

        .sb-module-form-label {
            display: block;
            font-weight: 500;
            margin-bottom: 8px;
            color: #334155;
        }

        .sb-module-form-desc {
            font-size: 13px;
            color: #64748b;
            margin: 0 0 8px 0;
        }

        .sb-module-input {
            padding: 8px 12px;
            border: 1px solid #e2e8f0;
            border-radius: 6px;
            font-size: 14px;
            transition: border-color 0.15s ease;
        }

        .sb-module-input:focus {
            outline: none;
            border-color: #3b82f6;
            box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
        }

        .sb-module-form-actions {
            margin-top: 24px;
            padding-top: 24px;
            border-top: 1px solid #e2e8f0;
        }

        /* Preview Help Notice */
        .preview-help-notice {
            display: flex;
            align-items: center;
            gap: 10px;
            background: #f0f9ff;
            border-top: 1px solid #e0f2fe;
            font-size: 13px;
            color: #0369a1;
        }

        .preview-help-notice .dashicons {
            font-size: 16px;
            width: 16px;
            height: 16px;
            color: #0ea5e9;
        }

        .preview-help-notice code {
            background: #e0f2fe;
            padding: 2px 6px;
            border-radius: 4px;
            font-family: monospace;
            font-size: 12px;
            color: #0c4a6e;
        }

        /* Watermark Type Tabs (Header) */
        .watermark-type-tabs-header {
            display: flex;
            background: #f1f5f9;
            border-radius: 6px;
            padding: 3px;
        }

        .watermark-type-tab-header {
            display: flex;
            align-items: center;
            gap: 6px;
            padding: 6px 12px;
            border: none;
            border-radius: 4px;
            background: transparent;
            cursor: pointer;
            font-size: 13px;
            font-weight: 500;
            color: #64748b;
            transition: all 0.15s ease;
        }

        .watermark-type-tab-header:hover {
            color: #334155;
        }

        .watermark-type-tab-header.active {
            background: white;
            color: #1d4ed8;
            box-shadow: 0 1px 2px rgba(0,0,0,0.05);
        }

        .watermark-type-tab-header svg {
            flex-shrink: 0;
        }

        /* Margin Controls */
        .margin-controls {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 8px;
            padding: 16px;
            background: #f8fafc;
            border-radius: 8px;
            max-width: 300px;
        }

        .margin-control-top,
        .margin-control-bottom {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 4px;
        }

        .margin-control-middle {
            display: flex;
            align-items: center;
            gap: 16px;
        }

        .margin-control-left,
        .margin-control-right {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 4px;
        }

        .margin-input {
            width: 60px;
            padding: 6px 8px;
            border: 1px solid #d1d5db;
            border-radius: 4px;
            text-align: center;
            font-size: 13px;
        }

        .margin-input:focus {
            outline: none;
            border-color: #3b82f6;
            box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
        }

        .margin-label {
            font-size: 11px;
            color: #64748b;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }

        .margin-preview-box {
            width: 80px;
            height: 60px;
            background: #e2e8f0;
            border-radius: 4px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: #94a3b8;
        }

        .margin-preview-box .dashicons {
            font-size: 24px;
            width: 24px;
            height: 24px;
        }

        /* Apply Mode Options */
        .apply-mode-options {
            display: flex;
            gap: 12px;
        }

        .apply-mode-option {
            flex: 1;
            display: flex;
            align-items: flex-start;
            gap: 12px;
            padding: 16px;
            border: 2px solid #e2e8f0;
            border-radius: 8px;
            background: white;
            cursor: pointer;
            transition: all 0.15s ease;
        }

        .apply-mode-option:hover {
            border-color: #cbd5e1;
        }

        .apply-mode-option.active {
            border-color: #3b82f6;
            background: #eff6ff;
        }

        .apply-mode-option input {
            display: none;
        }

        .apply-mode-icon {
            flex-shrink: 0;
            width: 40px;
            height: 40px;
            display: flex;
            align-items: center;
            justify-content: center;
            background: #f1f5f9;
            border-radius: 8px;
            color: #64748b;
        }

        .apply-mode-option.active .apply-mode-icon {
            background: #dbeafe;
            color: #2563eb;
        }

        .apply-mode-text {
            display: flex;
            flex-direction: column;
            gap: 4px;
        }

        .apply-mode-text strong {
            font-size: 14px;
            color: #1e293b;
        }

        .apply-mode-text small {
            font-size: 12px;
            color: #64748b;
        }

        /* Category Checkboxes */
        .category-checkboxes {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            padding: 16px;
            background: #f8fafc;
            border-radius: 8px;
            border: 1px solid #e2e8f0;
        }

        /* Compact Form Layout */
        .watermark-form-row {
            display: flex;
            gap: 24px;
            margin-bottom: 20px;
            align-items: flex-start;
        }

        .watermark-form-col {
            flex: 1;
            min-width: 0;
        }

        /* Compact Margin Controls */
        .margin-controls-compact {
            display: flex;
            flex-direction: column;
            gap: 8px;
            background: #f8fafc;
            padding: 12px;
            border-radius: 6px;
        }

        .margin-row {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .margin-label-inline {
            font-size: 11px;
            font-weight: 600;
            color: #64748b;
            width: 14px;
            text-align: center;
        }

        .margin-input-compact {
            width: 50px;
            padding: 6px 8px;
            border: 1px solid #d1d5db;
            border-radius: 4px;
            text-align: center;
            font-size: 13px;
        }

        .margin-input-compact:focus {
            outline: none;
            border-color: #3b82f6;
        }

        /* Compact Apply Mode */
        .apply-mode-options-compact {
            display: flex;
            gap: 8px;
        }

        .apply-mode-option-compact {
            display: flex;
            align-items: center;
            gap: 6px;
            padding: 8px 12px;
            border: 1px solid #e2e8f0;
            border-radius: 6px;
            cursor: pointer;
            font-size: 13px;
            transition: all 0.15s;
        }

        .apply-mode-option-compact:hover {
            border-color: #cbd5e1;
        }

        .apply-mode-option-compact.active {
            border-color: #3b82f6;
            background: #eff6ff;
            color: #1d4ed8;
        }

        .apply-mode-option-compact input {
            display: none;
        }

        /* Compact Category Checkboxes */
        .category-checkboxes-compact {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
        }

        .category-checkbox-item {
            display: flex;
            align-items: center;
            gap: 6px;
            padding: 6px 10px;
            background: #f8fafc;
            border: 1px solid #e2e8f0;
            border-radius: 4px;
            font-size: 12px;
            cursor: pointer;
        }

        .category-checkbox-item:has(input:checked) {
            background: #eff6ff;
            border-color: #3b82f6;
        }

        .category-count {
            font-size: 10px;
            color: #94a3b8;
            background: #f1f5f9;
            padding: 1px 5px;
            border-radius: 8px;
            font-weight: 500;
            margin-left: 2px;
        }

        .category-checkbox-item:has(input:checked) .category-count {
            background: #dbeafe;
            color: #3b82f6;
        }

        /* Compact Format Checkboxes */
        .format-checkbox-compact {
            display: flex;
            align-items: center;
            gap: 4px;
            font-size: 13px;
            cursor: pointer;
        }

        /* Color Input */
        .watermark-color-input {
            width: 36px;
            height: 36px;
            padding: 2px;
            border: 1px solid #d1d5db;
            border-radius: 4px;
            cursor: pointer;
        }

        /* ===== NEW WATERMARK SETTINGS LAYOUT ===== */

        /* Section container */
        .wm-section {
            margin-bottom: 20px;
        }

        .wm-section-divider {
            margin-top: 24px;
            padding-top: 20px;
            border-top: 1px solid #e5e7eb;
        }

        /* Row layouts */
        .wm-row {
            display: grid;
            gap: 20px;
            margin-bottom: 20px;
        }

        .wm-row-3col {
            grid-template-columns: repeat(3, 1fr);
        }

        .wm-row-4col {
            grid-template-columns: 2fr 1fr 1fr 1fr;
        }

        /* Column */
        .wm-col {
            min-width: 0;
        }

        .wm-col-flex2 {
            grid-column: span 1;
        }

        /* Large checkbox styling */
        .wm-checkbox-large {
            padding: 10px 14px;
            background: #f8fafc;
            border: 1px solid #e2e8f0;
            border-radius: 6px;
            margin-top: 0;
        }

        .wm-checkbox-large:has(input:checked) {
            background: #eff6ff;
            border-color: #3b82f6;
        }

        /* Input with unit */
        .wm-input-unit {
            display: flex;
            align-items: center;
            gap: 6px;
        }

        .wm-input-unit .sb-module-input {
            width: 70px;
        }

        .wm-unit {
            color: #64748b;
            font-size: 12px;
        }

        /* Color input improved */
        .wm-color-input {
            width: 100%;
            height: 38px;
            padding: 4px;
            border: 1px solid #d1d5db;
            border-radius: 6px;
            cursor: pointer;
        }

        /* Size inputs */
        .wm-size-inputs {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .wm-size-inputs .sb-module-input {
            width: 70px;
        }

        /* Quality input */
        .wm-quality-input {
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .wm-quality-input .sb-module-range {
            flex: 1;
            min-width: 80px;
        }

        .wm-quality-input span {
            font-size: 13px;
            color: #3b82f6;
            font-weight: 500;
            min-width: 40px;
        }

        .wm-size-x {
            color: #9ca3af;
            font-size: 14px;
        }

        /* Format checkboxes */
        .wm-format-checkboxes {
            display: flex;
            flex-wrap: wrap;
            gap: 12px;
        }

        /* Options checkboxes */
        .wm-options-checkboxes {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }

        /* Position grid for text mode */
        .position-grid-text {
            max-width: 120px;
        }

        /* Margin Grid - matches position grid styling */
        .margin-grid {
            display: grid;
            grid-template-columns: repeat(3, 40px);
            gap: 8px;
            background: #f8fafc;
            padding: 12px;
            border-radius: 8px;
            width: fit-content;
        }

        .margin-grid-cell {
            width: 40px;
            height: 40px;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .margin-grid-corner {
            /* Empty corners - no styling needed */
        }

        .margin-grid-center {
            background: #f1f5f9;
            border: 2px dashed #e2e8f0;
            border-radius: 6px;
        }

        .margin-grid-input {
            width: 40px;
            height: 40px;
            padding: 0;
            border: 2px solid #e2e8f0;
            border-radius: 6px;
            font-size: 13px;
            font-weight: 500;
            text-align: center;
            background: white;
            color: #374151;
            -moz-appearance: textfield;
            transition: all 0.15s ease;
        }

        .margin-grid-input::-webkit-outer-spin-button,
        .margin-grid-input::-webkit-inner-spin-button {
            -webkit-appearance: none;
            margin: 0;
        }

        .margin-grid-input:hover {
            border-color: #cbd5e1;
        }

        .margin-grid-input:focus {
            outline: none;
            border-color: #3b82f6;
            background: #eff6ff;
        }

        /* Responsive adjustments */
        @media (max-width: 1200px) {
            .wm-row-3col {
                grid-template-columns: repeat(2, 1fr);
            }
            .wm-row-4col {
                grid-template-columns: 1fr 1fr;
            }
        }

        @media (max-width: 768px) {
            .wm-row-3col,
            .wm-row-4col {
                grid-template-columns: 1fr;
            }
        }
        ';
    }

    /**
     * Output inline JavaScript
     */
    public function output_inline_js() {
        ?>
        <script>
        (function($) {
            'use strict';

            const WatermarkModule = {
                isProcessing: false,
                shouldStop: false,
                processedCount: 0,
                totalCount: 0,

                init: function() {
                    this.bindEvents();
                    this.initRangeSliders();
                    this.initPositionGrid();
                    this.updatePreviewButton();
                },

                bindEvents: function() {
                    const self = this;

                    // Watermark type tabs (header)
                    $('.watermark-type-tab-header').on('click', function() {
                        const type = $(this).data('type');
                        $('.watermark-type-tab-header').removeClass('active');
                        $(this).addClass('active');
                        $('#watermark_type').val(type);

                        if (type === 'image') {
                            $('#image-watermark-settings').show();
                            $('#text-watermark-settings').hide();
                        } else {
                            $('#image-watermark-settings').hide();
                            $('#text-watermark-settings').show();
                        }
                        self.updatePreviewButton();
                        self.updateBulkButton();
                    });

                    // Apply mode options (compact version)
                    $('input[name="apply_mode"]').on('change', function() {
                        const mode = $(this).val();
                        $('.apply-mode-option-compact').removeClass('active');
                        $(this).closest('.apply-mode-option-compact').addClass('active');

                        if (mode === 'categories') {
                            $('#category-selector').slideDown(200);
                        } else {
                            $('#category-selector').slideUp(200);
                        }
                    });

                    // Preview category change
                    $('#preview_category').on('change', function() {
                        self.refreshPreview();
                    });

                    // Text watermark inputs
                    $('#watermark_text, #text_font_size, #text_color').on('input change', function() {
                        self.debouncedPreview();
                        self.updateBulkButton();
                    });

                    // Margin inputs (compact version)
                    $('.margin-input-compact').on('input change', function() {
                        self.debouncedPreview();
                    });

                    // Grayscale checkbox
                    $('input[name="grayscale"]').on('change', function() {
                        self.debouncedPreview();
                    });

                    // Watermark image upload
                    $('#select-watermark-btn').on('click', function() {
                        self.openMediaUploader();
                    });

                    $('#remove-watermark-btn').on('click', function() {
                        self.removeWatermarkImage();
                    });

                    // Settings form
                    $('#watermark-settings-form').on('submit', function(e) {
                        e.preventDefault();
                        self.saveSettings();
                    });

                    // Bulk confirmation
                    $('#bulk-confirm-checkbox').on('change', function() {
                        self.updateBulkButton();
                    });

                    // Bulk processing
                    $('#start-bulk-btn').on('click', function() {
                        self.startBulkProcessing();
                    });

                    $('#stop-bulk-btn').on('click', function() {
                        self.shouldStop = true;
                        $(this).prop('disabled', true).text('<?php esc_html_e('Stopping...', 'wpswitchboard'); ?>');
                    });

                    // Restore
                    $('#restore-all-btn').on('click', function() {
                        if (confirm('<?php esc_html_e('This will restore all watermarked images to their original versions. Continue?', 'wpswitchboard'); ?>')) {
                            self.startRestore();
                        }
                    });

                    // Delete backups
                    $('#delete-backups-btn').on('click', function() {
                        if (confirm('<?php esc_html_e('This will permanently delete all backup files. This cannot be undone. Continue?', 'wpswitchboard'); ?>')) {
                            self.deleteBackups();
                        }
                    });

                    // Reset status for re-watermarking
                    $('#reset-status-btn').on('click', function() {
                        if (confirm('<?php esc_html_e('This will reset the watermark status for images in the selected categories, allowing you to re-apply watermarks. Note: This does not restore original images - use "Restore Originals" for that. Continue?', 'wpswitchboard'); ?>')) {
                            self.resetStatus();
                        }
                    });

                    // Preview
                    $('#refresh-preview-btn').on('click', function() {
                        self.refreshPreview();
                    });
                },

                initRangeSliders: function() {
                    const self = this;

                    // Quality slider (output quality)
                    $('#image_quality').on('input', function() {
                        $('#quality-value').text($(this).val() + '%');
                    });

                    // Image mode sliders
                    $('#opacity').on('input', function() {
                        $('#opacity-value').text($(this).val() + '%');
                        self.debouncedPreview();
                    });

                    $('#scale').on('input', function() {
                        $('#scale-value').text($(this).val() + '%');
                        self.debouncedPreview();
                    });

                    $('#margin').on('input', function() {
                        $('#margin-value').text($(this).val() + 'px');
                        self.debouncedPreview();
                    });

                    // Text mode sliders
                    $('#opacity_text').on('input', function() {
                        $('#opacity-value-text').text($(this).val() + '%');
                        self.debouncedPreview();
                    });

                    $('#scale_text').on('input', function() {
                        $('#scale-value-text').text($(this).val() + '%');
                        self.debouncedPreview();
                    });

                    // Margin inputs - both modes
                    $('.margin-input-compact').on('input change', function() {
                        self.debouncedPreview();
                    });

                    // Text watermark inputs
                    $('#watermark_text, #text_font_size, #text_color').on('input change', function() {
                        self.debouncedPreview();
                    });

                    // Text shadow checkbox
                    $('input[name="text_shadow"]').on('change', function() {
                        self.debouncedPreview();
                    });
                },

                initPositionGrid: function() {
                    const self = this;
                    // Both position grids (image and text modes)
                    $('.position-option input').on('change', function() {
                        // Update active state within the same position grid only
                        $(this).closest('.position-grid').find('.position-option').removeClass('active');
                        $(this).closest('.position-option').addClass('active');
                        self.debouncedPreview();
                    });
                },

                // Debounced preview - waits 300ms after last change before refreshing
                previewTimeout: null,
                debouncedPreview: function() {
                    const self = this;
                    const watermarkType = $('#watermark_type').val();
                    let hasWatermark = false;

                    if (watermarkType === 'image') {
                        hasWatermark = $('#watermark_image_id').val() !== '0' && $('#watermark_image_id').val() !== '';
                    } else {
                        hasWatermark = $('#watermark_text').val().trim() !== '';
                    }

                    if (!hasWatermark) return;

                    clearTimeout(this.previewTimeout);
                    this.previewTimeout = setTimeout(function() {
                        self.refreshPreview();
                    }, 300);
                },

                updatePreviewButton: function() {
                    const watermarkType = $('#watermark_type').val();
                    let hasWatermark = false;

                    if (watermarkType === 'image') {
                        hasWatermark = $('#watermark_image_id').val() !== '0' && $('#watermark_image_id').val() !== '';
                    } else {
                        hasWatermark = $('#watermark_text').val().trim() !== '';
                    }

                    $('#refresh-preview-btn').prop('disabled', !hasWatermark);

                    if (hasWatermark) {
                        this.refreshPreview();
                    }
                },

                openMediaUploader: function() {
                    const self = this;

                    const frame = wp.media({
                        title: '<?php esc_html_e('Select Watermark Image', 'wpswitchboard'); ?>',
                        button: { text: '<?php esc_html_e('Use as Watermark', 'wpswitchboard'); ?>' },
                        multiple: false,
                        library: { type: 'image' }
                    });

                    frame.on('select', function() {
                        const attachment = frame.state().get('selection').first().toJSON();
                        $('#watermark_image_id').val(attachment.id);
                        $('#watermark-preview').html('<img src="' + attachment.url + '" alt="Watermark">');
                        $('#remove-watermark-btn').show();
                        self.updatePreviewButton();
                        self.updateBulkButton();
                    });

                    frame.open();
                },

                removeWatermarkImage: function() {
                    $('#watermark_image_id').val('0');
                    $('#watermark-preview').html('<span class="no-image"><?php esc_html_e('No watermark selected', 'wpswitchboard'); ?></span>');
                    $('#remove-watermark-btn').hide();
                    this.updatePreviewButton();
                    this.updateBulkButton();
                    $('#preview-area').html('<div class="preview-placeholder"><span class="dashicons dashicons-format-image"></span><p><?php esc_html_e('Select a watermark image to see preview', 'wpswitchboard'); ?></p></div>');
                },

                updateBulkButton: function() {
                    const watermarkType = $('#watermark_type').val();
                    let hasWatermark = false;

                    if (watermarkType === 'text') {
                        hasWatermark = $('#watermark_text').val().trim() !== '';
                    } else {
                        hasWatermark = $('#watermark_image_id').val() !== '0' && $('#watermark_image_id').val() !== '';
                    }

                    const isConfirmed = $('#bulk-confirm-checkbox').is(':checked');
                    $('#start-bulk-btn').prop('disabled', !hasWatermark || !isConfirmed);
                },

                saveSettings: function() {
                    const self = this;
                    const $btn = $('#save-settings-btn');
                    const originalText = $btn.text();
                    const watermarkType = $('#watermark_type').val();
                    const isTextMode = watermarkType === 'text';

                    $btn.prop('disabled', true).text('<?php esc_html_e('Saving...', 'wpswitchboard'); ?>');

                    const formData = {
                        action: 'switchboard_watermark_save_settings',
                        nonce: $('#watermark_nonce').val(),
                        // Watermark type
                        watermark_type: watermarkType,
                        watermark_image_id: $('#watermark_image_id').val(),
                        // Text watermark settings
                        watermark_text: $('#watermark_text').val(),
                        text_font_size: $('#text_font_size').val(),
                        text_color: $('#text_color').val(),
                        text_shadow: $('input[name="text_shadow"]').is(':checked') ? 1 : 0,
                        // Position & layout (use text inputs if in text mode)
                        position: isTextMode ? $('input[name="position_text"]:checked').val() : $('input[name="position"]:checked').val(),
                        opacity: isTextMode ? $('#opacity_text').val() : $('#opacity').val(),
                        scale: isTextMode ? $('#scale_text').val() : $('#scale').val(),
                        // Separate margins (use text inputs if in text mode)
                        margin_top: isTextMode ? $('#margin_top_text').val() : $('#margin_top').val(),
                        margin_right: isTextMode ? $('#margin_right_text').val() : $('#margin_right').val(),
                        margin_bottom: isTextMode ? $('#margin_bottom_text').val() : $('#margin_bottom').val(),
                        margin_left: isTextMode ? $('#margin_left_text').val() : $('#margin_left').val(),
                        margin: $('#margin').val(),
                        // Image filters
                        grayscale: $('input[name="grayscale"]').is(':checked') ? 1 : 0,
                        // Category-based watermarking
                        apply_mode: $('input[name="apply_mode"]:checked').val(),
                        apply_categories: $('input[name="apply_categories[]"]:checked').map(function() { return $(this).val(); }).get(),
                        // General settings
                        min_image_width: $('#min_image_width').val(),
                        min_image_height: $('#min_image_height').val(),
                        image_quality: $('#image_quality').val(),
                        auto_watermark: $('input[name="auto_watermark"]').is(':checked') ? 1 : 0,
                        backup_originals: $('input[name="backup_originals"]').is(':checked') ? 1 : 0,
                        formats: $('input[name="formats[]"]:checked').map(function() { return $(this).val(); }).get(),
                        // Preview settings
                        preview_category: $('#preview_category').val() || 0
                    };

                    $.ajax({
                        url: ajaxurl,
                        type: 'POST',
                        data: formData,
                        success: function(response) {
                            if (response.success) {
                                $btn.text('<?php esc_html_e('Saved!', 'wpswitchboard'); ?>');
                                self.refreshStats();
                                setTimeout(function() {
                                    $btn.prop('disabled', false).text(originalText);
                                }, 2000);
                            } else {
                                alert('<?php esc_html_e('Failed to save settings', 'wpswitchboard'); ?>');
                                $btn.prop('disabled', false).text(originalText);
                            }
                        },
                        error: function() {
                            alert('<?php esc_html_e('Failed to save settings', 'wpswitchboard'); ?>');
                            $btn.prop('disabled', false).text(originalText);
                        }
                    });
                },

                refreshPreview: function() {
                    const self = this;
                    const $btn = $('#refresh-preview-btn');
                    const $area = $('#preview-area');
                    const watermarkType = $('#watermark_type').val();
                    const isTextMode = watermarkType === 'text';

                    $btn.prop('disabled', true);
                    $area.html('<div class="preview-placeholder"><span class="dashicons dashicons-update spin"></span><p><?php esc_html_e('Generating preview...', 'wpswitchboard'); ?></p></div>');

                    // Build data object with all settings - use correct inputs based on mode
                    const data = {
                        action: 'switchboard_watermark_preview',
                        nonce: $('#watermark_nonce').val(),
                        watermark_type: watermarkType,
                        position: isTextMode ? $('input[name="position_text"]:checked').val() : $('input[name="position"]:checked').val(),
                        opacity: isTextMode ? $('#opacity_text').val() : $('#opacity').val(),
                        scale: isTextMode ? $('#scale_text').val() : $('#scale').val(),
                        // Separate margins - use correct inputs based on mode
                        margin_top: isTextMode ? ($('#margin_top_text').val() || 10) : ($('#margin_top').val() || 10),
                        margin_right: isTextMode ? ($('#margin_right_text').val() || 10) : ($('#margin_right').val() || 10),
                        margin_bottom: isTextMode ? ($('#margin_bottom_text').val() || 10) : ($('#margin_bottom').val() || 10),
                        margin_left: isTextMode ? ($('#margin_left_text').val() || 10) : ($('#margin_left').val() || 10),
                        // Preview category
                        preview_category: $('#preview_category').val() || 0
                    };

                    // Add type-specific settings
                    if (watermarkType === 'image') {
                        data.watermark_image_id = $('#watermark_image_id').val();
                        data.grayscale = $('input[name="grayscale"]').is(':checked') ? 1 : 0;
                    } else {
                        data.watermark_text = $('#watermark_text').val();
                        data.text_font_size = $('#text_font_size').val();
                        data.text_color = $('#text_color').val();
                        data.text_shadow = $('input[name="text_shadow"]').is(':checked') ? 1 : 0;
                    }

                    $.ajax({
                        url: ajaxurl,
                        type: 'POST',
                        data: data,
                        success: function(response) {
                            if (response.success && response.data.preview_url) {
                                $area.html('<img src="' + response.data.preview_url + '?' + Date.now() + '" alt="Preview">');
                            } else {
                                $area.html('<div class="preview-placeholder"><span class="dashicons dashicons-warning"></span><p>' + (response.data.message || '<?php esc_html_e('Failed to generate preview', 'wpswitchboard'); ?>') + '</p></div>');
                            }
                            $btn.prop('disabled', false);
                        },
                        error: function() {
                            $area.html('<div class="preview-placeholder"><span class="dashicons dashicons-warning"></span><p><?php esc_html_e('Failed to generate preview', 'wpswitchboard'); ?></p></div>');
                            $btn.prop('disabled', false);
                        }
                    });
                },

                startBulkProcessing: function() {
                    const self = this;

                    this.isProcessing = true;
                    this.shouldStop = false;
                    this.processedCount = 0;

                    // Show progress UI
                    $('#bulk-progress-area').show();
                    $('#start-bulk-btn').hide();
                    $('#stop-bulk-btn').show().prop('disabled', false).text('<?php esc_html_e('Stop', 'wpswitchboard'); ?>');
                    $('#bulk-confirm-checkbox').prop('disabled', true);
                    $('#progress-log').empty();

                    // Get total count first
                    $.ajax({
                        url: ajaxurl,
                        type: 'POST',
                        data: {
                            action: 'switchboard_watermark_get_stats',
                            nonce: $('#watermark_nonce').val()
                        },
                        success: function(response) {
                            if (response.success) {
                                self.totalCount = response.data.eligible_pending;
                                $('#progress-count').text('0 / ' + self.totalCount);
                                if (self.totalCount === 0) {
                                    self.finishProcessing('<?php esc_html_e('No eligible images to process', 'wpswitchboard'); ?>');
                                } else {
                                    self.processBatch();
                                }
                            }
                        }
                    });
                },

                processBatch: function() {
                    const self = this;

                    if (this.shouldStop) {
                        this.finishProcessing('<?php esc_html_e('Processing stopped by user', 'wpswitchboard'); ?>');
                        return;
                    }

                    $.ajax({
                        url: ajaxurl,
                        type: 'POST',
                        data: {
                            action: 'switchboard_watermark_process_batch',
                            nonce: $('#watermark_nonce').val(),
                            batch_size: 5
                        },
                        success: function(response) {
                            if (response.success) {
                                const data = response.data;

                                // Update progress - count all results (success + skipped + errors)
                                const batchTotal = data.results ? data.results.length : 0;
                                self.processedCount += batchTotal;
                                const percent = self.totalCount > 0 ? Math.round((self.processedCount / self.totalCount) * 100) : 0;

                                $('#progress-bar').css('width', percent + '%');
                                $('#progress-count').text(self.processedCount + ' / ' + self.totalCount);
                                $('#progress-status').text('<?php esc_html_e('Processing...', 'wpswitchboard'); ?> ' + percent + '%');

                                // Add to log
                                if (data.results && data.results.length > 0) {
                                    data.results.forEach(function(result) {
                                        const logClass = result.success ? 'log-success' : (result.skipped ? 'log-skip' : 'log-error');
                                        const icon = result.success ? 'Done:' : (result.skipped ? 'Skipped:' : 'Failed:');
                                        $('#progress-log').append('<div class="' + logClass + '">' + icon + ' ' + result.filename + (result.message ? ' - ' + result.message : '') + '</div>');
                                    });
                                    $('#progress-log').scrollTop($('#progress-log')[0].scrollHeight);
                                }

                                // Continue or finish
                                if (data.remaining > 0 && !self.shouldStop) {
                                    setTimeout(function() {
                                        self.processBatch();
                                    }, 100);
                                } else {
                                    self.finishProcessing('<?php esc_html_e('Processing complete!', 'wpswitchboard'); ?>');
                                }
                            } else {
                                self.finishProcessing('<?php esc_html_e('Error occurred during processing', 'wpswitchboard'); ?>');
                            }
                        },
                        error: function() {
                            self.finishProcessing('<?php esc_html_e('Error occurred during processing', 'wpswitchboard'); ?>');
                        }
                    });
                },

                finishProcessing: function(message) {
                    this.isProcessing = false;
                    $('#progress-status').text(message);
                    $('#stop-bulk-btn').hide();
                    $('#start-bulk-btn').show();
                    $('#bulk-confirm-checkbox').prop('disabled', false).prop('checked', false);
                    $('#start-bulk-btn').prop('disabled', true);

                    // Refresh stats
                    this.refreshStats();
                },

                startRestore: function() {
                    const self = this;

                    $('#restore-progress-area').show();
                    $('#restore-all-btn').prop('disabled', true);
                    $('#delete-backups-btn').prop('disabled', true);

                    this.restoreBatch();
                },

                restoreBatch: function() {
                    const self = this;

                    $.ajax({
                        url: ajaxurl,
                        type: 'POST',
                        data: {
                            action: 'switchboard_watermark_restore_batch',
                            nonce: $('#watermark_nonce').val(),
                            batch_size: 10
                        },
                        success: function(response) {
                            if (response.success) {
                                const data = response.data;
                                const percent = data.total > 0 ? Math.round(((data.total - data.remaining) / data.total) * 100) : 100;

                                $('#restore-progress-bar').css('width', percent + '%');
                                $('#restore-progress-count').text((data.total - data.remaining) + ' / ' + data.total);

                                if (data.remaining > 0) {
                                    setTimeout(function() {
                                        self.restoreBatch();
                                    }, 100);
                                } else {
                                    $('#restore-progress-status').text('<?php esc_html_e('Restore complete!', 'wpswitchboard'); ?>');
                                    self.refreshStats();
                                    setTimeout(function() {
                                        $('#restore-progress-area').hide();
                                        $('#restore-all-btn').prop('disabled', false);
                                        $('#delete-backups-btn').prop('disabled', false);
                                    }, 2000);
                                }
                            }
                        }
                    });
                },

                deleteBackups: function() {
                    const self = this;
                    const $btn = $('#delete-backups-btn');

                    $btn.prop('disabled', true).text('<?php esc_html_e('Deleting...', 'wpswitchboard'); ?>');

                    $.ajax({
                        url: ajaxurl,
                        type: 'POST',
                        data: {
                            action: 'switchboard_watermark_delete_backups',
                            nonce: $('#watermark_nonce').val()
                        },
                        success: function(response) {
                            if (response.success) {
                                alert('<?php esc_html_e('All backups deleted successfully', 'wpswitchboard'); ?>');
                                self.refreshStats();
                            } else {
                                alert('<?php esc_html_e('Failed to delete backups', 'wpswitchboard'); ?>');
                            }
                            $btn.prop('disabled', true).text('<?php esc_html_e('Delete All Backups', 'wpswitchboard'); ?>');
                        }
                    });
                },

                resetStatus: function() {
                    const self = this;
                    const $btn = $('#reset-status-btn');
                    const originalText = $btn.text();

                    $btn.prop('disabled', true).text('<?php esc_html_e('Resetting...', 'wpswitchboard'); ?>');

                    $.ajax({
                        url: ajaxurl,
                        type: 'POST',
                        data: {
                            action: 'switchboard_watermark_reset_status',
                            nonce: $('#watermark_nonce').val()
                        },
                        success: function(response) {
                            if (response.success) {
                                alert(response.data.message);
                                self.refreshStats();
                            } else {
                                alert(response.data.message || '<?php esc_html_e('Failed to reset status', 'wpswitchboard'); ?>');
                                $btn.prop('disabled', false).text(originalText);
                            }
                        },
                        error: function() {
                            alert('<?php esc_html_e('Failed to reset status', 'wpswitchboard'); ?>');
                            $btn.prop('disabled', false).text(originalText);
                        }
                    });
                },

                refreshStats: function() {
                    $.ajax({
                        url: ajaxurl,
                        type: 'POST',
                        data: {
                            action: 'switchboard_watermark_get_stats',
                            nonce: $('#watermark_nonce').val()
                        },
                        success: function(response) {
                            if (response.success) {
                                const data = response.data;
                                $('#stat-total-images').text(data.total_images.toLocaleString());
                                $('#stat-watermarked').text(data.watermarked.toLocaleString());
                                $('#stat-pending').text(data.pending.toLocaleString());
                                $('#stat-backup-size').text(data.backup_size_formatted);
                                $('#backup-count').text(data.backup_count.toLocaleString());
                                $('#backup-size').text(data.backup_size_formatted);

                                // Update eligible count in header
                                $('#bulk-pending-count').text(data.eligible_pending.toLocaleString() + ' <?php echo esc_js(__('eligible images', 'wpswitchboard')); ?>');

                                // Show/hide the already processed info box
                                if (data.eligible_pending === 0 && data.eligible_processed > 0) {
                                    $('#already-processed-info').show();
                                    $('#already-processed-info p:nth-child(2)').html(
                                        data.eligible_processed.toLocaleString() + ' <?php echo esc_js(__('images in the selected categories have already been watermarked. To re-apply watermarks with different settings, you can either restore from backups or reset the processing status.', 'wpswitchboard')); ?>'
                                    );
                                    $('#reset-status-btn').prop('disabled', false).text('<?php echo esc_js(__('Reset Status to Re-watermark', 'wpswitchboard')); ?>');
                                } else {
                                    $('#already-processed-info').hide();
                                }

                                // Update buttons
                                $('#restore-all-btn').prop('disabled', data.backup_count === 0);
                                $('#delete-backups-btn').prop('disabled', data.backup_count === 0);
                            }
                        }
                    });
                }
            };

            $(document).ready(function() {
                WatermarkModule.init();
            });

        })(jQuery);
        </script>
        <style>
        .dashicons.spin {
            animation: spin 1s linear infinite;
        }
        @keyframes spin {
            100% { transform: rotate(360deg); }
        }
        </style>
        <?php
    }

    /**
     * Get stats for the admin page
     */
    private function get_stats() {
        global $wpdb;

        // Total images (supported types only)
        $mime_types = $this->get_supported_mime_types();
        $placeholders = implode(',', array_fill(0, count($mime_types), '%s'));

        $total_images = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = 'attachment' AND post_mime_type IN ($placeholders)",
            ...$mime_types
        ));

        // Watermarked images
        $watermarked = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->postmeta} WHERE meta_key = %s",
            $this->watermarked_meta_key
        ));

        // Pending (excluding -sbwe files)
        $excluded_pattern = '%' . $this->exclusion_suffix . '%';
        $pending_args = array_merge(
            [$this->watermarked_meta_key],
            $mime_types,
            [$excluded_pattern]
        );
        $pending = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->posts} p
             LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = %s
             WHERE p.post_type = 'attachment'
             AND p.post_mime_type IN ($placeholders)
             AND pm.meta_id IS NULL
             AND p.guid NOT LIKE %s",
            ...$pending_args
        ));

        // Backup info
        $backup_count = 0;
        $backup_size = 0;

        if (is_dir($this->backup_dir)) {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($this->backup_dir, RecursiveDirectoryIterator::SKIP_DOTS)
            );
            foreach ($iterator as $file) {
                if ($file->isFile()) {
                    $backup_count++;
                    $backup_size += $file->getSize();
                }
            }
        }

        return [
            'total_images' => intval($total_images),
            'watermarked' => intval($watermarked),
            'pending' => intval($pending),
            'backup_count' => $backup_count,
            'backup_size' => $backup_size,
            'backup_size_formatted' => size_format($backup_size),
            'eligible_pending' => $this->get_eligible_pending_count(),
            'eligible_processed' => $this->get_eligible_processed_count(),
        ];
    }

    /**
     * Get count of pending images that are actually eligible for watermarking
     * based on category settings and size restrictions
     */
    private function get_eligible_pending_count() {
        global $wpdb;

        $apply_mode = isset($this->settings['apply_mode']) ? $this->settings['apply_mode'] : 'all';
        $mime_types = $this->get_supported_mime_types();
        $placeholders = implode(',', array_fill(0, count($mime_types), '%s'));
        $excluded_pattern = '%' . $this->exclusion_suffix . '%';

        // If mode is 'all', return total pending count
        if ($apply_mode === 'all') {
            $pending_args = array_merge(
                [$this->watermarked_meta_key],
                $mime_types,
                [$excluded_pattern]
            );
            return intval($wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM {$wpdb->posts} p
                 LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = %s
                 WHERE p.post_type = 'attachment'
                 AND p.post_mime_type IN ($placeholders)
                 AND pm.meta_id IS NULL
                 AND p.guid NOT LIKE %s",
                ...$pending_args
            )));
        }

        // If mode is 'categories', count only images in selected categories
        if ($apply_mode === 'categories') {
            $apply_categories = isset($this->settings['apply_categories']) ? (array) $this->settings['apply_categories'] : [];

            // If no categories selected, no images are eligible
            if (empty($apply_categories)) {
                return 0;
            }

            // Check if media_category taxonomy exists
            if (!taxonomy_exists('media_category')) {
                return 0;
            }

            // Build query to count pending images in selected categories
            $category_placeholders = implode(',', array_fill(0, count($apply_categories), '%d'));
            $query_args = array_merge(
                [$this->watermarked_meta_key],
                $mime_types,
                [$excluded_pattern],
                array_map('intval', $apply_categories)
            );

            return intval($wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(DISTINCT p.ID) FROM {$wpdb->posts} p
                 LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = %s
                 INNER JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
                 INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
                 WHERE p.post_type = 'attachment'
                 AND p.post_mime_type IN ($placeholders)
                 AND pm.meta_id IS NULL
                 AND p.guid NOT LIKE %s
                 AND tt.taxonomy = 'media_category'
                 AND tt.term_id IN ($category_placeholders)",
                ...$query_args
            )));
        }

        // Default: return total pending
        $pending_args = array_merge(
            [$this->watermarked_meta_key],
            $mime_types,
            [$excluded_pattern]
        );
        return intval($wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->posts} p
             LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = %s
             WHERE p.post_type = 'attachment'
             AND p.post_mime_type IN ($placeholders)
             AND pm.meta_id IS NULL
             AND p.guid NOT LIKE %s",
            ...$pending_args
        )));
    }

    /**
     * Get count of already processed images in selected categories
     * These can be reset for re-watermarking
     */
    private function get_eligible_processed_count() {
        global $wpdb;

        $apply_mode = isset($this->settings['apply_mode']) ? $this->settings['apply_mode'] : 'all';
        $mime_types = $this->get_supported_mime_types();
        $placeholders = implode(',', array_fill(0, count($mime_types), '%s'));
        $excluded_pattern = '%' . $this->exclusion_suffix . '%';

        // If mode is 'all', return total processed count
        if ($apply_mode === 'all') {
            $args = array_merge(
                [$this->watermarked_meta_key],
                $mime_types,
                [$excluded_pattern]
            );
            return intval($wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM {$wpdb->posts} p
                 INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = %s
                 WHERE p.post_type = 'attachment'
                 AND p.post_mime_type IN ($placeholders)
                 AND p.guid NOT LIKE %s",
                ...$args
            )));
        }

        // If mode is 'categories', count only processed images in selected categories
        if ($apply_mode === 'categories') {
            $apply_categories = isset($this->settings['apply_categories']) ? (array) $this->settings['apply_categories'] : [];

            if (empty($apply_categories)) {
                return 0;
            }

            if (!taxonomy_exists('media_category')) {
                return 0;
            }

            $category_placeholders = implode(',', array_fill(0, count($apply_categories), '%d'));
            $query_args = array_merge(
                [$this->watermarked_meta_key],
                $mime_types,
                [$excluded_pattern],
                array_map('intval', $apply_categories)
            );

            return intval($wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(DISTINCT p.ID) FROM {$wpdb->posts} p
                 INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = %s
                 INNER JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
                 INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
                 WHERE p.post_type = 'attachment'
                 AND p.post_mime_type IN ($placeholders)
                 AND p.guid NOT LIKE %s
                 AND tt.taxonomy = 'media_category'
                 AND tt.term_id IN ($category_placeholders)",
                ...$query_args
            )));
        }

        // Default
        $args = array_merge(
            [$this->watermarked_meta_key],
            $mime_types,
            [$excluded_pattern]
        );
        return intval($wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->posts} p
             INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = %s
             WHERE p.post_type = 'attachment'
             AND p.post_mime_type IN ($placeholders)
             AND p.guid NOT LIKE %s",
            ...$args
        )));
    }

    /**
     * Get supported MIME types based on settings
     */
    private function get_supported_mime_types() {
        $formats = $this->settings['formats'];
        $mime_map = [
            'jpeg' => 'image/jpeg',
            'png' => 'image/png',
            'gif' => 'image/gif',
            'webp' => 'image/webp',
        ];

        $mime_types = [];
        foreach ($formats as $format) {
            if (isset($mime_map[$format])) {
                $mime_types[] = $mime_map[$format];
            }
        }

        return $mime_types ?: ['image/jpeg', 'image/png'];
    }

    /**
     * Check if file should be excluded
     */
    private function should_exclude($filepath) {
        $filename = basename($filepath);
        return strpos($filename, $this->exclusion_suffix) !== false;
    }

    /**
     * Check if attachment should be watermarked based on category settings
     *
     * @param int $attachment_id The attachment ID to check
     * @return bool True if watermark should be applied, false otherwise
     */
    private function should_watermark_attachment($attachment_id) {
        $apply_mode = isset($this->settings['apply_mode']) ? $this->settings['apply_mode'] : 'all';

        // If mode is 'all', always watermark
        if ($apply_mode === 'all') {
            return true;
        }

        // If mode is 'categories', check if attachment is in selected categories
        if ($apply_mode === 'categories') {
            $apply_categories = isset($this->settings['apply_categories']) ? (array) $this->settings['apply_categories'] : [];

            // If no categories selected, don't watermark
            if (empty($apply_categories)) {
                return false;
            }

            // Check if media_category taxonomy exists
            if (!taxonomy_exists('media_category')) {
                return false;
            }

            // Get attachment's categories
            $terms = wp_get_object_terms($attachment_id, 'media_category', ['fields' => 'ids']);
            if (is_wp_error($terms) || empty($terms)) {
                return false;
            }

            // Check if any of attachment's categories match selected categories
            $matching = array_intersect($terms, $apply_categories);
            return !empty($matching);
        }

        // Default: watermark
        return true;
    }

    /**
     * Security: Check rate limiting for AJAX requests
     * Prevents abuse by limiting requests per user per minute
     */
    private function check_rate_limit() {
        $user_id = get_current_user_id();
        $transient_key = $this->rate_limit_prefix . $user_id;
        $current_count = get_transient($transient_key);

        if ($current_count === false) {
            set_transient($transient_key, 1, self::RATE_LIMIT_WINDOW);
            return true;
        }

        if ($current_count >= $this->rate_limit_max) {
            return false;
        }

        set_transient($transient_key, $current_count + 1, self::RATE_LIMIT_WINDOW);
        return true;
    }

    /**
     * Security: Check if there's enough memory to process an image
     * Estimates memory needed based on image dimensions
     */
    private function check_memory_available($width, $height, $channels = 4) {
        // Estimate memory needed: width * height * channels * 2 (for processing buffer)
        $estimated_memory = $width * $height * $channels * 2;

        // Get available memory
        $memory_limit = $this->get_memory_limit();
        $memory_used = memory_get_usage(true);
        $memory_available = $memory_limit - $memory_used;

        // Leave buffer for safety
        return $estimated_memory < ($memory_available * self::MEMORY_BUFFER_PERCENT);
    }

    /**
     * Get PHP memory limit in bytes
     */
    private function get_memory_limit() {
        $memory_limit = ini_get('memory_limit');
        if ($memory_limit === '-1') {
            return PHP_INT_MAX;
        }

        $unit = strtolower(substr($memory_limit, -1));
        $value = intval($memory_limit);

        switch ($unit) {
            case 'g':
                $value *= 1024;
                // Fall through
            case 'm':
                $value *= 1024;
                // Fall through
            case 'k':
                $value *= 1024;
        }

        return $value;
    }

    /**
     * Security: Validate position value against whitelist
     */
    private function validate_position($position) {
        if (in_array($position, $this->allowed_positions, true)) {
            return $position;
        }
        return 'bottom-right'; // Default safe value
    }

    /**
     * Security: Validate formats array against whitelist
     */
    private function validate_formats($formats) {
        if (!is_array($formats)) {
            return ['jpeg', 'png'];
        }
        $valid_formats = array_intersect($formats, $this->allowed_formats);
        return !empty($valid_formats) ? array_values($valid_formats) : ['jpeg', 'png'];
    }

    /**
     * Security: Validate numeric value within bounds
     */
    private function validate_numeric($value, $min, $max, $default) {
        $value = intval($value);
        if ($value < $min || $value > $max) {
            return $default;
        }
        return $value;
    }

    /**
     * Security: Validate attachment is a valid image
     */
    private function validate_image_attachment($attachment_id) {
        if (!$attachment_id) {
            return false;
        }

        $attachment_id = intval($attachment_id);

        // Check attachment exists and is an image
        $mime_type = get_post_mime_type($attachment_id);
        if (!$mime_type || strpos($mime_type, 'image/') !== 0) {
            return false;
        }

        // Check file exists
        $filepath = get_attached_file($attachment_id);
        if (!$filepath || !file_exists($filepath)) {
            return false;
        }

        // Verify it's actually an image
        $image_info = @getimagesize($filepath);
        if (!$image_info) {
            return false;
        }

        return true;
    }

    /**
     * Security: Validate file path is within allowed directories
     * Prevents path traversal attacks
     */
    private function validate_file_path($filepath, $allowed_base) {
        // Security: Reject if allowed_base is empty or invalid
        if (empty($allowed_base) || !is_string($allowed_base)) {
            return false;
        }

        // Resolve allowed base path
        $real_base = realpath($allowed_base);

        // If allowed base doesn't exist, we can't validate safely
        if ($real_base === false) {
            return false;
        }

        // Resolve file path
        $real_file = realpath($filepath);

        // If realpath returns false, the path doesn't exist yet (for new files)
        if ($real_file === false) {
            // For new files, check the parent directory
            $parent_dir = dirname($filepath);
            $real_parent = realpath($parent_dir);
            if ($real_parent === false) {
                return false;
            }
            // Ensure parent is within allowed base
            return strpos($real_parent, $real_base) === 0;
        }

        // Ensure file is within allowed base
        return strpos($real_file, $real_base) === 0;
    }

    /**
     * Apply watermark to an image
     */
    private function apply_watermark($source_path, $watermark_path, $settings = []) {
        $settings = wp_parse_args($settings, $this->settings);

        // Check for GD or Imagick
        if (!function_exists('imagecreatefrompng') && !extension_loaded('imagick')) {
            return new WP_Error('no_image_library', __('GD or Imagick library is required', 'wpswitchboard'));
        }

        // Get source image info
        $source_info = @getimagesize($source_path);
        if (!$source_info) {
            return new WP_Error('invalid_source', __('Invalid source image', 'wpswitchboard'));
        }

        // Security: Check maximum dimensions to prevent memory exhaustion
        if ($source_info[0] > $this->max_image_dimension || $source_info[1] > $this->max_image_dimension) {
            return new WP_Error('too_large', sprintf(
                __('Image dimensions exceed maximum allowed (%dx%d pixels)', 'wpswitchboard'),
                $this->max_image_dimension,
                $this->max_image_dimension
            ));
        }

        // Security: Check available memory before processing
        if (!$this->check_memory_available($source_info[0], $source_info[1])) {
            return new WP_Error('insufficient_memory', __('Insufficient memory to process this image', 'wpswitchboard'));
        }

        // Check minimum size
        if ($source_info[0] < $settings['min_image_width'] || $source_info[1] < $settings['min_image_height']) {
            return new WP_Error('too_small', __('Image is smaller than minimum size', 'wpswitchboard'));
        }

        // Get watermark image info
        $watermark_info = @getimagesize($watermark_path);
        if (!$watermark_info) {
            return new WP_Error('invalid_watermark', __('Invalid watermark image', 'wpswitchboard'));
        }

        // Use GD library
        return $this->apply_watermark_gd($source_path, $watermark_path, $source_info, $watermark_info, $settings);
    }

    /**
     * Apply watermark using GD library
     */
    private function apply_watermark_gd($source_path, $watermark_path, $source_info, $watermark_info, $settings) {
        // Create source image
        switch ($source_info[2]) {
            case IMAGETYPE_JPEG:
                $source = @imagecreatefromjpeg($source_path);
                break;
            case IMAGETYPE_PNG:
                $source = @imagecreatefrompng($source_path);
                break;
            case IMAGETYPE_GIF:
                $source = @imagecreatefromgif($source_path);
                break;
            case IMAGETYPE_WEBP:
                if (function_exists('imagecreatefromwebp')) {
                    $source = @imagecreatefromwebp($source_path);
                } else {
                    return new WP_Error('webp_not_supported', __('WebP is not supported by your server', 'wpswitchboard'));
                }
                break;
            default:
                return new WP_Error('unsupported_format', __('Unsupported image format', 'wpswitchboard'));
        }

        if (!$source) {
            return new WP_Error('create_failed', __('Failed to create source image', 'wpswitchboard'));
        }

        // Create watermark image (should be PNG for transparency)
        $watermark = @imagecreatefrompng($watermark_path);
        if (!$watermark) {
            // Try other formats
            switch ($watermark_info[2]) {
                case IMAGETYPE_JPEG:
                    $watermark = @imagecreatefromjpeg($watermark_path);
                    break;
                case IMAGETYPE_GIF:
                    $watermark = @imagecreatefromgif($watermark_path);
                    break;
                case IMAGETYPE_WEBP:
                    if (function_exists('imagecreatefromwebp')) {
                        $watermark = @imagecreatefromwebp($watermark_path);
                    }
                    break;
            }
        }

        if (!$watermark) {
            imagedestroy($source);
            return new WP_Error('watermark_failed', __('Failed to create watermark image', 'wpswitchboard'));
        }

        // Calculate watermark size
        $source_width = imagesx($source);
        $source_height = imagesy($source);
        $watermark_width = imagesx($watermark);
        $watermark_height = imagesy($watermark);

        // Validate watermark dimensions
        if ($watermark_width <= 0 || $watermark_height <= 0) {
            imagedestroy($source);
            imagedestroy($watermark);
            return new WP_Error('invalid_watermark', __('Watermark image has invalid dimensions', 'wpswitchboard'));
        }

        // Scale watermark (minimum 1% to prevent invisible watermarks)
        $scale = max(1, intval($settings['scale'])) / 100;
        $new_watermark_width = max(1, intval($source_width * $scale));
        $new_watermark_height = max(1, intval($watermark_height * ($new_watermark_width / $watermark_width)));

        // Resize watermark
        $resized_watermark = imagecreatetruecolor($new_watermark_width, $new_watermark_height);
        imagealphablending($resized_watermark, false);
        imagesavealpha($resized_watermark, true);
        imagecopyresampled($resized_watermark, $watermark, 0, 0, 0, 0, $new_watermark_width, $new_watermark_height, $watermark_width, $watermark_height);

        // Apply grayscale if enabled
        if (!empty($settings['grayscale'])) {
            imagefilter($resized_watermark, IMG_FILTER_GRAYSCALE);
        }

        // Calculate position with separate margins
        $margins = [
            'top' => isset($settings['margin_top']) ? intval($settings['margin_top']) : intval($settings['margin']),
            'right' => isset($settings['margin_right']) ? intval($settings['margin_right']) : intval($settings['margin']),
            'bottom' => isset($settings['margin_bottom']) ? intval($settings['margin_bottom']) : intval($settings['margin']),
            'left' => isset($settings['margin_left']) ? intval($settings['margin_left']) : intval($settings['margin']),
        ];
        $position = $this->calculate_position_with_margins($settings['position'], $source_width, $source_height, $new_watermark_width, $new_watermark_height, $margins);

        // Apply opacity
        $opacity = intval($settings['opacity']);
        if ($opacity < 100) {
            $this->apply_opacity($resized_watermark, $opacity);
        }

        // Enable alpha blending
        imagealphablending($source, true);

        // Merge watermark onto source
        imagecopy($source, $resized_watermark, $position['x'], $position['y'], 0, 0, $new_watermark_width, $new_watermark_height);

        // Save based on original format
        $success = false;
        $quality = isset($settings['image_quality']) ? intval($settings['image_quality']) : self::IMAGE_QUALITY;
        switch ($source_info[2]) {
            case IMAGETYPE_JPEG:
                $success = imagejpeg($source, $source_path, $quality);
                break;
            case IMAGETYPE_PNG:
                imagesavealpha($source, true);
                $success = imagepng($source, $source_path, 9);
                break;
            case IMAGETYPE_GIF:
                $success = imagegif($source, $source_path);
                break;
            case IMAGETYPE_WEBP:
                if (function_exists('imagewebp')) {
                    $success = imagewebp($source, $source_path, $quality);
                }
                break;
        }

        // Cleanup
        imagedestroy($source);
        imagedestroy($watermark);
        imagedestroy($resized_watermark);

        return $success ? true : new WP_Error('save_failed', __('Failed to save watermarked image', 'wpswitchboard'));
    }

    /**
     * Apply opacity to an image
     */
    private function apply_opacity($image, $opacity) {
        $width = imagesx($image);
        $height = imagesy($image);

        for ($x = 0; $x < $width; $x++) {
            for ($y = 0; $y < $height; $y++) {
                $rgba = imagecolorat($image, $x, $y);
                $alpha = ($rgba >> 24) & 0x7F;

                // Calculate new alpha
                $new_alpha = 127 - intval((127 - $alpha) * ($opacity / 100));

                $colors = imagecolorsforindex($image, $rgba);
                $new_color = imagecolorallocatealpha($image, $colors['red'], $colors['green'], $colors['blue'], $new_alpha);
                imagesetpixel($image, $x, $y, $new_color);
            }
        }
    }

    /**
     * Calculate watermark position
     */
    private function calculate_position($position, $source_width, $source_height, $watermark_width, $watermark_height, $margin) {
        $x = 0;
        $y = 0;

        switch ($position) {
            case 'top-left':
                $x = $margin;
                $y = $margin;
                break;
            case 'top-center':
                $x = ($source_width - $watermark_width) / 2;
                $y = $margin;
                break;
            case 'top-right':
                $x = $source_width - $watermark_width - $margin;
                $y = $margin;
                break;
            case 'center-left':
                $x = $margin;
                $y = ($source_height - $watermark_height) / 2;
                break;
            case 'center':
                $x = ($source_width - $watermark_width) / 2;
                $y = ($source_height - $watermark_height) / 2;
                break;
            case 'center-right':
                $x = $source_width - $watermark_width - $margin;
                $y = ($source_height - $watermark_height) / 2;
                break;
            case 'bottom-left':
                $x = $margin;
                $y = $source_height - $watermark_height - $margin;
                break;
            case 'bottom-center':
                $x = ($source_width - $watermark_width) / 2;
                $y = $source_height - $watermark_height - $margin;
                break;
            case 'bottom-right':
            default:
                $x = $source_width - $watermark_width - $margin;
                $y = $source_height - $watermark_height - $margin;
                break;
        }

        return ['x' => max(0, intval($x)), 'y' => max(0, intval($y))];
    }

    /**
     * Calculate watermark position with separate margins (top/right/bottom/left)
     */
    private function calculate_position_with_margins($position, $source_width, $source_height, $watermark_width, $watermark_height, $margins) {
        $x = 0;
        $y = 0;

        switch ($position) {
            case 'top-left':
                $x = $margins['left'];
                $y = $margins['top'];
                break;
            case 'top-center':
                $x = ($source_width - $watermark_width) / 2;
                $y = $margins['top'];
                break;
            case 'top-right':
                $x = $source_width - $watermark_width - $margins['right'];
                $y = $margins['top'];
                break;
            case 'center-left':
                $x = $margins['left'];
                $y = ($source_height - $watermark_height) / 2;
                break;
            case 'center':
                $x = ($source_width - $watermark_width) / 2;
                $y = ($source_height - $watermark_height) / 2;
                break;
            case 'center-right':
                $x = $source_width - $watermark_width - $margins['right'];
                $y = ($source_height - $watermark_height) / 2;
                break;
            case 'bottom-left':
                $x = $margins['left'];
                $y = $source_height - $watermark_height - $margins['bottom'];
                break;
            case 'bottom-center':
                $x = ($source_width - $watermark_width) / 2;
                $y = $source_height - $watermark_height - $margins['bottom'];
                break;
            case 'bottom-right':
            default:
                $x = $source_width - $watermark_width - $margins['right'];
                $y = $source_height - $watermark_height - $margins['bottom'];
                break;
        }

        return ['x' => max(0, intval($x)), 'y' => max(0, intval($y))];
    }

    /**
     * Backup an image before watermarking
     */
    private function backup_image($attachment_id, $filepath) {
        if (!$this->settings['backup_originals']) {
            return true;
        }

        // Security: Validate attachment ID
        $attachment_id = intval($attachment_id);
        if ($attachment_id <= 0) {
            return false;
        }

        // Security: Validate filepath is within uploads directory
        $upload_dir = wp_upload_dir();
        if (!$this->validate_file_path($filepath, $upload_dir['basedir'])) {
            return false;
        }

        // Create backup directory if needed
        if (!is_dir($this->backup_dir)) {
            wp_mkdir_p($this->backup_dir);
            // Security: Add index.php for security
            file_put_contents($this->backup_dir . '/index.php', '<?php // Silence is golden');
            // Security: Add .htaccess to block direct access
            $this->create_htaccess_protection($this->backup_dir);
        }

        // Create subdirectory based on attachment ID
        $subdir = $this->backup_dir . '/' . intval($attachment_id / 1000);
        if (!is_dir($subdir)) {
            wp_mkdir_p($subdir);
        }

        // Security: Sanitize filename to prevent path traversal
        $safe_filename = sanitize_file_name(basename($filepath));

        // Copy file to backup - only if backup doesn't already exist
        // This preserves the true original even if user watermarks multiple times
        $backup_path = $subdir . '/' . $attachment_id . '_' . $safe_filename;
        if (file_exists($backup_path)) {
            // Backup already exists, don't overwrite the original
            return true;
        }
        return copy($filepath, $backup_path);
    }

    /**
     * Security: Create .htaccess file to protect backup directory
     */
    private function create_htaccess_protection($directory) {
        $htaccess_file = $directory . '/.htaccess';
        if (file_exists($htaccess_file)) {
            return;
        }

        $htaccess_content = "# Security: Block direct access to backup files\n";
        $htaccess_content .= "<IfModule mod_authz_core.c>\n";
        $htaccess_content .= "    Require all denied\n";
        $htaccess_content .= "</IfModule>\n";
        $htaccess_content .= "<IfModule !mod_authz_core.c>\n";
        $htaccess_content .= "    Order deny,allow\n";
        $htaccess_content .= "    Deny from all\n";
        $htaccess_content .= "</IfModule>\n";

        file_put_contents($htaccess_file, $htaccess_content);
    }

    /**
     * Get backup path for an attachment
     */
    private function get_backup_path($attachment_id, $filename) {
        // Security: Validate inputs
        $attachment_id = intval($attachment_id);
        if ($attachment_id <= 0) {
            return false;
        }

        // If backup directory doesn't exist, no backups can exist
        if (!is_dir($this->backup_dir)) {
            return false;
        }

        // Security: Sanitize filename to prevent path traversal
        $safe_filename = sanitize_file_name(basename($filename));

        $subdir = $this->backup_dir . '/' . intval($attachment_id / 1000);
        $backup_path = $subdir . '/' . $attachment_id . '_' . $safe_filename;

        // Security: Validate path is within backup directory
        if (!$this->validate_file_path($backup_path, $this->backup_dir)) {
            return false;
        }

        if (file_exists($backup_path)) {
            return $backup_path;
        }

        return false;
    }

    /**
     * Watermark on upload
     */
    public function watermark_on_upload($metadata, $attachment_id) {
        // Security: Validate attachment_id
        $attachment_id = intval($attachment_id);
        if ($attachment_id <= 0) {
            return $metadata;
        }

        // Get attachment info
        $filepath = get_attached_file($attachment_id);
        if (!$filepath) {
            return $metadata;
        }

        $mime_type = get_post_mime_type($attachment_id);

        // Check if supported type
        if (!in_array($mime_type, $this->get_supported_mime_types())) {
            return $metadata;
        }

        // Check for exclusion suffix
        if ($this->should_exclude($filepath)) {
            return $metadata;
        }

        // Check category-based watermarking rules
        if (!$this->should_watermark_attachment($attachment_id)) {
            return $metadata;
        }

        // Check watermark type
        $watermark_type = isset($this->settings['watermark_type']) ? $this->settings['watermark_type'] : 'image';
        $watermark_path = null;

        if ($watermark_type === 'text') {
            // Text watermark - validate text is set
            $watermark_text = isset($this->settings['watermark_text']) ? trim($this->settings['watermark_text']) : '';
            if (empty($watermark_text)) {
                return $metadata;
            }
        } else {
            // Image watermark - validate image exists
            $watermark_id = $this->settings['watermark_image_id'];
            if (!$watermark_id) {
                return $metadata;
            }

            // Security: Validate watermark is a valid image attachment
            if (!$this->validate_image_attachment($watermark_id)) {
                return $metadata;
            }

            $watermark_path = get_attached_file($watermark_id);
            if (!$watermark_path || !file_exists($watermark_path)) {
                return $metadata;
            }

            // Check watermark dimensions aren't too large
            $watermark_info = @getimagesize($watermark_path);
            if (!$watermark_info || $watermark_info[0] > 2000 || $watermark_info[1] > 2000) {
                return $metadata;
            }
        }

        // Backup original
        $this->backup_image($attachment_id, $filepath);

        // Apply watermark to main image based on type
        if ($watermark_type === 'text') {
            $result = $this->apply_text_watermark($filepath, $this->settings);
        } else {
            $result = $this->apply_watermark($filepath, $watermark_path);
        }

        if (!is_wp_error($result)) {
            // Mark as watermarked
            update_post_meta($attachment_id, $this->watermarked_meta_key, time());

            // Also watermark intermediate sizes
            if (!empty($metadata['sizes'])) {
                $base_dir = dirname($filepath);

                foreach ($metadata['sizes'] as $size => $size_info) {
                    // Security: Sanitize the filename from metadata
                    $safe_size_file = sanitize_file_name(basename($size_info['file']));
                    $size_path = $base_dir . '/' . $safe_size_file;
                    if (file_exists($size_path)) {
                        if ($watermark_type === 'text') {
                            $this->apply_text_watermark($size_path, $this->settings);
                        } else {
                            $this->apply_watermark($size_path, $watermark_path);
                        }
                    }
                }
            }
        }

        return $metadata;
    }

    /**
     * AJAX: Get stats
     */
    public function ajax_get_stats() {
        check_ajax_referer('switchboard_watermark_nonce', 'nonce');

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

        // Reload settings fresh from database to get latest category selections
        $this->settings = $this->get_settings();

        wp_send_json_success($this->get_stats());
    }

    /**
     * AJAX: Process batch
     */
    public function ajax_process_batch() {
        check_ajax_referer('switchboard_watermark_nonce', 'nonce');

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

        // Security: Rate limiting (allow more for batch processing)
        // We don't apply strict rate limiting here since batches are user-controlled
        // and the UI already prevents rapid clicking

        // Reload settings fresh from database
        $this->settings = $this->get_settings();

        // Security: Validate and clamp batch_size
        $batch_size = isset($_POST['batch_size']) ? intval($_POST['batch_size']) : self::BATCH_SIZE_DEFAULT;
        $batch_size = max(1, min($batch_size, self::BATCH_SIZE_MAX));

        // Check watermark type
        $watermark_type = isset($this->settings['watermark_type']) ? $this->settings['watermark_type'] : 'image';
        $watermark_path = null;

        if ($watermark_type === 'text') {
            // Text watermark - validate text is set
            $watermark_text = isset($this->settings['watermark_text']) ? trim($this->settings['watermark_text']) : '';
            if (empty($watermark_text)) {
                wp_send_json_error(['message' => __('No watermark text configured', 'wpswitchboard')]);
            }
        } else {
            // Image watermark - validate image exists
            $watermark_id = $this->settings['watermark_image_id'];
            if (!$watermark_id) {
                wp_send_json_error(['message' => __('No watermark image configured', 'wpswitchboard')]);
            }

            // Security: Validate watermark is actually a valid image attachment
            if (!$this->validate_image_attachment($watermark_id)) {
                wp_send_json_error(['message' => __('Invalid watermark image configured', 'wpswitchboard')]);
            }

            $watermark_path = get_attached_file($watermark_id);
            if (!$watermark_path || !file_exists($watermark_path)) {
                wp_send_json_error(['message' => __('Watermark image not found', 'wpswitchboard')]);
            }

            // Verify watermark is a valid image
            $watermark_info = @getimagesize($watermark_path);
            if (!$watermark_info) {
                wp_send_json_error(['message' => __('Watermark file is not a valid image', 'wpswitchboard')]);
            }

            // Check watermark dimensions aren't too large (max 2000px to prevent memory issues)
            if ($watermark_info[0] > 2000 || $watermark_info[1] > 2000) {
                wp_send_json_error(['message' => __('Watermark image is too large. Maximum dimensions: 2000x2000 pixels.', 'wpswitchboard')]);
            }
        }

        // Get pending images
        global $wpdb;
        $mime_types = $this->get_supported_mime_types();
        $placeholders = implode(',', array_fill(0, count($mime_types), '%s'));
        $excluded_pattern = '%' . $this->exclusion_suffix . '%';

        $query_args = array_merge(
            [$this->watermarked_meta_key],
            $mime_types,
            [$excluded_pattern, $batch_size]
        );
        $images = $wpdb->get_results($wpdb->prepare(
            "SELECT p.ID, p.guid FROM {$wpdb->posts} p
             LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = %s
             WHERE p.post_type = 'attachment'
             AND p.post_mime_type IN ($placeholders)
             AND pm.meta_id IS NULL
             AND p.guid NOT LIKE %s
             LIMIT %d",
            ...$query_args
        ));

        $results = [];
        $processed = 0;

        foreach ($images as $image) {
            $filepath = get_attached_file($image->ID);
            $filename = basename($filepath);

            // Check exclusion again
            if ($this->should_exclude($filepath)) {
                $results[] = [
                    'filename' => $filename,
                    'skipped' => true,
                    'message' => __('Excluded by filename', 'wpswitchboard'),
                ];
                continue;
            }

            // Check category-based watermarking rules
            if (!$this->should_watermark_attachment($image->ID)) {
                // Mark as skipped so we don't retry (but not as an error)
                update_post_meta($image->ID, $this->watermarked_meta_key, 'skipped:category');
                $results[] = [
                    'filename' => $filename,
                    'skipped' => true,
                    'message' => __('Not in selected categories', 'wpswitchboard'),
                ];
                continue;
            }

            // Check minimum size requirements before processing
            $image_info = @getimagesize($filepath);
            if ($image_info) {
                $min_width = isset($this->settings['min_image_width']) ? intval($this->settings['min_image_width']) : 0;
                $min_height = isset($this->settings['min_image_height']) ? intval($this->settings['min_image_height']) : 0;

                if (($min_width > 0 && $image_info[0] < $min_width) || ($min_height > 0 && $image_info[1] < $min_height)) {
                    update_post_meta($image->ID, $this->watermarked_meta_key, 'skipped:size');
                    $results[] = [
                        'filename' => $filename,
                        'skipped' => true,
                        'message' => sprintf(__('Image too small (%dx%d, minimum: %dx%d)', 'wpswitchboard'), $image_info[0], $image_info[1], $min_width, $min_height),
                    ];
                    continue;
                }
            }

            // Backup
            $this->backup_image($image->ID, $filepath);

            // Apply watermark based on type
            if ($watermark_type === 'text') {
                $result = $this->apply_text_watermark($filepath, $this->settings);
            } else {
                $result = $this->apply_watermark($filepath, $watermark_path);
            }

            if (is_wp_error($result)) {
                // Mark as attempted (with error) so we don't retry forever
                update_post_meta($image->ID, $this->watermarked_meta_key, 'error:' . $result->get_error_code());
                $results[] = [
                    'filename' => $filename,
                    'success' => false,
                    'message' => $result->get_error_message(),
                ];
            } else {
                // Mark as watermarked
                update_post_meta($image->ID, $this->watermarked_meta_key, time());

                // Also watermark intermediate sizes
                $metadata = wp_get_attachment_metadata($image->ID);
                if (!empty($metadata['sizes'])) {
                    $base_dir = dirname($filepath);
                    foreach ($metadata['sizes'] as $size => $size_info) {
                        // Security: Sanitize filename from metadata
                        $safe_size_file = sanitize_file_name(basename($size_info['file']));
                        $size_path = $base_dir . '/' . $safe_size_file;
                        if (file_exists($size_path)) {
                            if ($watermark_type === 'text') {
                                $this->apply_text_watermark($size_path, $this->settings);
                            } else {
                                $this->apply_watermark($size_path, $watermark_path);
                            }
                        }
                    }
                }

                $results[] = [
                    'filename' => $filename,
                    'success' => true,
                ];
                $processed++;
            }
        }

        // Get remaining count
        $remaining_args = array_merge(
            [$this->watermarked_meta_key],
            $mime_types,
            [$excluded_pattern]
        );
        $remaining = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->posts} p
             LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = %s
             WHERE p.post_type = 'attachment'
             AND p.post_mime_type IN ($placeholders)
             AND pm.meta_id IS NULL
             AND p.guid NOT LIKE %s",
            ...$remaining_args
        ));

        wp_send_json_success([
            'processed' => $processed,
            'remaining' => intval($remaining),
            'results' => $results,
        ]);
    }

    /**
     * AJAX: Restore batch
     */
    public function ajax_restore_batch() {
        check_ajax_referer('switchboard_watermark_nonce', 'nonce');

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

        // Security: Validate and clamp batch_size
        $batch_size = isset($_POST['batch_size']) ? intval($_POST['batch_size']) : self::RESTORE_BATCH_SIZE_DEFAULT;
        $batch_size = max(1, min($batch_size, self::RESTORE_BATCH_SIZE_MAX));

        // Get watermarked attachments with backups
        global $wpdb;

        $attachments = $wpdb->get_results($wpdb->prepare(
            "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = %s LIMIT %d",
            $this->watermarked_meta_key,
            $batch_size
        ));

        $restored = 0;
        $total = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->postmeta} WHERE meta_key = %s",
            $this->watermarked_meta_key
        ));

        foreach ($attachments as $attachment) {
            $post_id = intval($attachment->post_id);
            if ($post_id <= 0) {
                continue;
            }

            $filepath = get_attached_file($post_id);
            if (!$filepath) {
                delete_post_meta($post_id, $this->watermarked_meta_key);
                continue;
            }

            $filename = basename($filepath);
            $backup_path = $this->get_backup_path($post_id, $filename);

            if ($backup_path && file_exists($backup_path)) {
                // Security: Validate destination is within uploads directory
                $upload_dir = wp_upload_dir();
                if (!$this->validate_file_path($filepath, $upload_dir['basedir'])) {
                    delete_post_meta($post_id, $this->watermarked_meta_key);
                    continue;
                }

                // Restore main image
                if (copy($backup_path, $filepath)) {
                    // Also restore intermediate sizes
                    $metadata = wp_get_attachment_metadata($post_id);
                    if (!empty($metadata['sizes'])) {
                        $base_dir = dirname($filepath);
                        foreach ($metadata['sizes'] as $size => $size_info) {
                            // Security: Sanitize filename from metadata
                            $safe_size_file = sanitize_file_name(basename($size_info['file']));
                            $size_backup = $this->get_backup_path($post_id, $safe_size_file);
                            if ($size_backup && file_exists($size_backup)) {
                                $dest_path = $base_dir . '/' . $safe_size_file;
                                // Security: Validate destination path
                                if ($this->validate_file_path($dest_path, $upload_dir['basedir'])) {
                                    copy($size_backup, $dest_path);
                                }
                            }
                        }
                    }
                    $restored++;
                }
            }

            // Remove watermark meta
            delete_post_meta($post_id, $this->watermarked_meta_key);
        }

        $remaining = $total - $restored;

        wp_send_json_success([
            'restored' => $restored,
            'remaining' => max(0, $remaining),
            'total' => intval($total),
        ]);
    }

    /**
     * AJAX: Delete backups
     */
    public function ajax_delete_backups() {
        check_ajax_referer('switchboard_watermark_nonce', 'nonce');

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

        // Security: Only delete the specific backup directory, verify it's in wp-content
        if (is_dir($this->backup_dir)) {
            // Validate the backup directory is within wp-content
            $real_backup = realpath($this->backup_dir);
            $real_content = realpath(WP_CONTENT_DIR);

            if ($real_backup === false || $real_content === false) {
                wp_send_json_error(['message' => __('Invalid backup directory', 'wpswitchboard')]);
            }

            if (strpos($real_backup, $real_content) !== 0) {
                wp_send_json_error(['message' => __('Security error: Invalid backup path', 'wpswitchboard')]);
            }

            // Additional check: ensure it's specifically our backup directory
            if (basename($real_backup) !== 'switchboard-watermark-backups') {
                wp_send_json_error(['message' => __('Security error: Invalid backup directory name', 'wpswitchboard')]);
            }

            $this->recursive_delete($this->backup_dir, $real_content);
        }

        wp_send_json_success(['message' => __('Backups deleted', 'wpswitchboard')]);
    }

    /**
     * AJAX: Reset watermark status for eligible images
     * Allows re-watermarking without restoring backups
     */
    public function ajax_reset_status() {
        check_ajax_referer('switchboard_watermark_nonce', 'nonce');

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

        // Reload settings fresh
        $this->settings = $this->get_settings();

        global $wpdb;

        $apply_mode = isset($this->settings['apply_mode']) ? $this->settings['apply_mode'] : 'all';
        $mime_types = $this->get_supported_mime_types();
        $placeholders = implode(',', array_fill(0, count($mime_types), '%s'));
        $excluded_pattern = '%' . $this->exclusion_suffix . '%';

        $reset_count = 0;

        if ($apply_mode === 'all') {
            // Reset all watermarked images
            $args = array_merge(
                [$this->watermarked_meta_key],
                $mime_types,
                [$excluded_pattern]
            );
            $attachment_ids = $wpdb->get_col($wpdb->prepare(
                "SELECT p.ID FROM {$wpdb->posts} p
                 INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = %s
                 WHERE p.post_type = 'attachment'
                 AND p.post_mime_type IN ($placeholders)
                 AND p.guid NOT LIKE %s",
                ...$args
            ));
        } elseif ($apply_mode === 'categories') {
            $apply_categories = isset($this->settings['apply_categories']) ? (array) $this->settings['apply_categories'] : [];

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

            if (!taxonomy_exists('media_category')) {
                wp_send_json_error(['message' => __('Media category taxonomy not found', 'wpswitchboard')]);
            }

            $category_placeholders = implode(',', array_fill(0, count($apply_categories), '%d'));
            $query_args = array_merge(
                [$this->watermarked_meta_key],
                $mime_types,
                [$excluded_pattern],
                array_map('intval', $apply_categories)
            );

            $attachment_ids = $wpdb->get_col($wpdb->prepare(
                "SELECT DISTINCT p.ID FROM {$wpdb->posts} p
                 INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = %s
                 INNER JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
                 INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
                 WHERE p.post_type = 'attachment'
                 AND p.post_mime_type IN ($placeholders)
                 AND p.guid NOT LIKE %s
                 AND tt.taxonomy = 'media_category'
                 AND tt.term_id IN ($category_placeholders)",
                ...$query_args
            ));
        } else {
            $attachment_ids = [];
        }

        // Delete the watermark meta for these attachments
        foreach ($attachment_ids as $attachment_id) {
            delete_post_meta(intval($attachment_id), $this->watermarked_meta_key);
            $reset_count++;
        }

        wp_send_json_success([
            'message' => sprintf(__('%d images reset for re-watermarking', 'wpswitchboard'), $reset_count),
            'reset_count' => $reset_count,
        ]);
    }

    /**
     * Recursively delete directory (with security checks)
     */
    private function recursive_delete($dir, $allowed_base = null) {
        // Security: If no allowed base provided, use WP_CONTENT_DIR
        if ($allowed_base === null) {
            $allowed_base = realpath(WP_CONTENT_DIR);
        }

        // Security: Verify directory is within allowed base before deleting
        $real_dir = realpath($dir);
        if ($real_dir === false) {
            return; // Directory doesn't exist
        }

        if (strpos($real_dir, $allowed_base) !== 0) {
            return; // Not within allowed base, refuse to delete
        }

        if (is_dir($dir)) {
            $objects = scandir($dir);
            foreach ($objects as $object) {
                if ($object !== '.' && $object !== '..') {
                    $path = $dir . '/' . $object;
                    if (is_dir($path)) {
                        $this->recursive_delete($path, $allowed_base);
                    } else {
                        // Security: Verify file is within allowed base before deleting
                        $real_path = realpath($path);
                        if ($real_path !== false && strpos($real_path, $allowed_base) === 0) {
                            unlink($path);
                        }
                    }
                }
            }
            rmdir($dir);
        }
    }

    /**
     * AJAX: Save settings
     */
    public function ajax_save_settings() {
        check_ajax_referer('switchboard_watermark_nonce', 'nonce');

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

        // Security: Rate limiting
        if (!$this->check_rate_limit()) {
            wp_send_json_error(['message' => __('Too many requests. Please wait a moment.', 'wpswitchboard')]);
        }

        // Security: Validate watermark image ID is actually a valid image attachment
        $watermark_id = isset($_POST['watermark_image_id']) ? intval($_POST['watermark_image_id']) : 0;
        if ($watermark_id > 0 && !$this->validate_image_attachment($watermark_id)) {
            wp_send_json_error(['message' => __('Invalid watermark image selected', 'wpswitchboard')]);
        }

        // Validate watermark type
        $watermark_type = isset($_POST['watermark_type']) ? sanitize_text_field($_POST['watermark_type']) : 'image';
        if (!in_array($watermark_type, ['image', 'text'])) {
            $watermark_type = 'image';
        }

        // Validate apply mode
        $apply_mode = isset($_POST['apply_mode']) ? sanitize_text_field($_POST['apply_mode']) : 'all';
        if (!in_array($apply_mode, ['all', 'categories', 'single'])) {
            $apply_mode = 'all';
        }

        // Validate apply categories
        $apply_categories = [];
        if (isset($_POST['apply_categories']) && is_array($_POST['apply_categories'])) {
            $apply_categories = array_map('intval', $_POST['apply_categories']);
            $apply_categories = array_filter($apply_categories, function($id) { return $id > 0; });
        }

        // Security: Validate and sanitize all inputs with bounds checking
        $settings = [
            // Watermark type
            'watermark_type' => $watermark_type,
            'watermark_image_id' => $watermark_id,
            // Text watermark settings
            'watermark_text' => isset($_POST['watermark_text']) ? sanitize_text_field($_POST['watermark_text']) : '',
            'text_font_size' => $this->validate_numeric(isset($_POST['text_font_size']) ? $_POST['text_font_size'] : 24, 12, 200, 24),
            'text_color' => isset($_POST['text_color']) ? sanitize_hex_color($_POST['text_color']) : '#ffffff',
            'text_shadow' => isset($_POST['text_shadow']) && $_POST['text_shadow'] == 1,
            // Position & layout
            'position' => $this->validate_position(isset($_POST['position']) ? sanitize_text_field($_POST['position']) : 'bottom-right'),
            'opacity' => $this->validate_numeric(isset($_POST['opacity']) ? $_POST['opacity'] : self::OPACITY_DEFAULT, self::OPACITY_MIN, self::OPACITY_MAX, self::OPACITY_DEFAULT),
            'scale' => $this->validate_numeric(isset($_POST['scale']) ? $_POST['scale'] : self::SCALE_DEFAULT, self::SCALE_MIN, self::SCALE_MAX, self::SCALE_DEFAULT),
            // Separate margin controls
            'margin_top' => $this->validate_numeric(isset($_POST['margin_top']) ? $_POST['margin_top'] : self::MARGIN_DEFAULT, self::MARGIN_MIN, 100, self::MARGIN_DEFAULT),
            'margin_right' => $this->validate_numeric(isset($_POST['margin_right']) ? $_POST['margin_right'] : self::MARGIN_DEFAULT, self::MARGIN_MIN, 100, self::MARGIN_DEFAULT),
            'margin_bottom' => $this->validate_numeric(isset($_POST['margin_bottom']) ? $_POST['margin_bottom'] : self::MARGIN_DEFAULT, self::MARGIN_MIN, 100, self::MARGIN_DEFAULT),
            'margin_left' => $this->validate_numeric(isset($_POST['margin_left']) ? $_POST['margin_left'] : self::MARGIN_DEFAULT, self::MARGIN_MIN, 100, self::MARGIN_DEFAULT),
            'margin' => $this->validate_numeric(isset($_POST['margin']) ? $_POST['margin'] : self::MARGIN_DEFAULT, self::MARGIN_MIN, self::MARGIN_MAX, self::MARGIN_DEFAULT),
            // Image filters
            'grayscale' => isset($_POST['grayscale']) && $_POST['grayscale'] == 1,
            // Category-based watermarking
            'apply_mode' => $apply_mode,
            'apply_categories' => $apply_categories,
            // General settings
            'min_image_width' => $this->validate_numeric(isset($_POST['min_image_width']) ? $_POST['min_image_width'] : self::MIN_IMAGE_SIZE_DEFAULT, 0, self::MIN_IMAGE_SIZE_MAX, self::MIN_IMAGE_SIZE_DEFAULT),
            'min_image_height' => $this->validate_numeric(isset($_POST['min_image_height']) ? $_POST['min_image_height'] : self::MIN_IMAGE_SIZE_DEFAULT, 0, self::MIN_IMAGE_SIZE_MAX, self::MIN_IMAGE_SIZE_DEFAULT),
            'image_quality' => $this->validate_numeric(isset($_POST['image_quality']) ? $_POST['image_quality'] : self::IMAGE_QUALITY, 50, 100, self::IMAGE_QUALITY),
            'auto_watermark' => isset($_POST['auto_watermark']) && $_POST['auto_watermark'] == 1,
            'backup_originals' => isset($_POST['backup_originals']) && $_POST['backup_originals'] == 1,
            'formats' => $this->validate_formats(isset($_POST['formats']) && is_array($_POST['formats']) ? array_map('sanitize_text_field', $_POST['formats']) : $this->allowed_formats),
            // Preview settings
            'preview_category' => isset($_POST['preview_category']) ? intval($_POST['preview_category']) : 0,
        ];

        update_option($this->option_name, $settings);
        $this->settings = $settings;

        wp_send_json_success(['message' => __('Settings saved', 'wpswitchboard')]);
    }

    /**
     * AJAX: Generate preview
     */
    public function ajax_preview() {
        check_ajax_referer('switchboard_watermark_nonce', 'nonce');

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

        // Security: Rate limiting
        if (!$this->check_rate_limit()) {
            wp_send_json_error(['message' => __('Too many requests. Please wait a moment.', 'wpswitchboard')]);
        }

        // Check GD library
        if (!function_exists('imagecreatetruecolor')) {
            wp_send_json_error(['message' => __('GD library is not available on this server', 'wpswitchboard')]);
        }

        // Get watermark type
        $watermark_type = isset($_POST['watermark_type']) ? sanitize_text_field($_POST['watermark_type']) : 'image';

        // Validate based on type
        $watermark_path = null;
        $watermark_id = 0;

        if ($watermark_type === 'image') {
            $watermark_id = isset($_POST['watermark_image_id']) ? intval($_POST['watermark_image_id']) : 0;
            if (!$watermark_id) {
                wp_send_json_error(['message' => __('No watermark image selected', 'wpswitchboard')]);
            }

            if (!$this->validate_image_attachment($watermark_id)) {
                wp_send_json_error(['message' => __('Invalid watermark image', 'wpswitchboard')]);
            }

            $watermark_path = get_attached_file($watermark_id);
            if (!$watermark_path || !file_exists($watermark_path)) {
                wp_send_json_error(['message' => __('Watermark file does not exist', 'wpswitchboard')]);
            }
        } else {
            // Text watermark
            $watermark_text = isset($_POST['watermark_text']) ? sanitize_text_field($_POST['watermark_text']) : '';
            if (empty($watermark_text)) {
                wp_send_json_error(['message' => __('No watermark text entered', 'wpswitchboard')]);
            }
        }

        // Setup preview directory
        $upload_dir = wp_upload_dir();
        $preview_dir = $upload_dir['basedir'] . '/switchboard-temp';
        if (!is_dir($preview_dir)) {
            wp_mkdir_p($preview_dir);
        }

        // Get preview source image
        $preview_category = isset($_POST['preview_category']) ? intval($_POST['preview_category']) : 0;
        $source_file = null;

        // Try to get an image from selected category
        if ($preview_category > 0) {
            $args = [
                'post_type' => 'attachment',
                'post_mime_type' => ['image/jpeg', 'image/png', 'image/webp'],
                'posts_per_page' => 1,
                'orderby' => 'rand',
                'tax_query' => [
                    [
                        'taxonomy' => 'media_category',
                        'field' => 'term_id',
                        'terms' => $preview_category,
                    ]
                ]
            ];
            $images = get_posts($args);
            if (!empty($images)) {
                $source_file = get_attached_file($images[0]->ID);
            }
        }

        // If no category image, use default sample
        $preview_file = $preview_dir . '/watermark-preview.jpg';

        if ($source_file && file_exists($source_file)) {
            // Copy the category image to preview
            copy($source_file, $preview_file);
        } else {
            // Create default sample image
            $this->create_sample_preview_image($preview_file);
        }

        // Build settings for preview
        $settings = [
            'watermark_type' => $watermark_type,
            'position' => $this->validate_position(isset($_POST['position']) ? sanitize_text_field($_POST['position']) : 'bottom-right'),
            'opacity' => $this->validate_numeric(isset($_POST['opacity']) ? $_POST['opacity'] : self::OPACITY_DEFAULT, self::OPACITY_MIN, self::OPACITY_MAX, self::OPACITY_DEFAULT),
            'scale' => $this->validate_numeric(isset($_POST['scale']) ? $_POST['scale'] : self::SCALE_DEFAULT, self::SCALE_MIN, self::SCALE_MAX, self::SCALE_DEFAULT),
            // Separate margins
            'margin_top' => $this->validate_numeric(isset($_POST['margin_top']) ? $_POST['margin_top'] : self::MARGIN_DEFAULT, 0, 100, self::MARGIN_DEFAULT),
            'margin_right' => $this->validate_numeric(isset($_POST['margin_right']) ? $_POST['margin_right'] : self::MARGIN_DEFAULT, 0, 100, self::MARGIN_DEFAULT),
            'margin_bottom' => $this->validate_numeric(isset($_POST['margin_bottom']) ? $_POST['margin_bottom'] : self::MARGIN_DEFAULT, 0, 100, self::MARGIN_DEFAULT),
            'margin_left' => $this->validate_numeric(isset($_POST['margin_left']) ? $_POST['margin_left'] : self::MARGIN_DEFAULT, 0, 100, self::MARGIN_DEFAULT),
            'margin' => self::MARGIN_DEFAULT,
            'grayscale' => isset($_POST['grayscale']) && $_POST['grayscale'] == 1,
            'min_image_width' => 0,
            'min_image_height' => 0,
        ];

        // Add text-specific settings
        if ($watermark_type === 'text') {
            $settings['watermark_text'] = isset($_POST['watermark_text']) ? sanitize_text_field($_POST['watermark_text']) : '';
            $settings['text_font_size'] = $this->validate_numeric(isset($_POST['text_font_size']) ? $_POST['text_font_size'] : 24, 12, 200, 24);
            $settings['text_color'] = isset($_POST['text_color']) ? sanitize_hex_color($_POST['text_color']) : '#ffffff';
            $settings['text_shadow'] = isset($_POST['text_shadow']) && $_POST['text_shadow'] == 1;
        }

        // Apply watermark
        if ($watermark_type === 'image') {
            $result = $this->apply_watermark($preview_file, $watermark_path, $settings);
        } else {
            $result = $this->apply_text_watermark($preview_file, $settings);
        }

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

        $preview_url = $upload_dir['baseurl'] . '/switchboard-temp/watermark-preview.jpg';
        wp_send_json_success(['preview_url' => $preview_url]);
    }

    /**
     * Create sample preview image
     */
    private function create_sample_preview_image($filepath) {
        $preview_width = 800;
        $preview_height = 533;

        $preview = @imagecreatetruecolor($preview_width, $preview_height);
        if (!$preview) return false;

        // Sky gradient
        for ($y = 0; $y < $preview_height * 0.6; $y++) {
            $ratio = $y / ($preview_height * 0.6);
            $r = intval(135 + (200 - 135) * $ratio);
            $g = intval(206 + (220 - 206) * $ratio);
            $b = intval(235 + (240 - 235) * $ratio);
            $color = imagecolorallocate($preview, $r, $g, $b);
            imageline($preview, 0, $y, $preview_width, $y, $color);
        }

        // Ground gradient
        for ($y = intval($preview_height * 0.6); $y < $preview_height; $y++) {
            $ratio = ($y - $preview_height * 0.6) / ($preview_height * 0.4);
            $r = intval(100 - 30 * $ratio);
            $g = intval(160 - 40 * $ratio);
            $b = intval(80 - 20 * $ratio);
            $color = imagecolorallocate($preview, $r, $g, $b);
            imageline($preview, 0, $y, $preview_width, $y, $color);
        }

        // Hills
        $hill_color = imagecolorallocate($preview, 80, 140, 70);
        imagefilledellipse($preview, 200, intval($preview_height * 0.65), 400, 150, $hill_color);
        $hill_color2 = imagecolorallocate($preview, 70, 130, 60);
        imagefilledellipse($preview, 600, intval($preview_height * 0.7), 500, 180, $hill_color2);

        // Sun
        $sun_color = imagecolorallocate($preview, 255, 220, 100);
        imagefilledellipse($preview, 650, 80, 80, 80, $sun_color);

        // Clouds
        $cloud_color = imagecolorallocatealpha($preview, 255, 255, 255, 30);
        imagefilledellipse($preview, 150, 60, 120, 50, $cloud_color);
        imagefilledellipse($preview, 200, 55, 100, 40, $cloud_color);
        imagefilledellipse($preview, 400, 100, 150, 60, $cloud_color);

        imagejpeg($preview, $filepath, self::IMAGE_QUALITY);
        imagedestroy($preview);
        return true;
    }

    /**
     * Apply text watermark to image
     */
    private function apply_text_watermark($source_path, $settings) {
        $source_info = @getimagesize($source_path);
        if (!$source_info) {
            return new WP_Error('invalid_image', __('Invalid source image', 'wpswitchboard'));
        }

        // Create source image
        $image_type = $source_info[2];
        switch ($image_type) {
            case IMAGETYPE_JPEG:
                $source = @imagecreatefromjpeg($source_path);
                break;
            case IMAGETYPE_PNG:
                $source = @imagecreatefrompng($source_path);
                break;
            case IMAGETYPE_WEBP:
                $source = function_exists('imagecreatefromwebp') ? @imagecreatefromwebp($source_path) : null;
                break;
            default:
                return new WP_Error('unsupported', __('Unsupported image format', 'wpswitchboard'));
        }

        if (!$source) {
            return new WP_Error('create_failed', __('Failed to create source image', 'wpswitchboard'));
        }

        // Preserve alpha for PNG
        if ($image_type === IMAGETYPE_PNG) {
            imagealphablending($source, true);
            imagesavealpha($source, true);
        }

        $source_width = imagesx($source);
        $source_height = imagesy($source);

        // Parse text color
        $hex = ltrim($settings['text_color'], '#');
        $r = hexdec(substr($hex, 0, 2));
        $g = hexdec(substr($hex, 2, 2));
        $b = hexdec(substr($hex, 4, 2));

        // Calculate opacity (0-127 scale where 0 is opaque)
        $alpha = intval(127 - (127 * $settings['opacity'] / 100));
        $text_color = imagecolorallocatealpha($source, $r, $g, $b, $alpha);

        // Shadow color
        $shadow_alpha = intval(127 - (60 * $settings['opacity'] / 100));
        $shadow_color = imagecolorallocatealpha($source, 0, 0, 0, $shadow_alpha);

        $text = $settings['watermark_text'];

        // Calculate font size based on scale percentage of image width
        $base_font_size = intval($settings['text_font_size']);
        $scale_percent = intval($settings['scale']) / 100;

        // Scale font size relative to image width - the scale setting controls how big the text is relative to image
        $scaled_font_size = max(12, intval($base_font_size * $scale_percent * ($source_width / 400)));

        // Try to find a TTF font to use
        $font_path = $this->get_ttf_font_path();

        // Calculate position margins
        $margins = [
            'top' => intval($settings['margin_top']),
            'right' => intval($settings['margin_right']),
            'bottom' => intval($settings['margin_bottom']),
            'left' => intval($settings['margin_left']),
        ];

        if ($font_path && function_exists('imagettftext')) {
            // Use TTF font for proper sizing
            $bbox = imagettfbbox($scaled_font_size, 0, $font_path, $text);
            $text_width = abs($bbox[4] - $bbox[0]);
            $text_height = abs($bbox[5] - $bbox[1]);

            $pos = $this->calculate_position_with_margins(
                $settings['position'],
                $source_width,
                $source_height,
                $text_width,
                $text_height,
                $margins
            );

            // TTF text baseline is at bottom, adjust y position
            $text_y = $pos['y'] + $text_height;

            // Draw shadow first if enabled
            if (!empty($settings['text_shadow'])) {
                $shadow_offset = max(2, intval($scaled_font_size / 15));
                imagettftext($source, $scaled_font_size, 0, $pos['x'] + $shadow_offset, $text_y + $shadow_offset, $shadow_color, $font_path, $text);
            }

            // Draw text
            imagettftext($source, $scaled_font_size, 0, $pos['x'], $text_y, $text_color, $font_path, $text);
        } else {
            // Fallback to built-in font (limited sizes 1-5)
            $builtin_font = min(5, max(1, intval($base_font_size / 15)));
            $text_width = imagefontwidth($builtin_font) * strlen($text);
            $text_height = imagefontheight($builtin_font);

            $pos = $this->calculate_position_with_margins(
                $settings['position'],
                $source_width,
                $source_height,
                $text_width,
                $text_height,
                $margins
            );

            // Draw shadow first if enabled
            if (!empty($settings['text_shadow'])) {
                imagestring($source, $builtin_font, $pos['x'] + 2, $pos['y'] + 2, $text, $shadow_color);
            }

            // Draw text
            imagestring($source, $builtin_font, $pos['x'], $pos['y'], $text, $text_color);
        }

        // Save based on original format
        $save_result = false;
        $quality = isset($settings['image_quality']) ? intval($settings['image_quality']) : self::IMAGE_QUALITY;
        switch ($image_type) {
            case IMAGETYPE_PNG:
                $save_result = imagepng($source, $source_path, 9);
                break;
            case IMAGETYPE_WEBP:
                $save_result = function_exists('imagewebp') ? imagewebp($source, $source_path, $quality) : false;
                break;
            case IMAGETYPE_JPEG:
            default:
                $save_result = imagejpeg($source, $source_path, $quality);
                break;
        }

        imagedestroy($source);

        return $save_result ? true : new WP_Error('save_failed', __('Failed to save watermarked image', 'wpswitchboard'));
    }

    /**
     * Get path to a TTF font for text watermarking
     * Uses commonly available system fonts - no external font required
     */
    private function get_ttf_font_path() {
        // Common system fonts available on virtually all servers
        $font_paths = [
            // Linux (most common on web servers)
            '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf',
            '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf',
            '/usr/share/fonts/dejavu/DejaVuSans.ttf',
            '/usr/share/fonts/TTF/DejaVuSans.ttf',
            '/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf',
            '/usr/share/fonts/liberation-sans/LiberationSans-Regular.ttf',
            '/usr/share/fonts/truetype/freefont/FreeSans.ttf',
            '/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf',
            '/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf',
            '/usr/share/fonts/noto/NotoSans-Regular.ttf',
            '/usr/share/fonts/google-noto/NotoSans-Regular.ttf',
            // CentOS/RHEL paths
            '/usr/share/fonts/dejavu-sans-fonts/DejaVuSans.ttf',
            '/usr/share/fonts/liberation-sans/LiberationSans-Regular.ttf',
            // Alpine Linux
            '/usr/share/fonts/ttf-dejavu/DejaVuSans.ttf',
            // macOS
            '/System/Library/Fonts/Supplemental/Arial.ttf',
            '/System/Library/Fonts/Helvetica.ttc',
            '/Library/Fonts/Arial.ttf',
            '/System/Library/Fonts/SFNSText.ttf',
            '/System/Library/Fonts/SFNS.ttf',
            // Windows
            'C:/Windows/Fonts/arial.ttf',
            'C:/Windows/Fonts/Arial.ttf',
            'C:/Windows/Fonts/verdana.ttf',
            'C:/Windows/Fonts/tahoma.ttf',
            'C:/Windows/Fonts/segoeui.ttf',
        ];

        // Check plugin directory for custom bundled font (optional override)
        $plugin_font = dirname(__FILE__) . '/fonts/OpenSans-Regular.ttf';
        if (file_exists($plugin_font)) {
            return $plugin_font;
        }

        // Check system fonts
        foreach ($font_paths as $path) {
            if (file_exists($path)) {
                return $path;
            }
        }

        return false;
    }

    /**
     * Save settings (from settings panel redirect)
     */
    public function save_settings($module, $settings) {
        if ($module !== 'image-watermark') {
            return;
        }
        // Settings are saved via AJAX, this is for framework compatibility
    }

    /**
     * Render settings (redirects to module page)
     */
    public function render_settings($settings = []) {
        $module_url = admin_url('admin.php?page=switchboard-image-watermark');
        echo '
        <input type="hidden" name="module" value="image-watermark">
        <div class="sb-sp-alert sb-sp-alert-info" style="margin-bottom: 20px;">
            <div class="sb-sp-alert-title">Image Watermark</div>
            <p class="sb-sp-text-sm" style="margin: 8px 0;">This module has a dedicated configuration page with preview, bulk processing, and backup management.</p>
            <a href="' . esc_url($module_url) . '" class="sb-sp-button" style="display: inline-block; margin-top: 8px; padding: 8px 16px; background: #3b82f6; color: white; text-decoration: none; border-radius: 6px; font-weight: 500;">
                Open Watermark Manager →
            </a>
        </div>
        ';
    }

    /**
     * Register data locations for cleanup
     */
    public function register_data_locations() {
        return [
            'options' => [
                'switchboard_image-watermark_settings',
            ],
            'option_patterns' => [],
            'tables' => [],
            'post_meta' => [
                '_switchboard_watermarked',
            ],
            'user_meta' => [],
            'directories' => [
                WP_CONTENT_DIR . '/switchboard-watermark-backups',
                wp_upload_dir()['basedir'] . '/switchboard-temp',
            ],
        ];
    }
}

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