<?php
/**
 * RestoreManager - Điều phối quá trình phục hồi theo state machine
 *
 * Steps: extract_meta → import_db → replace_domain → restore_files → done
 */

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

class Foxblock_RestoreManager
{
    const TIME_LIMIT    = 8;  // giây mỗi request
    const SQL_CHUNK     = 10000; // số câu lệnh SQL mỗi request
    const FILES_CHUNK   = 5000; // số file phục hồi mỗi request

    /**
     * Bắt đầu restore từ archive_path
     */
    public static function init($archive_path)
    {
        Foxblock_Restore_StateManager_Compat::clear();
        $state = Foxblock_Backup_StateManager::init_restore_state($archive_path);

        // Đọc TOC ngay
        $result = Foxblock_Backup_FileHandler::read_archive_toc($archive_path);
        if (!empty($result['error'])) {
            return ['done' => false, 'error' => $result['error']];
        }

        $toc = $result['toc'];

        // Tìm meta để lấy old domain + tạo danh sách file
        $meta      = [];
        $sql_blocks = [];
        $file_manifest = [];

        foreach ($toc as $block) {
            $h = $block['header'];
            if ($h['type'] === 'meta') {
                $raw = Foxblock_Backup_FileHandler::extract_block($archive_path, $block['data_offset'], $h['size']);
                $meta = json_decode($raw, true) ?: [];
            } elseif ($h['type'] === 'sql_chunk') {
                $sql_blocks[] = $block;
            } elseif ($h['type'] === 'file') {
                $file_manifest[] = $block;
            }
        }

        $old_domain = isset($meta['site_url']) ? $meta['site_url'] : '';

        $state['toc']           = $toc;
        $state['meta']          = $meta;
        $state['old_domain']    = $old_domain;
        $state['new_domain']    = get_site_url();
        $state['sql_blocks']    = $sql_blocks;
        $state['sql_block_idx'] = 0;
        $state['sql_line_offset'] = 0;
        $state['file_manifest'] = $file_manifest;
        $state['file_offset']   = 0;
        $state['step']          = 'import_db';
        $state['progress']      = 5;

        Foxblock_Backup_StateManager::save_restore_state($state);

        return [
            'step'      => 'init',
            'progress'  => 5,
            'next_step' => 'import_db',
            'done'      => false,
            'message'   => 'Đã đọc file backup. DB: ' . count($sql_blocks) . ' chunk(s), Files: ' . count($file_manifest),
        ];
    }

    /**
     * Chạy bước tiếp theo của restore
     */
    public static function run_step()
    {
        $state = Foxblock_Backup_StateManager::get_restore_state();
        if (!$state) {
            return ['done' => false, 'error' => 'Không có quá trình phục hồi nào đang chạy.'];
        }

        $start_time = microtime(true);

        switch ($state['step']) {
            case 'import_db':
                return self::step_import_db($state, $start_time);

            case 'replace_domain':
                return self::step_replace_domain($state, $start_time);

            case 'restore_files':
                return self::step_restore_files($state, $start_time);

            case 'cleanup':
                return self::step_cleanup($state, $start_time);

            case 'done':
                return ['step' => 'done', 'progress' => 100, 'done' => true, 'message' => 'Phục hồi hoàn tất!'];

            default:
                Foxblock_Backup_StateManager::clear_restore_state();
                return ['done' => false, 'error' => 'Restore state không hợp lệ'];
        }
    }

    /**
     * Hủy restore
     */
    public static function cancel()
    {
        Foxblock_Backup_StateManager::clear_restore_state();
    }

    // ── Step 1: Import DB ─────────────────────────────────────────────────────

