<?php
// IV1 WP Tools – Backup AJAX & Funktionen
defined("ABSPATH") || exit;

add_action('wp_ajax_iv1wpt_run_backup', 'iv1wpt_run_backup_ajax');
function iv1wpt_run_backup_ajax(): void {
    check_ajax_referer('iv1wpt_ajax', '_ajax_nonce');

    if (!current_user_can('manage_options')) {
        wp_die(__('No permission.', 'iv1-wp-tools'));
    }

    iv1wpt_ensure_backup_dir();

    $type     = sanitize_text_field($_POST['backup_type'] ?? 'full');
    $messages = [];

    if ($type === 'full' || $type === 'files') {
        $file = iv1wpt_create_file_backup();
        if ($file) {
            $messages[] = __('File backup created:', 'iv1-wp-tools') . ' ' . basename($file) . ' (' . size_format(filesize($file)) . ')';
            iv1wpt_maybe_ftp_upload($file);
        } else {
            $messages[] = __('Error creating file backup.', 'iv1-wp-tools');
        }
        iv1wpt_cleanup_old_backups('files');
    }

    if ($type === 'full' || $type === 'db') {
        $file = iv1wpt_create_db_backup();
        if ($file) {
            $messages[] = __('Database backup created:', 'iv1-wp-tools') . ' ' . basename($file) . ' (' . size_format(filesize($file)) . ')';
            iv1wpt_maybe_ftp_upload($file);
        } else {
            $messages[] = __('Error creating database backup.', 'iv1-wp-tools');
        }
        iv1wpt_cleanup_old_backups('db');
    }

    $has_error = false;
    foreach ($messages as $m) {
        if (stripos($m, 'fehler') !== false || stripos($m, 'fehlgeschlagen') !== false) {
            $has_error = true;
            break;
        }
    }

    // E-Mail-Benachrichtigung
    iv1wpt_send_notification(!$has_error, 'Manuell (' . $type . ')', '', '', $messages);

    $class = $has_error ? 'notice-error' : 'notice-success';
    echo '<div class="notice ' . esc_attr($class) . ' is-dismissible"><p>' . implode('<br>', array_map('esc_html', $messages)) . '</p></div>';
    wp_die();
}

// ────────────────────────────────────────────────
// AJAX: Backup-Liste laden
// ────────────────────────────────────────────────
add_action('wp_ajax_iv1wpt_list_backups', 'iv1wpt_list_backups_ajax');
function iv1wpt_list_backups_ajax(): void {
    check_ajax_referer('iv1wpt_ajax', '_ajax_nonce');

    if (!current_user_can('manage_options')) {
        wp_die(__('No permission.', 'iv1-wp-tools'));
    }

    iv1wpt_render_backup_table();
    wp_die();
}

// ────────────────────────────────────────────────
// AJAX: Einzelnes Backup löschen
// ────────────────────────────────────────────────
add_action('wp_ajax_iv1wpt_delete_backup', 'iv1wpt_delete_backup_ajax');
function iv1wpt_delete_backup_ajax(): void {
    check_ajax_referer('iv1wpt_ajax', '_ajax_nonce');

    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('No permission.', 'iv1-wp-tools'));
    }

    $file = basename(sanitize_text_field($_POST['filename'] ?? ''));
    $path = IV1WPT_BACKUP_DIR . $file;

    if (empty($file) || !preg_match('/^(files|db)-/', $file)) {
        wp_send_json_error(__('Invalid file name.', 'iv1-wp-tools'));
    }

    if (!file_exists($path)) {
        wp_send_json_error(__('File not found.', 'iv1-wp-tools'));
    }

    if (@unlink($path)) {
        iv1wpt_maybe_ftp_delete($file);
        wp_send_json_success('Gelöscht: ' . $file);
    } else {
        wp_send_json_error(__('Delete failed.', 'iv1-wp-tools'));
    }
}

