<?php
/**
 * Name: Simple Backups
 * Description: Download individual WordPress components as ZIP files for backup - plugins, themes, uploads, and database. Simple, reliable backups without complex configurations.
 * Category: Utilities
 * Icon: -
 * Added: 2025-12-25
 * Tags: backup, download, zip, database, export, plugins, themes, uploads
 * WhereToFind: {
 *   "location": "Switchboard Settings",
 *   "url": "admin.php?page=switchboard",
 *   "instructions": "Find this module in the Utilities category and click to access backup options",
 *   "newTab": false
 * }
 * UseCases: [
 *   {
 *     "title": "Quick Site Backups",
 *     "description": "Download individual components (plugins, themes, uploads, database) as ZIP files for safekeeping before updates or changes."
 *   },
 *   {
 *     "title": "Site Migration",
 *     "description": "Export your database and content folders to easily move to a new server or staging environment."
 *   },
 *   {
 *     "title": "Development Snapshots",
 *     "description": "Create quick snapshots of your current setup to restore if development changes go wrong."
 *   }
 * ]
 */

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

class SwitchboardSimpleBackups {

    private $option_key = 'switchboard_simple-backups_settings';

    /**
     * Sanitize and validate a table name for safe use in SQL queries
     * Only allows alphanumeric characters, underscores, and the WordPress table prefix
     *
     * @param string $table_name The table name to sanitize
     * @return string|false Sanitized table name or false if invalid
     */
    private function sanitize_table_name($table_name) {
        global $wpdb;

        // Table name must be a string
        if (!is_string($table_name) || empty($table_name)) {
            return false;
        }

        // Only allow alphanumeric, underscores, and hyphens (covers WordPress table names)
        if (!preg_match('/^[a-zA-Z0-9_\-]+$/', $table_name)) {
            return false;
        }

        // Limit length to prevent buffer overflow attacks (MySQL max is 64)
        if (strlen($table_name) > 64) {
            return false;
        }

        // Verify it's a real table in the database (whitelist validation)
        $all_tables = $wpdb->get_col("SHOW TABLES");
        if (!in_array($table_name, $all_tables, true)) {
            return false;
        }

        return $table_name;
    }

    /**
     * Sanitize a column name for safe use in SQL queries
     *
     * @param string $column_name The column name to sanitize
     * @return string Sanitized column name with backticks
     */
    private function sanitize_column_name($column_name) {
        // Only allow alphanumeric and underscores
        $clean = preg_replace('/[^a-zA-Z0-9_]/', '', $column_name);
        return '`' . $clean . '`';
    }

    public function __construct() {
        add_action('wp_ajax_switchboard_create_backup', [$this, 'ajax_create_backup']);
        add_action('wp_ajax_switchboard_get_existing_backups', [$this, 'ajax_get_existing_backups']);
        add_action('wp_ajax_switchboard_delete_backup', [$this, 'ajax_delete_backup']);
        add_action('wp_ajax_switchboard_get_backup_sizes', [$this, 'ajax_get_backup_sizes']);

        Switchboard_Data_Registry::get_instance()->auto_register($this, __FILE__);
    }

    public function register_data_locations() {
        return [
            'options' => [$this->option_key],
            'option_patterns' => [],
            'tables' => [],
            'post_meta' => [],
            'user_meta' => [],
        ];
    }