    private static function step_import_db(&$state, $start_time)
    {
        global $wpdb;

        $archive_path = $state['archive_path'];
        $sql_blocks   = $state['sql_blocks'];
        $block_idx    = $state['sql_block_idx'];
        $total_blocks = count($sql_blocks);

        if ($block_idx === 0 && $state['sql_line_offset'] === 0) {
            // DROP toàn bộ bảng cũ trước khi import
            $tables = $wpdb->get_col('SHOW TABLES');
            $wpdb->query('SET FOREIGN_KEY_CHECKS=0');
            foreach ($tables as $t) {
                $wpdb->query("DROP TABLE IF EXISTS `" . esc_sql($t) . "`");
            }
            $wpdb->query('SET FOREIGN_KEY_CHECKS=1');
        }

        $stmt_count = 0;
        $error_msg  = '';

        while ($block_idx < $total_blocks) {
            $block = $sql_blocks[$block_idx];

            // Đọc SQL chunk từ archive
            $sql_chunk = Foxblock_Backup_FileHandler::extract_block(
                $archive_path,
                $block['data_offset'],
                $block['header']['size']
            );

            if ($sql_chunk === false) {
                $state['sql_block_idx'] = $block_idx + 1;
                $state['sql_line_offset'] = 0;
                Foxblock_Backup_StateManager::save_restore_state($state);
                continue;
            }

            // Parse và thực thi SQL line-by-line mà không làm hỏng khoảng trắng bên trong chuỗi 
            // bằng cách giữ nguyên ký tự xuống dòng (split sau \n)
            $lines      = preg_split('/(?<=\n)/', $sql_chunk);
            $line_offset = $state['sql_line_offset'];
            $buffer     = '';

            for ($i = $line_offset; $i < count($lines); $i++) {
                $line = $lines[$i];
                $trimmed = trim($line);

                // Bỏ qua comment và dòng rỗng hoàn toàn
                if ($trimmed === '' || strpos($trimmed, '--') === 0 || strpos($trimmed, '#') === 0) continue;

                $buffer .= $line;

                // Kiểm tra xem dòng cuối cùng có kết thúc bằng ; hay không (bỏ qua khoảng trắng dư thừa)
                if (substr(rtrim($buffer), -1) === ';') {
                    $sql = trim($buffer);
                    $buffer = '';

                    if (!empty($sql)) {
                        $res = $wpdb->query($sql);
                        if ($res === false && !empty($wpdb->last_error)) {
                            // Log lỗi rớt data để dễ bề fix
                            error_log("Foxblock Restore Error: " . $wpdb->last_error . " | SQL: " . substr($sql, 0, 300));
                        }
                        $stmt_count++;
                    }
                }

                // Timeout + chunk limit
                if ($stmt_count >= self::SQL_CHUNK || (microtime(true) - $start_time) >= self::TIME_LIMIT) {
                    $state['sql_block_idx']   = $block_idx;
                    $state['sql_line_offset'] = $i + 1;
                    $progress = 5 + intval(($block_idx / max(1, $total_blocks)) * 35);
                    $state['progress'] = $progress;
                    Foxblock_Backup_StateManager::save_restore_state($state);
                    return [
                        'step'      => 'import_db',
                        'progress'  => $progress,
                        'next_step' => 'import_db',
                        'done'      => false,
                        'message'   => "Đang import database: chunk {$block_idx}/{$total_blocks}, {$stmt_count} câu lệnh",
                    ];
                }
            }

            // Hết block này, sang block tiếp theo
            $block_idx++;
            $state['sql_line_offset'] = 0;
        }

        // Hoàn tất import DB
        $state['sql_block_idx']   = $total_blocks;
        $state['sql_line_offset'] = 0;
        $state['step']            = 'replace_domain';
        $state['progress']        = 45;
        Foxblock_Backup_StateManager::save_restore_state($state);

        return [
            'step'      => 'import_db',
            'progress'  => 45,
            'next_step' => 'replace_domain',
            'done'      => false,
            'message'   => 'Import database hoàn tất. Đang thay thế domain...',
        ];
    }

    // ── Step 2: Replace domain (serialization-safe) ───────────────────────────