// ────────────────────────────────────────────────
// Dateien-Backup erstellen (chunk-basiert)
// ────────────────────────────────────────────────
function iv1wpt_create_file_backup() {
    @set_time_limit(0);
    @ini_set('memory_limit', '512M');

	$mb = 100;

    $opts     = get_option(IV1WPT_SCHEDULE_OPTION, iv1wpt_default_schedule());
    $patterns = array_filter(array_map('trim', explode("\n", $opts['exclude_patterns'] ?? '')));

    $file = IV1WPT_BACKUP_DIR . 'files-' . date('Y-m-d-H-i-s') . '.zip';

    $zip = new ZipArchive();
    if ($zip->open($file, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
		iv1wpt_plugin_log(
			sprintf(
				/* translators: %s: file path */
				__('IV1WPT: ZIP failed: %s', 'iv1-wp-tools'),
				$file
			)
		);		
        return false;
    }

    $root = realpath(ABSPATH);
    $backup_folder = basename(IV1WPT_BACKUP_DIR);

    // Maximale Einzeldateigröße – sehr große Dateien überspringen
    $max_filesize = $mb * 1024 * 1024;

    // Chunk-Größe: nach X Dateien ZIP schließen und neu öffnen
    // Das verhindert, dass close() zu lange dauert
    $chunk_size    = 500;
    $counter       = 0;
    $skipped_large = 0;

    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($root, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );

    foreach ($iterator as $item) {
        $path = $item->getRealPath();
        if ($path === false) continue;

        $rel = substr($path, strlen($root) + 1);
        if ($rel === false || $rel === '') continue;

        // Backup-Ordner selbst ausschließen
        if (strpos($rel, $backup_folder . '/') === 0 || $rel === $backup_folder) {
            continue;
        }

        // Konfigurierbare Ausschlüsse
        $skip = false;
        foreach ($patterns as $pattern) {
            if (stripos($rel, $pattern) !== false) {
                $skip = true;
                break;
            }
        }
        if ($skip) continue;

        if ($item->isDir()) {
            $zip->addEmptyDir($rel);
        } elseif ($item->isFile()) {
            // Sehr große Dateien überspringen
            $fsize = @filesize($path);
            if ($fsize !== false && $fsize > $max_filesize) {
                $skipped_large++;
				
				iv1wpt_plugin_log(
					sprintf(
						/* translators: 1: file path, 2: maximum file size in MB, 3: actual file size in MB */
						__('IV1WPT: Skipped large file: %1$s (> %2$d MB, actual: %3$s MB)', 'iv1-wp-tools'),
						$rel,
						$mb,
						size_format($fsize / 1024 / 1024, 2)
					)
				);				
				
                continue;
            }
            $zip->addFile($path, $rel);
        }

        $counter++;

        // Alle X Dateien: ZIP schließen und wieder öffnen (APPEND)
        // Das schreibt die bisherigen Daten auf die Platte und resettet den Timer
        if ($counter % $chunk_size === 0) {
            $zip->close();
            @set_time_limit(120);
            gc_collect_cycles();

            // ZIP wieder im Append-Modus öffnen
            if ($zip->open($file, ZipArchive::CREATE) !== true) {
				iv1wpt_plugin_log(
					sprintf(
						/* translators: %d: number of processed files */
						__('IV1WPT: ZIP re-open failed after %d files', 'iv1-wp-tools'),
						$counter
					)
				);				
                return false;
            }
        }
    }

    @set_time_limit(120);
    $zip->close();

    if ($skipped_large > 0) {
		iv1wpt_plugin_log(
			sprintf(
				/* translators: 1: number of skipped files, 2: maximum file size in MB */
				__('IV1WPT: %1$d file(s) skipped (> %2$d MB)', 'iv1-wp-tools'),
				$skipped_large,
				$mb
			)
		);		
    }

	iv1wpt_plugin_log(
		sprintf(
			/* translators: 1: backup file path, 2: number of entries */
			__('IV1WPT: Files backed up → %1$s (%2$d entries)', 'iv1-wp-tools'),
			$file,
			$counter
		)
	);	
    return $file;
}

// ────────────────────────────────────────────────
// Datenbank-Backup erstellen (chunk-basiert)
// ────────────────────────────────────────────────
function iv1wpt_create_db_backup() {
    global $wpdb;
    @set_time_limit(0);

    $tables = $wpdb->get_col('SHOW TABLES');
    if (empty($tables)) return false;

    $file = IV1WPT_BACKUP_DIR . 'db-' . date('Y-m-d-H-i-s') . '.sql';
    $fp   = @fopen($file, 'w');
    if (!$fp) {
		iv1wpt_plugin_log(
			sprintf(
				/* translators: %s: SQL file path */
				__('IV1WPT: SQL failed: %s', 'iv1-wp-tools'),
				$file
			)
		);		
        return false;
    }

    fwrite($fp, "-- IV1 WP Tools v" . IV1WPT_VERSION . " SQL Dump\n");
    fwrite($fp, "-- " . date('Y-m-d H:i:s') . "\n\n");
    fwrite($fp, "SET SQL_MODE = \"NO_AUTO_VALUE_ON_ZERO\";\n");
    fwrite($fp, "SET time_zone = \"+00:00\";\n\n");

    $chunk_size = 500;

    foreach ($tables as $table) {
        @set_time_limit(120);

        $create = $wpdb->get_row("SHOW CREATE TABLE `$table`", ARRAY_N);
        fwrite($fp, "\nDROP TABLE IF EXISTS `$table`;\n");
        fwrite($fp, $create[1] . ";\n\n");

        // Zeilenweise in Chunks exportieren statt alles auf einmal
        $offset = 0;
        while (true) {
            $rows = $wpdb->get_results(
                $wpdb->prepare("SELECT * FROM `$table` LIMIT %d OFFSET %d", $chunk_size, $offset),
                ARRAY_A
            );

            if (empty($rows)) break;

            foreach ($rows as $row) {
                $vals = array_map(function ($v) use ($wpdb) {
                    return $v === null ? 'NULL' : "'" . $wpdb->_real_escape($v) . "'";
                }, array_values($row));
                fwrite($fp, "INSERT INTO `$table` VALUES (" . implode(',', $vals) . ");\n");
            }

            $offset += $chunk_size;

            // Timer und Speicher freigeben
            if ($offset % 2000 === 0) {
                @set_time_limit(120);
                gc_collect_cycles();
            }
        }
    }

    fclose($fp);
	iv1wpt_plugin_log(
		sprintf(
			/* translators: %s: database backup file path */
			__('IV1WPT: Database backed up → %s', 'iv1-wp-tools'),
			$file
		)
	);	
    return $file;
}

// ────────────────────────────────────────────────
// FTP-Upload (optional)
// ────────────────────────────────────────────────
function iv1wpt_maybe_ftp_upload(string $local_file): bool {
    $settings = get_option(IV1WPT_FTP_OPTION, iv1wpt_default_ftp());
    if (empty($settings['enabled']) || empty($settings['host']) || empty($settings['username'])) {
        return false;
    }
    return iv1wpt_upload_to_ftp($local_file, $settings);
}

function iv1wpt_upload_to_ftp(string $local_file, array $settings): bool {
    $conn_id = $settings['use_ftps']
        ? @ftp_ssl_connect($settings['host'], $settings['port'] ?: 21, 30)
        : @ftp_connect($settings['host'], $settings['port'] ?: 21, 30);

    if (!$conn_id) {
		iv1wpt_plugin_log(
			sprintf(
				/* translators: %s: FTP host */
				__('IV1WPT FTP: Connection failed to %s', 'iv1-wp-tools'),
				$settings['host']
			)
		);		
        return false;
    }

    $login = @ftp_login($conn_id, $settings['username'], $settings['password']);
    if (!$login) {
		iv1wpt_plugin_log(
			sprintf(
				/* translators: %s: FTP username */
				__('IV1WPT FTP: Login failed for %s', 'iv1-wp-tools'),
				$settings['username']
			)
		);		
        ftp_close($conn_id);
        return false;
    }

    ftp_pasv($conn_id, !empty($settings['passive']));

    $remote_file = rtrim($settings['remote_dir'], '/') . '/' . basename($local_file);
    $upload = ftp_put($conn_id, $remote_file, $local_file, FTP_BINARY);
    ftp_close($conn_id);

    if ($upload) {
		iv1wpt_plugin_log(
			sprintf(
				/* translators: %s: remote FTP file path */
				__('IV1WPT FTP upload successful: %s', 'iv1-wp-tools'),
				$remote_file
			)
		);		
        return true;
    }

	iv1wpt_plugin_log(
		sprintf(
			/* translators: %s: remote FTP file path */
			__('IV1WPT FTP upload failed: %s', 'iv1-wp-tools'),
			$remote_file
		)
	);	
    return false;
}

// ────────────────────────────────────────────────
// FTP-Datei löschen (optional)
// ────────────────────────────────────────────────
function iv1wpt_maybe_ftp_delete(string $filename): bool {
    $settings = get_option(IV1WPT_FTP_OPTION, iv1wpt_default_ftp());
    if (empty($settings['enabled']) || empty($settings['host']) || empty($settings['username'])) {
        return false;
    }
    return iv1wpt_delete_from_ftp($filename, $settings);
}

function iv1wpt_delete_from_ftp(string $filename, array $settings): bool {
    $conn_id = $settings['use_ftps']
        ? @ftp_ssl_connect($settings['host'], $settings['port'] ?: 21, 30)
        : @ftp_connect($settings['host'], $settings['port'] ?: 21, 30);

    if (!$conn_id) {
		iv1wpt_plugin_log(
			sprintf(
				/* translators: %s: FTP host */
				__('IV1WPT FTP: Connection failed to %s (delete)', 'iv1-wp-tools'),
				$settings['host']
			)
		);		
        return false;
    }

    $login = @ftp_login($conn_id, $settings['username'], $settings['password']);
    if (!$login) {
		iv1wpt_plugin_log(
			sprintf(
				/* translators: %s: FTP username */
				__('IV1WPT FTP: Login failed for %s (delete)', 'iv1-wp-tools'),
				$settings['username']
			)
		);        
        ftp_close($conn_id);
        return false;
    }

    ftp_pasv($conn_id, !empty($settings['passive']));

    $remote_file = rtrim($settings['remote_dir'], '/') . '/' . basename($filename);
    $deleted = @ftp_delete($conn_id, $remote_file);
    ftp_close($conn_id);

    if ($deleted) {
		iv1wpt_plugin_log(
			sprintf(
				/* translators: %s: remote FTP file path */
				__('IV1WPT FTP delete successful: %s', 'iv1-wp-tools'),
				$remote_file
			)
		);		
        return true;
    }

	iv1wpt_plugin_log(
		sprintf(
			/* translators: %s: remote FTP file path */
			__('IV1WPT FTP delete failed: %s (file may not exist on server)', 'iv1-wp-tools'),
			$remote_file
		)
	);	
    return false;
}

// ────────────────────────────────────────────────
// E-Mail-Benachrichtigung senden
// ────────────────────────────────────────────────
function iv1wpt_send_notification(bool $success, string $type, string $filename = '', string $size = '', array $details = []): void {
    $opts = get_option(IV1WPT_EMAIL_OPTION, iv1wpt_default_email());

    if (empty($opts['enabled'])) {
        return;
    }

    // Nur senden wenn gewünscht
    if ($success && empty($opts['on_success'])) {
        return;
    }
    if (!$success && empty($opts['on_error'])) {
        return;
    }

    $to = !empty($opts['recipient']) ? $opts['recipient'] : get_option('admin_email');
    if (empty($to)) {
        return;
    }

    $site_name = get_bloginfo('name');
    $site_url  = home_url();
    $time      = wp_date('d.m.Y H:i:s');

    if ($success) {
        $subject = '[' . $site_name . '] Backup erfolgreich – ' . $type;
        $status_line = 'Erfolgreich';
    } else {
        $subject = '[' . $site_name . '] Backup FEHLGESCHLAGEN – ' . $type;
        $status_line = 'FEHLGESCHLAGEN';
    }

    // E-Mail-Text zusammenbauen
    $body  = "IV1 WP Tools – Benachrichtigung\n";
    $body .= str_repeat('─', 45) . "\n\n";
    $body .= "Website:    {$site_name}\n";
    $body .= "URL:        {$site_url}\n";
    $body .= "Zeitpunkt:  {$time}\n";
    $body .= "Typ:        {$type}\n";
    $body .= "Status:     {$status_line}\n";

    if (!empty($filename)) {
        $body .= "Datei:      {$filename}\n";
    }
    if (!empty($size)) {
        $body .= "Größe:      {$size}\n";
    }

    // Details (bei manuellem Backup mit mehreren Schritten)
    if (!empty($details)) {
        $body .= "\nDetails:\n";
        foreach ($details as $detail) {
            $body .= "  • {$detail}\n";
        }
    }

    $body .= "\n" . str_repeat('─', 45) . "\n";
    $body .= "IV1 WP Tools v" . IV1WPT_VERSION . "\n";

    $headers = ['Content-Type: text/plain; charset=UTF-8'];

    $sent = wp_mail($to, $subject, $body, $headers);

    if (!$sent) {
		iv1wpt_plugin_log(
			sprintf(
				/* translators: %s: recipient email address */
				__('IV1WPT: Email sending failed to %s', 'iv1-wp-tools'),
				$to
			)
		);		
    }
}

// ────────────────────────────────────────────────
// Alte Backups aufräumen (getrennt nach Typ)
// ────────────────────────────────────────────────
function iv1wpt_cleanup_old_backups(string $type = 'both'): void {
    $opts = get_option(IV1WPT_SCHEDULE_OPTION, iv1wpt_default_schedule());

    if ($type === 'files' || $type === 'both') {
        $keep = max(1, (int) ($opts['retention_files'] ?? 7));
        $files = glob(IV1WPT_BACKUP_DIR . 'files-*.zip');
        if ($files) {
            usort($files, fn($a, $b) => filemtime($b) - filemtime($a));
            foreach (array_slice($files, $keep) as $old) {
                iv1wpt_maybe_ftp_delete(basename($old));
                @unlink($old);
            }
        }
    }

    if ($type === 'db' || $type === 'both') {
        $keep = max(1, (int) ($opts['retention_db'] ?? 7));
        $files = glob(IV1WPT_BACKUP_DIR . 'db-*.sql');
        if ($files) {
            usort($files, fn($a, $b) => filemtime($b) - filemtime($a));
            foreach (array_slice($files, $keep) as $old) {
                iv1wpt_maybe_ftp_delete(basename($old));
                @unlink($old);
            }
        }
    }
}

// ────────────────────────────────────────────────
// Backup-Tabelle rendern (für AJAX + Seite)
// ────────────────────────────────────────────────
function iv1wpt_render_backup_table(): void {
    $files = glob(IV1WPT_BACKUP_DIR . '*.{zip,sql}', GLOB_BRACE);
    if (empty($files)) {
        echo '<p>Keine Backups vorhanden.</p>';
        return;
    }

    $has_pro = iv1wpt_has_pro();

    usort($files, fn($a, $b) => filemtime($b) - filemtime($a));

    echo '<table class="widefat striped">';
    echo '<thead><tr><th>' . esc_html__('Date', 'iv1-wp-tools') . '</th><th>' . esc_html__('Type', 'iv1-wp-tools') . '</th><th>' . esc_html__('Filename', 'iv1-wp-tools') . '</th><th>' . esc_html__('Size', 'iv1-wp-tools') . '</th><th>' . esc_html__('Actions', 'iv1-wp-tools') . '</th></tr></thead>';
    echo '<tbody>';

    foreach ($files as $file) {
        $name = basename($file);
        $date = wp_date('d.m.Y H:i:s', filemtime($file));
        $size = size_format(filesize($file));
        $is_db = strpos($name, 'db-') === 0;
        $type_label = $is_db ? __('Database', 'iv1-wp-tools') : __('Files', 'iv1-wp-tools');
        $type_class = $is_db ? 'iv1wpt-size-db' : 'iv1wpt-size-files';

        $relative = str_replace(WP_CONTENT_DIR . '/', '', $file);
        $url = content_url($relative);

        echo '<tr>';
        echo '<td>' . esc_html($date) . '</td>';
        echo '<td><span class="' . esc_attr($type_class) . '">' . esc_html($type_label) . '</span></td>';
        echo '<td>' . esc_html($name) . '</td>';
        echo '<td>' . esc_html($size) . '</td>';
        echo '<td>';

		$download_url = wp_nonce_url(
			add_query_arg([
				'action' => 'iv1wpt_download_file',
				'file'   => $name,
			], admin_url('admin-ajax.php')),
			'iv1wpt_download_file_' . $name,
			'_wpnonce'
		);

		echo '<a href="' . esc_url($download_url) . '" class="button button-small iv1wpt-icon-btn" title="' . esc_attr__('Download', 'iv1-wp-tools') . '" aria-label="' . esc_attr__('Download', 'iv1-wp-tools') . '">';
		echo '<span class="dashicons dashicons-download" aria-hidden="true"></span>';
		echo '</a> ';

		/*
        echo '<a href="' . esc_url($url) . '" class="button button-small iv1wpt-icon-btn" download title="' . esc_attr__('Download', 'iv1-wp-tools') . '" aria-label="' . esc_attr__('Download', 'iv1-wp-tools') . '">';
        echo '<span class="dashicons dashicons-download" aria-hidden="true"></span>';
        echo '</a> ';
		*/

        if ($is_db) {
            if ($has_pro) {
                echo '<button type="button" class="button button-small iv1wpt-icon-btn iv1wpt-restore-btn" data-file="' . esc_attr($name) . '" title="' . esc_attr__('Restore', 'iv1-wp-tools') . '" aria-label="' . esc_attr__('Restore', 'iv1-wp-tools') . '">';
                echo '<span class="dashicons dashicons-update" aria-hidden="true"></span>';
                echo '</button> ';
            } else {
                $target = iv1wpt_pro_is_installed() ? '' : ' target="_blank" rel="noopener noreferrer"';

                echo '<a href="' . esc_url(iv1wpt_pro_upgrade_url()) . '" class="button button-small iv1wpt-icon-btn iv1wpt-pro-restore-btn" title="' . esc_attr(iv1wpt_pro_upgrade_label()) . '"' . $target . ' aria-label="' . esc_attr__('Restore', 'iv1-wp-tools') . '">';
                echo '<span class="dashicons dashicons-lock" aria-hidden="true"></span>';
                echo '</a> ';
            }
        }

        echo '<button type="button" class="button button-small iv1wpt-icon-btn iv1wpt-delete-btn" data-file="' . esc_attr($name) . '" title="' . esc_attr__('Delete', 'iv1-wp-tools') . '" aria-label="' . esc_attr__('Delete', 'iv1-wp-tools') . '">';
        echo '<span class="dashicons dashicons-trash" aria-hidden="true"></span>';
        echo '</button>';

        echo '</td>';
        echo '</tr>';
    }

    echo '</tbody></table>';
}


// ────────────────────────────────────────────────
// AJAX: Datenbank-Restore
// ────────────────────────────────────────────────
add_action('wp_ajax_iv1wpt_restore_db', 'iv1wpt_restore_db_ajax');
function iv1wpt_restore_db_ajax(): void {
    check_ajax_referer('iv1wpt_ajax', '_ajax_nonce');
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('No permission.', 'iv1-wp-tools'));
    }

    // Pro-Check: Restore ist ein Pro-Feature
    if (!iv1wpt_has_pro()) {
        wp_send_json_error(__('Database Restore requires IV1 WP Tools Pro with a valid license.', 'iv1-wp-tools'));
    }

    $file = basename(sanitize_text_field($_POST['filename'] ?? ''));
    $path = IV1WPT_BACKUP_DIR . $file;

    if (empty($file) || !preg_match('/^db-.*\.sql$/', $file)) {
        wp_send_json_error(__('Invalid file name.', 'iv1-wp-tools'));
    }
    if (!file_exists($path)) {
        wp_send_json_error(__('File not found.', 'iv1-wp-tools'));
    }

    global $wpdb;
    @set_time_limit(0);

    $sql = file_get_contents($path);
    if ($sql === false) {
        wp_send_json_error(__('Could not read file.', 'iv1-wp-tools'));
    }

    // SQL in einzelne Statements aufteilen
    $statements = array_filter(array_map('trim', explode(";\n", $sql)));
    $executed = 0;
    $errors = 0;

    foreach ($statements as $stmt) {
        // Kommentare und leere Zeilen überspringen
        if (empty($stmt) || strpos($stmt, '--') === 0 || strpos($stmt, 'SET ') === 0) {
            continue;
        }
        $result = $wpdb->query($stmt);
        if ($result === false) {
            $errors++;
			iv1wpt_plugin_log(
				sprintf(
					/* translators: 1: database error message, 2: SQL statement excerpt */
					__('IV1WPT Restore Error: %1$s | Statement: %2$s', 'iv1-wp-tools'),
					$wpdb->last_error,
					mb_substr($stmt, 0, 100)
				)
			);			
        } else {
            $executed++;
        }
    }

	if ($errors > 0) {
		iv1wpt_plugin_log(
			sprintf(
				/* translators: 1: number of successful statements, 2: number of errors, 3: SQL file */
				__('IV1WPT Restore: %1$d OK, %2$d errors from %3$s', 'iv1-wp-tools'),
				$executed,
				$errors,
				$file
			)
		);		
		wp_send_json_success([
			'message' => sprintf(
				/* translators: 1: number of executed statements, 2: number of errors */
				__('Restore completed with warnings: %1$d statements executed, %2$d errors. Check error log.', 'iv1-wp-tools'),
				$executed, $errors
			),
			'status' => 'warning'
		]);
	} else {
		iv1wpt_plugin_log(
			sprintf(
				/* translators: 1: number of executed SQL statements, 2: SQL file */
				__('IV1WPT Restore: %1$d statements from %2$s', 'iv1-wp-tools'),
				$executed,
				$file
			)
		);		
		wp_send_json_success([
			'message' => sprintf(
				/* translators: 1: filename, 2: number of statements */
				__('Database successfully restored from %1$s (%2$d statements).', 'iv1-wp-tools'),
				$file, $executed
			),
			'status' => 'success'
		]);
	}
}