    public function render_settings($settings = []) {
        $nonce = wp_create_nonce('switchboard_nonce');
        $zip_handler = Switchboard_Zip_Handler::get_instance();

        // Check system requirements
        $system_ok = $zip_handler->check_system_requirements();
        $zip_support = $zip_handler->check_zip_support();
        ?>
        <style>
            .sb-simple-backups .sb-backup-grid {
                display: grid;
                grid-template-columns: repeat(2, 1fr);
                gap: 16px;
                margin-bottom: 24px;
            }
            @media (max-width: 782px) {
                .sb-simple-backups .sb-backup-grid {
                    grid-template-columns: 1fr;
                }
            }
            .sb-simple-backups .sb-backup-card {
                background: #fff;
                border: 1px solid #e5e7eb;
                border-radius: 8px;
                padding: 20px;
                transition: border-color 0.2s ease, box-shadow 0.2s ease;
            }
            .sb-simple-backups .sb-backup-card:hover {
                border-color: #2271b1;
                box-shadow: 0 2px 8px rgba(0,0,0,0.08);
            }
            .sb-simple-backups .sb-backup-card-header {
                display: flex;
                align-items: center;
                gap: 12px;
                margin-bottom: 12px;
            }
            .sb-simple-backups .sb-backup-icon {
                width: 40px;
                height: 40px;
                display: flex;
                align-items: center;
                justify-content: center;
                background: #f3f4f6;
                border-radius: 8px;
            }
            .sb-simple-backups .sb-backup-icon .dashicons {
                font-size: 20px;
                width: 20px;
                height: 20px;
                color: #6b7280;
            }
            .sb-simple-backups .sb-backup-card[data-type="plugins"] .sb-backup-icon { background: #dbeafe; }
            .sb-simple-backups .sb-backup-card[data-type="plugins"] .sb-backup-icon .dashicons { color: #2563eb; }
            .sb-simple-backups .sb-backup-card[data-type="themes"] .sb-backup-icon { background: #fce7f3; }
            .sb-simple-backups .sb-backup-card[data-type="themes"] .sb-backup-icon .dashicons { color: #db2777; }
            .sb-simple-backups .sb-backup-card[data-type="uploads"] .sb-backup-icon { background: #d1fae5; }
            .sb-simple-backups .sb-backup-card[data-type="uploads"] .sb-backup-icon .dashicons { color: #059669; }
            .sb-simple-backups .sb-backup-card[data-type="database"] .sb-backup-icon { background: #fef3c7; }
            .sb-simple-backups .sb-backup-card[data-type="database"] .sb-backup-icon .dashicons { color: #d97706; }
            .sb-simple-backups .sb-backup-title {
                font-size: 15px;
                font-weight: 600;
                margin: 0;
            }
            .sb-simple-backups .sb-backup-size {
                font-size: 12px;
                color: #6b7280;
                margin-top: 2px;
            }
            .sb-simple-backups .sb-backup-size.loading {
                color: #9ca3af;
                font-style: italic;
            }
            .sb-simple-backups .sb-backup-size.warning {
                color: #d97706;
            }
            .sb-simple-backups .sb-backup-size.error {
                color: #dc2626;
            }
            .sb-simple-backups .sb-backup-desc {
                font-size: 12px;
                color: #6b7280;
                margin: 0 0 16px 0;
                line-height: 1.5;
            }
            .sb-simple-backups .sb-backup-actions {
                display: flex;
                gap: 8px;
            }
            .sb-simple-backups .sb-backup-actions .sb-module-button {
                flex: 1;
                text-align: center;
                justify-content: center;
            }
            .sb-simple-backups .sb-backup-progress {
                display: none;
                margin-top: 12px;
                padding: 10px;
                background: #eff6ff;
                border-radius: 6px;
                font-size: 12px;
                color: #1e40af;
            }
            .sb-simple-backups .sb-backup-progress .sb-progress-bar {
                height: 4px;
                background: #dbeafe;
                border-radius: 2px;
                margin-top: 8px;
                overflow: hidden;
            }
            .sb-simple-backups .sb-backup-progress .sb-progress-bar-fill {
                height: 100%;
                background: #3b82f6;
                border-radius: 2px;
                animation: sb-progress-pulse 2s ease-in-out infinite;
            }
            @keyframes sb-progress-pulse {
                0%, 100% { width: 30%; margin-left: 0%; }
                50% { width: 50%; margin-left: 25%; }
            }
            .sb-simple-backups .sb-backup-result {
                display: none;
                margin-top: 12px;
                padding: 12px;
                background: #f0fdf4;
                border: 1px solid #bbf7d0;
                border-radius: 6px;
            }
            .sb-simple-backups .sb-backup-result a {
                display: inline-block;
                margin-top: 8px;
                padding: 6px 12px;
                background: #10b981;
                color: white;
                text-decoration: none;
                border-radius: 4px;
                font-size: 12px;
                font-weight: 500;
            }
            .sb-simple-backups .sb-backup-result a:hover {
                background: #059669;
            }
            .sb-simple-backups .sb-backup-error {
                display: none;
                margin-top: 12px;
                padding: 12px;
                background: #fef2f2;
                border: 1px solid #fecaca;
                border-radius: 6px;
                color: #dc2626;
                font-size: 12px;
            }
            .sb-simple-backups .sb-existing-backups-table {
                width: 100%;
                border-collapse: collapse;
                font-size: 13px;
            }
            .sb-simple-backups .sb-existing-backups-table th,
            .sb-simple-backups .sb-existing-backups-table td {
                padding: 10px 12px;
                text-align: left;
                border-bottom: 1px solid #e5e7eb;
            }
            .sb-simple-backups .sb-existing-backups-table th {
                background: #f9fafb;
                font-weight: 600;
                font-size: 11px;
                text-transform: uppercase;
                color: #6b7280;
            }
            .sb-simple-backups .sb-existing-backups-table .sb-backup-type {
                display: inline-block;
                padding: 2px 8px;
                border-radius: 4px;
                font-size: 11px;
                font-weight: 500;
            }
            .sb-simple-backups .sb-existing-backups-table .sb-backup-type.plugins { background: #dbeafe; color: #1e40af; }
            .sb-simple-backups .sb-existing-backups-table .sb-backup-type.themes { background: #fce7f3; color: #9d174d; }
            .sb-simple-backups .sb-existing-backups-table .sb-backup-type.uploads { background: #d1fae5; color: #065f46; }
            .sb-simple-backups .sb-existing-backups-table .sb-backup-type.database { background: #fef3c7; color: #92400e; }
            .sb-simple-backups .sb-zip-actions {
                display: flex;
                gap: 6px;
            }
            .sb-simple-backups .sb-zip-actions a,
            .sb-simple-backups .sb-zip-actions button {
                font-size: 11px;
                padding: 4px 8px;
            }
            .sb-simple-backups .sb-zip-actions a.sb-module-button-primary {
                background: #4f7df3;
                border-color: #4f7df3;
                color: white;
            }
        </style>

        <div class="sb-simple-backups">
            <input type="hidden" id="backup-nonce" value="<?php echo esc_attr($nonce); ?>">

            <?php if (!$system_ok || !$zip_support['available']): ?>
            <div class="sb-sp-info-box sb-sp-info-box-tip" style="margin-bottom: 24px; background: #fef2f2; border-color: #fecaca;">
                <h4 class="sb-sp-heading-sm" style="color: #dc2626;">System Requirements Not Met</h4>
                <p class="sb-sp-description" style="margin: 0; color: #991b1b;">
                    <?php if (!$zip_support['available']): ?>
                    ZIP extension is not available. Please contact your hosting provider to enable PHP ZIP extension or zlib support.
                    <?php else: ?>
                    Required PHP classes are not available. Please contact your hosting provider.
                    <?php endif; ?>
                </p>
            </div>
            <?php else: ?>

            <div class="sb-sp-info-box sb-sp-info-box-info" style="margin-bottom: 24px;">
                <h4 class="sb-sp-heading-sm">Simple Backups</h4>
                <p class="sb-sp-description" style="margin: 0;">Download individual WordPress components as ZIP files. Each backup is created fresh and can be kept indefinitely or auto-deleted after 1 hour.</p>
            </div>

            <div class="sb-sp-group" style="margin-bottom: 24px;">
                <div class="sb-module-option-item sb-module-toggle-item">
                    <div class="sb-module-option-content">
                        <div class="sb-module-toggle-header">
                            <strong class="sb-module-option-title">Auto-delete backup files</strong>
                            <label class="sb-module-toggle">
                                <input type="checkbox" id="auto-delete-toggle" checked>
                                <span class="sb-module-toggle-slider"></span>
                            </label>
                        </div>
                        <p class="sb-module-option-text">When enabled, backup files are automatically deleted after 1 hour. Disable to keep them until manually deleted.</p>
                    </div>
                </div>
            </div>

            <div class="sb-backup-grid">
                <!-- Plugins Backup -->
                <div class="sb-backup-card" data-type="plugins">
                    <div class="sb-backup-card-header">
                        <div class="sb-backup-icon"><span class="dashicons dashicons-admin-plugins"></span></div>
                        <div>
                            <h4 class="sb-backup-title">Plugins</h4>
                            <div class="sb-backup-size loading" data-size-for="plugins">Calculating size...</div>
                        </div>
                    </div>
                    <p class="sb-backup-desc">Download all installed plugins as a single ZIP file. Includes all plugin folders from wp-content/plugins.</p>
                    <div class="sb-backup-actions">
                        <button type="button" class="sb-module-button sb-module-button-primary" data-backup-type="plugins">
                            Download Plugins ZIP
                        </button>
                    </div>
                    <div class="sb-backup-progress">
                        <span>Creating backup... This may take a while for large plugin folders.</span>
                        <div class="sb-progress-bar"><div class="sb-progress-bar-fill"></div></div>
                    </div>
                    <div class="sb-backup-result"></div>
                    <div class="sb-backup-error"></div>
                </div>

                <!-- Themes Backup -->
                <div class="sb-backup-card" data-type="themes">
                    <div class="sb-backup-card-header">
                        <div class="sb-backup-icon"><span class="dashicons dashicons-admin-appearance"></span></div>
                        <div>
                            <h4 class="sb-backup-title">Themes</h4>
                            <div class="sb-backup-size loading" data-size-for="themes">Calculating size...</div>
                        </div>
                    </div>
                    <p class="sb-backup-desc">Download all installed themes as a single ZIP file. Includes all theme folders from wp-content/themes.</p>
                    <div class="sb-backup-actions">
                        <button type="button" class="sb-module-button sb-module-button-primary" data-backup-type="themes">
                            Download Themes ZIP
                        </button>
                    </div>
                    <div class="sb-backup-progress">
                        <span>Creating backup... This may take a while for large theme folders.</span>
                        <div class="sb-progress-bar"><div class="sb-progress-bar-fill"></div></div>
                    </div>
                    <div class="sb-backup-result"></div>
                    <div class="sb-backup-error"></div>
                </div>

                <!-- Uploads Backup -->
                <div class="sb-backup-card" data-type="uploads">
                    <div class="sb-backup-card-header">
                        <div class="sb-backup-icon"><span class="dashicons dashicons-admin-media"></span></div>
                        <div>
                            <h4 class="sb-backup-title">Uploads</h4>
                            <div class="sb-backup-size loading" data-size-for="uploads">Calculating size...</div>
                        </div>
                    </div>
                    <p class="sb-backup-desc">Download the uploads folder as a ZIP file. Contains all media files uploaded to your site.</p>
                    <div class="sb-backup-actions">
                        <button type="button" class="sb-module-button sb-module-button-primary" data-backup-type="uploads">
                            Download Uploads ZIP
                        </button>
                    </div>
                    <div class="sb-backup-progress">
                        <span>Creating backup... Large upload folders may take several minutes.</span>
                        <div class="sb-progress-bar"><div class="sb-progress-bar-fill"></div></div>
                    </div>
                    <div class="sb-backup-result"></div>
                    <div class="sb-backup-error"></div>
                </div>

                <!-- Database Backup -->
                <div class="sb-backup-card" data-type="database">
                    <div class="sb-backup-card-header">
                        <div class="sb-backup-icon"><span class="dashicons dashicons-database"></span></div>
                        <div>
                            <h4 class="sb-backup-title">Database</h4>
                            <div class="sb-backup-size loading" data-size-for="database">Calculating size...</div>
                        </div>
                    </div>
                    <p class="sb-backup-desc">Export the entire database as a SQL file (compressed). Includes all tables, not just WordPress defaults.</p>
                    <div class="sb-backup-actions">
                        <button type="button" class="sb-module-button sb-module-button-primary" data-backup-type="database">
                            Download Database ZIP
                        </button>
                    </div>
                    <div class="sb-backup-progress">
                        <span>Exporting database... This may take a while for large databases.</span>
                        <div class="sb-progress-bar"><div class="sb-progress-bar-fill"></div></div>
                    </div>
                    <div class="sb-backup-result"></div>
                    <div class="sb-backup-error"></div>
                </div>
            </div>

            <hr class="sb-sp-divider">

            <div class="sb-sp-group" style="margin-bottom: 24px;">
                <h3 class="sb-sp-heading">Existing Backups</h3>
                <p class="sb-sp-description" style="margin-bottom: 16px;">Previously created backup files that are still available on the server.</p>
                <div id="existing-backups-list">
                    <p class="sb-sp-description" style="font-style: italic;">Loading...</p>
                </div>
            </div>

            <div class="sb-sp-info-box sb-sp-info-box-tip">
                <h4 class="sb-sp-heading-sm">Important Notes</h4>
                <ul class="sb-sp-list">
                    <li class="sb-sp-list-item"><strong>Size Limits:</strong> Maximum 2GB per backup. Backups over 500MB may take longer and show a warning.</li>
                    <li class="sb-sp-list-item"><strong>Database:</strong> Exports ALL tables in the database, including those from other plugins and custom tables.</li>
                    <li class="sb-sp-list-item"><strong>Security:</strong> Backup files are stored temporarily. Download them promptly and store securely offline.</li>
                    <li class="sb-sp-list-item"><strong>Not a Full Backup:</strong> This creates individual component backups. For complete site backups with restore functionality, consider dedicated plugins like <strong>All-in-One WP Migration</strong>, <strong>UpdraftPlus</strong>, or <strong>Duplicator</strong>.</li>
                </ul>
            </div>

            <div class="sb-sp-info-box" style="background: #fef3c7; border-color: #fcd34d;">
                <h4 class="sb-sp-heading-sm" style="color: #92400e;">Need Full Site Backups?</h4>
                <p class="sb-sp-description" style="margin: 0; color: #78350f;">
                    For complete site backups with easy restore, scheduled backups, and cloud storage integration, we recommend:
                </p>
                <ul class="sb-sp-list" style="margin-top: 8px; margin-bottom: 0;">
                    <li class="sb-sp-list-item" style="color: #78350f;"><strong>All-in-One WP Migration</strong> — Simple one-click export/import, great for migrations</li>
                    <li class="sb-sp-list-item" style="color: #78350f;"><strong>UpdraftPlus</strong> — Scheduled backups, cloud storage, restore functionality</li>
                    <li class="sb-sp-list-item" style="color: #78350f;"><strong>Duplicator</strong> — Full site packages, ideal for cloning and migration</li>
                </ul>
            </div>

            <?php endif; ?>
        </div>

        <script>
        jQuery(document).ready(function($) {
            const nonce = $('#backup-nonce').val();

            // Load sizes on page load
            loadBackupSizes();

            // Load existing backups
            loadExistingBackups();

            function loadBackupSizes() {
                $.ajax({
                    url: ajaxurl,
                    type: 'POST',
                    data: {
                        action: 'switchboard_get_backup_sizes',
                        nonce: nonce
                    },
                    success: function(response) {
                        if (response.success) {
                            const sizes = response.data;
                            const warningThreshold = 500 * 1024 * 1024;  // 500MB warning
                            const maxSize = 2 * 1024 * 1024 * 1024;       // 2GB limit

                            Object.keys(sizes).forEach(function(type) {
                                const $sizeEl = $('[data-size-for="' + type + '"]');
                                $sizeEl.removeClass('loading warning error');

                                if (sizes[type].error) {
                                    $sizeEl.addClass('error').text(sizes[type].error);
                                    $('[data-backup-type="' + type + '"]').prop('disabled', true);
                                } else if (sizes[type].size_bytes > maxSize) {
                                    $sizeEl.addClass('error').text(sizes[type].size_human + ' (exceeds 2GB limit)');
                                    $('[data-backup-type="' + type + '"]').prop('disabled', true);
                                } else if (sizes[type].size_bytes > warningThreshold) {
                                    $sizeEl.addClass('warning').text(sizes[type].size_human + ' (large, may take time)');
                                } else {
                                    $sizeEl.text(sizes[type].size_human);
                                }
                            });
                        }
                    }
                });
            }

            // Handle backup button clicks
            $('.sb-backup-actions .sb-module-button-primary').on('click', function() {
                const $btn = $(this);
                const $card = $btn.closest('.sb-backup-card');
                const backupType = $btn.data('backup-type');
                const autoDelete = $('#auto-delete-toggle').is(':checked');

                const $progress = $card.find('.sb-backup-progress');
                const $result = $card.find('.sb-backup-result');
                const $error = $card.find('.sb-backup-error');

                $btn.prop('disabled', true);
                $progress.slideDown();
                $result.slideUp();
                $error.slideUp();

                $.ajax({
                    url: ajaxurl,
                    type: 'POST',
                    data: {
                        action: 'switchboard_create_backup',
                        backup_type: backupType,
                        auto_delete: autoDelete ? 1 : 0,
                        nonce: nonce
                    },
                    timeout: 600000, // 10 minute timeout
                    success: function(response) {
                        $btn.prop('disabled', false);
                        $progress.slideUp();

                        if (response.success) {
                            const data = response.data;
                            const expiryText = data.auto_delete ? 'Auto-deletes in 1 hour' : 'Kept until deleted';
                            $result.html(`
                                <strong style="color: #065f46;">Backup Created!</strong>
                                <div style="font-size: 12px; color: #6b7280; margin-top: 4px;">${data.file_count} items • ${data.size_human} • ${expiryText}</div>
                                <a href="${data.download_url}" download="${data.filename}">Download Backup</a>
                            `).slideDown();
                            loadExistingBackups();
                        } else {
                            $error.text(response.data || 'Failed to create backup').slideDown();
                        }
                    },
                    error: function(xhr, status, error) {
                        $btn.prop('disabled', false);
                        $progress.slideUp();

                        if (status === 'timeout') {
                            $error.text('Request timed out. The backup may be too large for your server configuration.').slideDown();
                        } else {
                            $error.text('Error creating backup: ' + error).slideDown();
                        }
                    }
                });
            });

            function loadExistingBackups() {
                $.ajax({
                    url: ajaxurl,
                    type: 'POST',
                    data: {
                        action: 'switchboard_get_existing_backups',
                        nonce: nonce
                    },
                    success: function(response) {
                        if (response.success && response.data.backups.length > 0) {
                            renderExistingBackups(response.data.backups);
                        } else {
                            $('#existing-backups-list').html('<p class="sb-sp-description" style="font-style: italic;">No existing backup files found.</p>');
                        }
                    }
                });
            }

            function renderExistingBackups(backups) {
                // Filter out expired
                backups = backups.filter(function(b) { return !b.is_expired; });

                if (backups.length === 0) {
                    $('#existing-backups-list').html('<p class="sb-sp-description" style="font-style: italic;">No existing backup files found.</p>');
                    return;
                }

                let html = `
                    <table class="sb-existing-backups-table">
                        <thead>
                            <tr>
                                <th>Type</th>
                                <th>Size</th>
                                <th>Created</th>
                                <th>Status</th>
                                <th>Actions</th>
                            </tr>
                        </thead>
                        <tbody>
                `;

                backups.forEach(function(backup) {
                    const expiryText = backup.auto_delete ? (backup.expires_in ? 'Expires in ' + backup.expires_in : 'Expired') : 'Kept';
                    const expiryStyle = backup.auto_delete ? 'color: #9ca3af;' : 'color: #10b981; font-weight: 500;';

                    html += `
                        <tr data-backup-id="${backup.backup_id}">
                            <td><span class="sb-backup-type ${backup.type}">${backup.type.charAt(0).toUpperCase() + backup.type.slice(1)}</span></td>
                            <td>${backup.size}</td>
                            <td style="color: #6b7280;">${backup.created_human}</td>
                            <td style="${expiryStyle}">${expiryText}</td>
                            <td>
                                <div class="sb-zip-actions">
                                    <a href="${backup.download_url}" download="${backup.filename}" class="sb-module-button sb-module-button-primary">Download</a>
                                    <button type="button" class="sb-module-button sb-module-button-danger sb-delete-backup-btn" data-backup-id="${backup.backup_id}">Delete</button>
                                </div>
                            </td>
                        </tr>
                    `;
                });

                html += '</tbody></table>';
                $('#existing-backups-list').html(html);

                // Bind delete buttons
                $('.sb-delete-backup-btn').on('click', function() {
                    const $btn = $(this);
                    const backupId = $btn.data('backup-id');
                    const $row = $btn.closest('tr');

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

                    $.ajax({
                        url: ajaxurl,
                        type: 'POST',
                        data: {
                            action: 'switchboard_delete_backup',
                            backup_id: backupId,
                            nonce: nonce
                        },
                        success: function(response) {
                            if (response.success) {
                                $row.fadeOut(function() {
                                    $(this).remove();
                                    if ($('.sb-existing-backups-table tbody tr').length === 0) {
                                        $('#existing-backups-list').html('<p class="sb-sp-description" style="font-style: italic;">No existing backup files found.</p>');
                                    }
                                });
                            } else {
                                $btn.prop('disabled', false).text('Delete');
                            }
                        },
                        error: function() {
                            $btn.prop('disabled', false).text('Delete');
                        }
                    });
                });
            }
        });
        </script>
        <?php
    }

    /**
     * AJAX: Get backup component sizes
     */
    public function ajax_get_backup_sizes() {
        check_ajax_referer('switchboard_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Unauthorized', 'wpswitchboard'));
        }

        $zip_handler = Switchboard_Zip_Handler::get_instance();
        $sizes = [];

        // Plugins
        $plugins_dir = WP_PLUGIN_DIR;
        if (is_dir($plugins_dir)) {
            $size = $zip_handler->get_directory_size($plugins_dir);
            $sizes['plugins'] = [
                'size_bytes' => $size,
                'size_human' => size_format($size),
            ];
        } else {
            $sizes['plugins'] = ['error' => 'Directory not found'];
        }

        // Themes
        $themes_dir = get_theme_root();
        if (is_dir($themes_dir)) {
            $size = $zip_handler->get_directory_size($themes_dir);
            $sizes['themes'] = [
                'size_bytes' => $size,
                'size_human' => size_format($size),
            ];
        } else {
            $sizes['themes'] = ['error' => 'Directory not found'];
        }

        // Uploads
        $upload_dir = wp_upload_dir();
        $uploads_dir = $upload_dir['basedir'];
        if (is_dir($uploads_dir)) {
            $size = $zip_handler->get_directory_size($uploads_dir);
            $sizes['uploads'] = [
                'size_bytes' => $size,
                'size_human' => size_format($size),
            ];
        } else {
            $sizes['uploads'] = ['error' => 'Directory not found'];
        }

        // Database
        global $wpdb;
        $tables = $wpdb->get_results("SHOW TABLE STATUS", ARRAY_A);
        $total_size = 0;
        if ($tables) {
            foreach ($tables as $table) {
                $total_size += isset($table['Data_length']) ? (int) $table['Data_length'] : 0;
                $total_size += isset($table['Index_length']) ? (int) $table['Index_length'] : 0;
            }
        }
        $sizes['database'] = [
            'size_bytes' => $total_size,
            'size_human' => size_format($total_size) . ' (approx)',
            'table_count' => count($tables),
        ];

        wp_send_json_success($sizes);
    }

    /**
     * AJAX: Create backup
     */
    public function ajax_create_backup() {
        check_ajax_referer('switchboard_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Unauthorized', 'wpswitchboard'));
        }

        $backup_type = isset($_POST['backup_type']) ? sanitize_text_field($_POST['backup_type']) : '';
        $auto_delete = isset($_POST['auto_delete']) ? (bool) intval($_POST['auto_delete']) : true;

        if (!in_array($backup_type, ['plugins', 'themes', 'uploads', 'database'])) {
            wp_send_json_error(__('Invalid backup type.', 'wpswitchboard'));
        }

        $zip_handler = Switchboard_Zip_Handler::get_instance();

        // Ensure download directory exists
        if (!$zip_handler->ensure_download_dir()) {
            wp_send_json_error(__('Failed to create download directory.', 'wpswitchboard'));
        }

        // Increase limits
        @ini_set('memory_limit', '512M');
        @set_time_limit(600);

        $result = null;

        switch ($backup_type) {
            case 'plugins':
                $result = $this->create_directory_backup(WP_PLUGIN_DIR, 'plugins', $auto_delete);
                break;
            case 'themes':
                $result = $this->create_directory_backup(get_theme_root(), 'themes', $auto_delete);
                break;
            case 'uploads':
                $upload_dir = wp_upload_dir();
                $result = $this->create_directory_backup($upload_dir['basedir'], 'uploads', $auto_delete);
                break;
            case 'database':
                $result = $this->create_database_backup($auto_delete);
                break;
        }

        if (is_wp_error($result)) {
            wp_send_json_error($result->get_error_message());
        }

        wp_send_json_success($result);
    }

    /**
     * Create directory backup using shared ZIP handler
     */
    private function create_directory_backup($source_dir, $type, $auto_delete) {
        $zip_handler = Switchboard_Zip_Handler::get_instance();

        if (!is_dir($source_dir)) {
            return new WP_Error('dir_not_found', __('Source directory not found.', 'wpswitchboard'));
        }

        // Check size
        $size = $zip_handler->get_directory_size($source_dir);
        if ($size > Switchboard_Zip_Handler::MAX_SIZE) {
            return new WP_Error(
                'too_large',
                sprintf(
                    __('Directory size (%s) exceeds maximum limit (500MB). Please use FTP for larger backups.', 'wpswitchboard'),
                    size_format($size)
                )
            );
        }

        // Check disk space
        if (!$zip_handler->has_disk_space($size)) {
            return new WP_Error('no_space', __('Insufficient disk space to create backup.', 'wpswitchboard'));
        }

        // Generate backup ID and paths
        $site_name = sanitize_file_name(get_bloginfo('name'));
        $date = date('Y-m-d');
        $backup_id = 'backup-' . $type . '-' . $date . '-' . wp_generate_password(8, false);
        $zip_dir = $zip_handler->get_download_dir();
        $zip_path = $zip_dir . '/' . $backup_id . '.zip';
        $zip_filename = $site_name . '-' . $type . '-' . $date . '.zip';

        // Count items
        $file_count = 0;
        try {
            $files = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($source_dir, RecursiveDirectoryIterator::SKIP_DOTS),
                RecursiveIteratorIterator::LEAVES_ONLY
            );
            foreach ($files as $file) {
                if ($file->isFile()) {
                    $file_count++;
                }
            }
        } catch (Exception $e) {
            // Fallback count
            $file_count = 0;
        }

        // Create ZIP
        $result = $zip_handler->create_zip_from_directory($source_dir, $zip_path, $type);

        if (is_wp_error($result)) {
            return $result;
        }

        // Verify ZIP
        if (!file_exists($zip_path) || filesize($zip_path) === 0) {
            return new WP_Error('zip_failed', __('Failed to create backup file.', 'wpswitchboard'));
        }

        // Save metadata
        $zip_handler->save_metadata($backup_id, [
            'type' => $type,
            'source' => $source_dir,
            'file_count' => $file_count,
            'auto_delete' => $auto_delete,
        ]);

        $upload_dir = wp_upload_dir();

        return [
            'backup_id' => $backup_id,
            'download_url' => trailingslashit($upload_dir['baseurl']) . 'switchboard-downloads/' . $backup_id . '.zip',
            'filename' => $zip_filename,
            'size' => filesize($zip_path),
            'size_human' => size_format(filesize($zip_path)),
            'file_count' => $file_count,
            'auto_delete' => $auto_delete,
        ];
    }

    /**
     * Create database backup
     */
    private function create_database_backup($auto_delete) {
        global $wpdb;

        $zip_handler = Switchboard_Zip_Handler::get_instance();

        // Generate backup ID and paths
        $site_name = sanitize_file_name(get_bloginfo('name'));
        $date = date('Y-m-d');
        $backup_id = 'backup-database-' . $date . '-' . wp_generate_password(8, false);
        $zip_dir = $zip_handler->get_download_dir();
        $sql_file = $zip_dir . '/' . $backup_id . '.sql';
        $zip_path = $zip_dir . '/' . $backup_id . '.zip';
        $zip_filename = $site_name . '-database-' . $date . '.zip';

        // Get all tables
        $tables = $wpdb->get_col("SHOW TABLES");

        if (empty($tables)) {
            return new WP_Error('no_tables', __('No database tables found.', 'wpswitchboard'));
        }

        // Open SQL file for writing
        $handle = fopen($sql_file, 'w');
        if (!$handle) {
            return new WP_Error('file_error', __('Failed to create SQL file.', 'wpswitchboard'));
        }

        // Write header
        fwrite($handle, "-- Switchboard Database Backup\n");
        fwrite($handle, "-- Generated: " . date('Y-m-d H:i:s') . "\n");
        fwrite($handle, "-- Site: " . get_bloginfo('name') . "\n");
        fwrite($handle, "-- URL: " . get_bloginfo('url') . "\n");
        fwrite($handle, "-- Tables: " . count($tables) . "\n\n");
        fwrite($handle, "SET SQL_MODE = \"NO_AUTO_VALUE_ON_ZERO\";\n");
        fwrite($handle, "SET time_zone = \"+00:00\";\n");
        fwrite($handle, "SET NAMES utf8mb4;\n\n");

        $row_count = 0;

        foreach ($tables as $table) {
            // Sanitize table name to prevent SQL injection
            $safe_table = $this->sanitize_table_name($table);
            if ($safe_table === false) {
                // Skip invalid table names
                continue;
            }

            // Get create table statement
            $create = $wpdb->get_row("SHOW CREATE TABLE `{$safe_table}`", ARRAY_N);
            if ($create) {
                fwrite($handle, "-- Table structure for table `{$safe_table}`\n");
                fwrite($handle, "DROP TABLE IF EXISTS `{$safe_table}`;\n");
                fwrite($handle, $create[1] . ";\n\n");
            }

            // Get table data in chunks
            $offset = 0;
            $limit = 1000;

            do {
                $rows = $wpdb->get_results(
                    $wpdb->prepare("SELECT * FROM `{$safe_table}` LIMIT %d OFFSET %d", $limit, $offset),
                    ARRAY_A
                );

                if (!empty($rows)) {
                    foreach ($rows as $row) {
                        $values = array_map(function($value) use ($wpdb) {
                            if ($value === null) {
                                return 'NULL';
                            }
                            return "'" . $wpdb->_real_escape($value) . "'";
                        }, array_values($row));

                        // Sanitize column names (only allow alphanumeric and underscores)
                        $columns = array_map(function($col) {
                            $clean = preg_replace('/[^a-zA-Z0-9_]/', '', $col);
                            return '`' . $clean . '`';
                        }, array_keys($row));

                        fwrite($handle, "INSERT INTO `{$safe_table}` (" . implode(', ', $columns) . ") VALUES (" . implode(', ', $values) . ");\n");
                        $row_count++;
                    }
                }

                $offset += $limit;
            } while (!empty($rows) && count($rows) === $limit);

            fwrite($handle, "\n");
        }

        fclose($handle);

        // Create ZIP from SQL file
        $zip_support = $zip_handler->check_zip_support();

        if ($zip_support['method'] === 'ZipArchive') {
            $zip = new ZipArchive();
            if ($zip->open($zip_path, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) {
                $zip->addFile($sql_file, basename($sql_file));
                $zip->close();
            } else {
                @unlink($sql_file);
                return new WP_Error('zip_failed', __('Failed to create ZIP archive.', 'wpswitchboard'));
            }
        } else {
            if (!class_exists('PclZip')) {
                require_once(ABSPATH . 'wp-admin/includes/class-pclzip.php');
            }
            $zip = new PclZip($zip_path);
            $result = $zip->create([$sql_file], PCLZIP_OPT_REMOVE_ALL_PATH);
            if ($result == 0) {
                @unlink($sql_file);
                return new WP_Error('zip_failed', __('Failed to create ZIP archive.', 'wpswitchboard'));
            }
        }

        // Delete SQL file (keep only ZIP)
        @unlink($sql_file);

        // Verify ZIP
        if (!file_exists($zip_path) || filesize($zip_path) === 0) {
            return new WP_Error('zip_failed', __('Failed to create backup file.', 'wpswitchboard'));
        }

        // Save metadata
        $zip_handler->save_metadata($backup_id, [
            'type' => 'database',
            'table_count' => count($tables),
            'row_count' => $row_count,
            'file_count' => count($tables),
            'auto_delete' => $auto_delete,
        ]);

        $upload_dir = wp_upload_dir();

        return [
            'backup_id' => $backup_id,
            'download_url' => trailingslashit($upload_dir['baseurl']) . 'switchboard-downloads/' . $backup_id . '.zip',
            'filename' => $zip_filename,
            'size' => filesize($zip_path),
            'size_human' => size_format(filesize($zip_path)),
            'file_count' => count($tables) . ' tables, ' . number_format($row_count) . ' rows',
            'auto_delete' => $auto_delete,
        ];
    }

    /**
     * AJAX: Get existing backups
     */
    public function ajax_get_existing_backups() {
        check_ajax_referer('switchboard_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Unauthorized', 'wpswitchboard'));
        }

        $zip_handler = Switchboard_Zip_Handler::get_instance();
        $zip_dir = $zip_handler->get_download_dir();
        $upload_dir = wp_upload_dir();

        if (!file_exists($zip_dir)) {
            wp_send_json_success(['backups' => []]);
        }

        $backups = [];
        $files = @glob($zip_dir . '/backup-*.zip');

        if ($files) {
            foreach ($files as $file) {
                $backup_id = basename($file, '.zip');
                $meta = $zip_handler->get_metadata($backup_id);

                // Default values
                $type = 'unknown';
                $auto_delete = true;
                $file_count = 0;

                if ($meta) {
                    $type = $meta['type'] ?? 'unknown';
                    $auto_delete = $meta['auto_delete'] ?? true;
                    $file_count = $meta['file_count'] ?? 0;
                } else {
                    // Try to parse type from filename
                    if (strpos($backup_id, 'backup-plugins') !== false) {
                        $type = 'plugins';
                    } elseif (strpos($backup_id, 'backup-themes') !== false) {
                        $type = 'themes';
                    } elseif (strpos($backup_id, 'backup-uploads') !== false) {
                        $type = 'uploads';
                    } elseif (strpos($backup_id, 'backup-database') !== false) {
                        $type = 'database';
                    }
                }

                // Calculate expiry
                $file_time = filemtime($file);
                $expiry_time = Switchboard_Zip_Handler::EXPIRY_TIME;
                $time_remaining = ($file_time + $expiry_time) - time();
                $is_expired = $auto_delete && $time_remaining <= 0;

                if ($is_expired) {
                    continue;
                }

                $backups[] = [
                    'backup_id' => $backup_id,
                    'download_url' => trailingslashit($upload_dir['baseurl']) . 'switchboard-downloads/' . basename($file),
                    'filename' => basename($file),
                    'type' => $type,
                    'file_count' => $file_count,
                    'created' => $file_time,
                    'created_human' => human_time_diff($file_time) . ' ago',
                    'expires_in' => $auto_delete && $time_remaining > 0 ? human_time_diff(time(), $file_time + $expiry_time) : null,
                    'is_expired' => $is_expired,
                    'auto_delete' => $auto_delete,
                    'size' => size_format(filesize($file)),
                ];
            }
        }

        // Sort by creation time (newest first)
        usort($backups, function($a, $b) {
            return $b['created'] - $a['created'];
        });

        wp_send_json_success(['backups' => $backups]);
    }

    /**
     * AJAX: Delete backup
     */
    public function ajax_delete_backup() {
        check_ajax_referer('switchboard_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Unauthorized', 'wpswitchboard'));
        }

        $backup_id = isset($_POST['backup_id']) ? sanitize_text_field($_POST['backup_id']) : '';

        if (empty($backup_id)) {
            wp_send_json_error(__('Invalid backup ID.', 'wpswitchboard'));
        }

        $zip_handler = Switchboard_Zip_Handler::get_instance();

        // Validate backup ID format
        if (!$zip_handler->validate_zip_id($backup_id)) {
            wp_send_json_error(__('Invalid backup ID format.', 'wpswitchboard'));
        }

        // Use shared delete method
        $result = $zip_handler->delete_zip($backup_id);

        if ($result) {
            wp_send_json_success(['deleted' => true]);
        } else {
            wp_send_json_error(__('Failed to delete backup.', 'wpswitchboard'));
        }
    }
}

// Initialize
$switchboard_instance = new SwitchboardSimpleBackups();