    private static function step_replace_domain(&$state, $start_time)
    {
        global $wpdb;

        $old_domain = rtrim($state['old_domain'], '/');
        $new_domain = rtrim($state['new_domain'], '/');

        if (empty($old_domain) || $old_domain === $new_domain) {
            $state['step']    = 'restore_files';
            $state['progress'] = 50;
            Foxblock_Backup_StateManager::save_restore_state($state);
            return [
                'step'      => 'replace_domain',
                'progress'  => 50,
                'next_step' => 'restore_files',
                'done'      => false,
                'message'   => 'Domain giống nhau, bỏ qua replace. Đang phục hồi file...',
            ];
        }

        $tables_to_fix = [
            $wpdb->options,
            $wpdb->posts,
            $wpdb->postmeta,
            $wpdb->usermeta,
            $wpdb->comments,
            $wpdb->commentmeta,
        ];

        $table_columns = [
            $wpdb->options   => ['option_value'],
            $wpdb->posts     => ['post_content', 'post_excerpt', 'guid', 'post_content_filtered'],
            $wpdb->postmeta  => ['meta_value'],
            $wpdb->usermeta  => ['meta_value'],
            $wpdb->comments  => ['comment_content', 'comment_author_url'],
            $wpdb->commentmeta => ['meta_value'],
        ];

        if (!isset($state['replace_table_idx'])) {
            $state['replace_table_idx'] = 0;
            $state['replace_col_idx']   = 0;
            $state['replace_last_pk']   = 0;
            $state['replace_offset']    = 0; // fallback
            $state['replace_replaced']  = 0;
        }

        $t_idx = $state['replace_table_idx'];
        $c_idx = $state['replace_col_idx'];
        $last_pk = $state['replace_last_pk'];
        $offset = $state['replace_offset'];
        $replaced = $state['replace_replaced'];
        
        $chunk_size = 500;

        while ($t_idx < count($tables_to_fix)) {
            $table = $tables_to_fix[$t_idx];

            if (!isset($table_columns[$table])) {
                $t_idx++;
                continue;
            }

            $columns = $table_columns[$table];

            while ($c_idx < count($columns)) {
                $col = $columns[$c_idx];

                // Kiểm tra bảng/cột tồn tại
                $col_exists = $wpdb->get_var(
                    $wpdb->prepare(
                        "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s",
                        DB_NAME, $table, $col
                    )
                );
                
                if (!$col_exists) {
                    $c_idx++;
                    $last_pk = 0;
                    $offset = 0;
                    continue;
                }

                $pk_cols = self::get_primary_key($table);
                $pk = !empty($pk_cols) ? $pk_cols[0] : '';
                $rows = [];

                if (!empty($pk)) {
                    $rows = $wpdb->get_results(
                        $wpdb->prepare(
                            "SELECT * FROM `{$table}` WHERE `{$col}` LIKE %s AND `{$pk}` > %d ORDER BY `{$pk}` ASC LIMIT %d",
                            '%' . $wpdb->esc_like($old_domain) . '%',
                            $last_pk,
                            $chunk_size
                        ),
                        ARRAY_A
                    );
                    
                    if (!empty($rows)) {
                        $end_row = end($rows);
                        $last_pk = $end_row[$pk];
                    }
                } else {
                    $rows = $wpdb->get_results(
                        $wpdb->prepare(
                            "SELECT * FROM `{$table}` WHERE `{$col}` LIKE %s LIMIT %d OFFSET %d",
                            '%' . $wpdb->esc_like($old_domain) . '%',
                            $chunk_size,
                            $offset
                        ),
                        ARRAY_A
                    );
                    $offset += $chunk_size;
                }

                if (empty($rows)) {
                    $c_idx++;
                    $last_pk = 0;
                    $offset = 0;
                    continue;
                }

                foreach ($rows as $row) {
                    $old_val  = $row[$col];
                    $new_val  = self::safe_replace($old_domain, $new_domain, $old_val);

                    if ($new_val !== $old_val) {
                        $where   = [];
                        $where_vals = [];
                        foreach ($pk_cols as $key_col) {
                            $where[] = "`{$key_col}` = %s";
                            $where_vals[] = $row[$key_col];
                        }
                        
                        if (!empty($where)) {
                            $wpdb->query(
                                $wpdb->prepare(
                                    "UPDATE `{$table}` SET `{$col}` = %s WHERE " . implode(' AND ', $where),
                                    array_merge([$new_val], $where_vals)
                                )
                            );
                            $replaced++;
                        }
                    }
                }

                if ((microtime(true) - $start_time) >= self::TIME_LIMIT) {
                    $state['replace_table_idx'] = $t_idx;
                    $state['replace_col_idx']   = $c_idx;
                    $state['replace_last_pk']   = $last_pk;
                    $state['replace_offset']    = $offset;
                    $state['replace_replaced']  = $replaced;
                    
                    $progress = 45 + intval((($t_idx + 1) / count($tables_to_fix)) * 5);
                    $state['progress'] = $progress;
                    Foxblock_Backup_StateManager::save_restore_state($state);
                    
                    return [
                        'step'      => 'replace_domain',
                        'progress'  => $progress,
                        'next_step' => 'replace_domain',
                        'done'      => false,
                        'message'   => "Đang thay thế domain ở bảng {$table} ({$replaced} mục)...",
                    ];
                }
            }

            $t_idx++;
            $c_idx = 0;
            $last_pk = 0;
            $offset = 0;
        }

        // Cập nhật siteurl & home
        update_option('siteurl', $new_domain);
        update_option('home', $new_domain);

        $state['step']     = 'restore_files';
        $state['progress'] = 50;
        Foxblock_Backup_StateManager::save_restore_state($state);

        return [
            'step'      => 'replace_domain',
            'progress'  => 50,
            'next_step' => 'restore_files',
            'done'      => false,
            'message'   => "Domain đã được thay thế: {$old_domain} → {$new_domain}. Đã sửa {$replaced} mục.",
        ];
    }