// ────────────────────────────────────────────────
// AJAX: Download Files
// ────────────────────────────────────────────────
add_action('wp_ajax_iv1wpt_download_file', 'iv1wpt_download_file_ajax');
function iv1wpt_download_file_ajax(): void {
    if (!current_user_can('manage_options')) {
        wp_die(__('No permission.', 'iv1-wp-tools'), 403);
    }

    $filename = isset($_GET['file'])
        ? sanitize_file_name(wp_unslash($_GET['file']))
        : '';

    if (!$filename) {
        wp_die(__('Invalid file.', 'iv1-wp-tools'), 400);
    }

    check_admin_referer('iv1wpt_download_file_' . $filename);

    $base_dir = trailingslashit(IV1WPT_BACKUP_DIR);
    $base     = realpath($base_dir);
    $file     = realpath($base_dir . $filename);

    if (!$file || !$base || strpos($file, $base . DIRECTORY_SEPARATOR) !== 0 || !is_file($file)) {
        wp_die(__('File not found.', 'iv1-wp-tools'), 404);
    }

    if (!preg_match('/\.(zip|sql)$/i', $filename)) {
        wp_die(__('Invalid file type.', 'iv1-wp-tools'), 400);
    }

    nocache_headers();

    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . basename($file) . '"');
    header('Content-Length: ' . filesize($file));
    header('X-Content-Type-Options: nosniff');

    while (ob_get_level()) {
        ob_end_clean();
    }

    readfile($file);
    exit;
}