    /**
     * Thay thế domain trong string, xử lý đúng serialized data
     * Không dùng str_replace đơn giản vì sẽ hỏng length trong serialized strings
     */
    public static function safe_replace($old, $new, $data)
    {
        if (is_array($data)) {
            return array_map(function($v) use ($old, $new) {
                return self::safe_replace($old, $new, $v);
            }, $data);
        }

        if (is_object($data)) {
            $cloned = clone $data;
            foreach (get_object_vars($cloned) as $key => $value) {
                $cloned->$key = self::safe_replace($old, $new, $value);
            }
            return $cloned;
        }

        if (!is_string($data)) return $data;

        // Thử unserialize
        if (self::is_serialized($data)) {
            $unserialized = @unserialize($data);
            if ($unserialized !== false || $data === 'b:0;') {
                $replaced = self::safe_replace($old, $new, $unserialized);
                return serialize($replaced);
            }
            // Nếu unserialize thất bại, dữ liệu đã bị hỏng từ trước (do backup cũ lỗi).
            // Tuyệt đối KHÔNG chạy str_replace lên nó vì sẽ làm độ dài chuỗi sai lệch thêm.
            return $data;
        }

        // Thay thế thông thường
        $data = str_replace($old, $new, $data);

        // Thay thế domain trong JSON (ví dụ Elementor sử dụng http:\/\/domain.com\/...)
        $old_json = str_replace('/', '\/', $old);
        $new_json = str_replace('/', '\/', $new);
        $data = str_replace($old_json, $new_json, $data);

        // Thay thế domain dạng URL Encode (ví dụ http%3A%2F%2Fdomain.com%2F...)
        $old_url = urlencode($old);
        $new_url = urlencode($new);
        $data = str_replace($old_url, $new_url, $data);

        return $data;
    }

    /**
     * Kiểm tra xem string có phải serialized data không
     */
    private static function is_serialized($data)
    {
        if (!is_string($data)) return false;
        $data = trim($data);
        if ($data === 'N;') return true;
        if (strlen($data) < 4) return false;
        if ($data[1] !== ':') return false;
        $lastc = substr($data, -1);
        if ($lastc !== ';' && $lastc !== '}') return false;
        $token = $data[0];
        switch ($token) {
            case 's':
                if ($data[strlen($data) - 2] !== '"') return false;
            case 'a':
            case 'O':
            case 'C':
                return (bool) preg_match("/^{$token}:[0-9]+:/s", $data);
            case 'b':
            case 'i':
            case 'd':
                return (bool) preg_match("/^{$token}:[0-9.E-]+;$/", $data);
        }
        return false;
    }

    /**
     * Lấy primary key columns của bảng
     */
    private static function get_primary_key($table)
    {
        global $wpdb;
        $keys = $wpdb->get_results("SHOW KEYS FROM `{$table}` WHERE Key_name = 'PRIMARY'", ARRAY_A);
        $pks  = [];
        foreach ($keys as $k) {
            $pks[] = $k['Column_name'];
        }
        // Fallback: dùng cột đầu tiên
        if (empty($pks)) {
            $cols = $wpdb->get_col("SHOW COLUMNS FROM `{$table}`");
            $pks  = [$cols[0]];
        }
        return $pks;
    }

    // ── Step 3: Restore files ─────────────────────────────────────────────────

    private static function step_restore_files(&$state, $start_time)
    {
        $archive_path  = $state['archive_path'];
        $file_manifest = $state['file_manifest'];
        $file_offset   = isset($state['file_offset']) ? $state['file_offset'] : 0;
        $file_resume_offset = isset($state['file_resume_offset']) ? $state['file_resume_offset'] : 0;
        $total         = count($file_manifest);
        $processed     = 0;
        $current_file  = '';
        
        $temp_restore_dir = WP_CONTENT_DIR . '/foxblock_temp/restore_data';

        while ($file_offset < $total) {
            $block       = $file_manifest[$file_offset];
            $rel_path    = $block['header']['path'];  // e.g "wp-content/uploads/2024/file.jpg"
            $size        = $block['header']['size'];
            $data_offset = $block['data_offset'];

            // Bỏ qua việc ghi đè trực tiếp lên chính plugin Foxblock (Trích xuất sẽ loại bỏ Foxblock)
            // Việc clone Foxblock để giữ mạng sống sẽ được thực hiện khi swap_directories
            if (strpos(wp_normalize_path($rel_path), 'wp-content/plugins/Foxblock/') === 0 || strpos(wp_normalize_path($rel_path), 'wp-content/plugins/foxblock/') === 0) {
                $file_offset++;
                $file_resume_offset = 0;
                $processed++;
                continue;
            }

            // Tính đường dẫn tạm thời tách biệt hoàn toàn với mã nguồn đang chạy (AI1WM Swap Pattern)
            $sub_path = preg_replace('#^wp-content/#i', '', $rel_path);
            $abs_path = $temp_restore_dir . '/' . ltrim($sub_path, '/');

            // Trích xuất và ghi file tạm có hỗ trợ dừng khi hết thời gian
            $result = Foxblock_Backup_FileHandler::extract_block(
                $archive_path, 
                $data_offset, 
                $size, 
                $abs_path,
                $start_time,
                self::TIME_LIMIT,
                $file_resume_offset
            );

            if ($result === false) {
                // Lỗi ghi file, bỏ qua và lấy file tiếp theo.
                $current_file = $rel_path;
                $file_offset++;
                $file_resume_offset = 0;
                $processed++;
                continue;
            }

            if (isset($result['done']) && $result['done'] === false) {
                // Hết giờ nhưng file chưa giải nén xong
                $file_resume_offset = $result['extracted'];
                $current_file = $rel_path . ' (' . round(($file_resume_offset / max(1, $size)) * 100) . '%)';
                break; // Thoát vòng lặp để tiếp tục request lần sau
            }

            // File đã giải nén hoàn toàn vào thư mục tạm
            $current_file = $rel_path;
            $file_offset++;
            $file_resume_offset = 0;
            $processed++;

            if ($processed >= self::FILES_CHUNK || (microtime(true) - $start_time) >= self::TIME_LIMIT) {
                break;
            }
        }

        $state['file_offset'] = $file_offset;
        $state['file_resume_offset'] = $file_resume_offset;

        if ($file_offset >= $total) {
            // Dọn dẹp file upload tạm
            if (!empty($state['archive_path']) && strpos($state['archive_path'], 'foxblock_temp') !== false) {
                @unlink($state['archive_path']);
            }

            // HOÁN ĐỔI THƯ MỤC - Swap wp-content dirs như All-in-One WP Migration
            self::swap_directories();

            // Xóa thư mục tạm thời sau khi swap
            Foxblock_Backup_FileHandler::delete_dir($temp_restore_dir);

            $state['step']     = 'cleanup';
            $state['progress'] = 98;
            Foxblock_Backup_StateManager::save_restore_state($state);

            // Flush rewrite rules
            flush_rewrite_rules();

            return [
                'step'      => 'cleanup',
                'progress'  => 98,
                'next_step' => 'cleanup',
                'done'      => false,
                'message'   => "Đang dọn dẹp các thư mục lưu tạm...",
            ];
        }

        $progress = 50 + intval(($file_offset / max(1, $total)) * 48);
        $state['progress'] = $progress;
        $state['current_file'] = $current_file;
        Foxblock_Backup_StateManager::save_restore_state($state);

        return [
            'step'      => 'restore_files',
            'progress'  => $progress,
            'next_step' => 'restore_files',
            'done'      => false,
            'message'   => "Đang phục hồi file: {$file_offset}/{$total} — {$current_file}",
        ];
    }

    /**
     * Swap directories (Như All-in-One WP Migration làm để tráo thư mục plugins/themes sau khi extract)
     * Kỹ thuật này ngăn chặn các plugin đang bị giải nén dang dở gây sập WordPress
     */
    private static function swap_directories()
    {
        $wp_content = rtrim(WP_CONTENT_DIR, '/\\');
        $restore_data = $wp_content . '/foxblock_temp/restore_data';
        
        $dirs_to_swap = ['plugins', 'themes', 'uploads'];
        foreach ($dirs_to_swap as $dir) {
            $src = $restore_data . '/' . $dir;
            $dst = $wp_content . '/' . $dir;
            $old = $wp_content . '/' . $dir . '-old-' . time();

            if (is_dir($src)) {
                // Trước khi tráo đổi plugins, clone Foxblock sang thư mục mới để nó vẫn sống sót sau swap
                if ($dir === 'plugins' && is_dir($wp_content . '/plugins/Foxblock')) {
                    Foxblock_Backup_FileHandler::copy_dir($wp_content . '/plugins/Foxblock', $src . '/Foxblock');
                }
                
                // Swap (Atomically replace directories)
                if (is_dir($dst)) {
                    if (!@rename($dst, $old)) {
                        // Fallback cho môi trường Windows (thư mục bị khóa do IIS/Apache đang chạy)
                        @mkdir($old, 0777, true);
                        $items = glob($dst . '/*');
                        if ($items) {
                            foreach ($items as $item) {
                                $basename = basename($item);
                                if ($dir === 'plugins' && $basename === 'Foxblock') continue;
                                @rename($item, $old . '/' . $basename);
                            }
                        }
                    }
                }
                
                if (!@rename($src, $dst)) {
                    if (!is_dir($dst)) @mkdir($dst, 0777, true);
                    $src_items = glob($src . '/*');
                    if ($src_items) {
                        foreach ($src_items as $item) {
                            $basename = basename($item);
                            if ($dir === 'plugins' && $basename === 'Foxblock') continue;
                            @rename($item, $dst . '/' . $basename);
                        }
                    }
                }
            }
        }
    }

    // ── Step 4: Cleanup temp files ────────────────────────────────────────────

    private static function step_cleanup(&$state, $start_time)
    {
        $wp_content = rtrim(WP_CONTENT_DIR, '/\\');
        
        $dirs_to_delete = [];
        // Tìm các thư mục có đuôi -old-
        $old_dirs = glob($wp_content . '/*-old-*', GLOB_ONLYDIR);
        if ($old_dirs) {
            $dirs_to_delete = array_merge($dirs_to_delete, $old_dirs);
        }
        
        $temp_dir = $wp_content . '/foxblock_temp';
        if (is_dir($temp_dir)) {
            $dirs_to_delete[] = $temp_dir;
        }

        $all_clean = true;
        foreach ($dirs_to_delete as $dir) {
            if (is_dir($dir)) {
                Foxblock_Backup_FileHandler::delete_dir($dir);
                if (is_dir($dir)) {
                    $all_clean = false; // Vẫn còn sót (hoặc bị timeout giữa chừng)
                }
            }
            if ((microtime(true) - $start_time) >= self::TIME_LIMIT) {
                break;
            }
        }

        $retries = isset($state['cleanup_retries']) ? $state['cleanup_retries'] : 0;
        
        if (!$all_clean && $retries < 5) {
            $state['cleanup_retries'] = $retries + 1;
            Foxblock_Backup_StateManager::save_restore_state($state);
            return [
                'step'      => 'cleanup',
                'progress'  => 98,
                'next_step' => 'cleanup',
                'done'      => false,
                'message'   => 'Tiếp tục dọn dẹp dữ liệu rác...',
            ];
        }

        // Hoàn tất thực sự
        $state['step']     = 'done';
        $state['progress'] = 100;
        Foxblock_Backup_StateManager::save_restore_state($state);

        return [
            'step'      => 'done',
            'progress'  => 100,
            'next_step' => 'done',
            'done'      => true,
            'message'   => 'Phục hồi và dọn dẹp hoàn tất thành công!',
        ];
    }
}

// Compatibility shim: redirect clear() calls
class Foxblock_Restore_StateManager_Compat
{
    public static function clear()
    {
        Foxblock_Backup_StateManager::clear_restore_state();
    }
}
