<?php
/**
 * BackupManager - Điều phối quá trình backup theo state machine
 *
 * Steps: scan_files → export_db → pack_files → finalize → done
 *
 * Mỗi AJAX request chạy tối đa 8 giây, sau đó lưu state và trả về tiến độ.
 */

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

class Foxblock_BackupManager
{
    const TIME_LIMIT = 8; // giây mỗi request

    /**
     * Bắt đầu backup mới hoặc tiếp tục từ state hiện tại
     * Trả về response JSON-ready array
     */
    public static function run_step()
    {
        $state = Foxblock_Backup_StateManager::get_backup_state();

        // Không có state nào → bắt đầu mới
        if (!$state) {
            $state = Foxblock_Backup_StateManager::init_backup_state();
        }

        $start_time = microtime(true);

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

            case 'export_db':
                return self::step_export_db($state, $start_time);

            case 'pack_files':
                return self::step_pack_files($state, $start_time);

            case 'finalize':
                return self::step_finalize($state, $start_time);

            case 'upload_gdrive':
                return self::step_upload_gdrive($state, $start_time);

            case 'done':
                return [
                    'step'     => 'done',
                    'progress' => 100,
                    'done'     => true,
                    'message'  => 'Backup hoàn tất!',
                    'filename' => basename($state['archive_path']),
                ];

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

    /**
     * Hủy backup đang chạy
     */
    public static function cancel()
    {
        $state = Foxblock_Backup_StateManager::get_backup_state();
        if ($state && !empty($state['temp_dir'])) {
            Foxblock_Backup_SecurityHelper::cleanup_temp_dir($state['temp_dir']);
        }
        Foxblock_Backup_StateManager::clear_backup_state();
    }

    // ── Step 1: Scan files ────────────────────────────────────────────────────

    private static function step_scan_files(&$state, $start_time)
    {
        // Tạo temp dir
        wp_mkdir_p($state['temp_dir']);

        // Scan toàn bộ file (nhanh, không cần chunk)
        $file_list = Foxblock_Backup_FileHandler::scan_files();

        // Lưu danh sách vào state
        // Chỉ lưu path để giảm kích thước, không lưu toàn bộ metadata
        $state['file_list']    = array_map(function($f) { return ['abs' => $f['abs'], 'rel' => $f['rel'], 'size' => $f['size']]; }, $file_list);
        $state['file_offset']  = 0;
        $state['db_tables']    = Foxblock_Backup_DatabaseExporter::get_all_tables();
        $state['db_table_index'] = 0;
        $state['db_row_offset']  = 0;
        $state['step']         = 'export_db';
        $state['progress']     = 5;

        // Khởi tạo file archive tạm
        $archive_tmp = $state['temp_dir'] . '/data.foxblock.tmp';
        $fh = fopen($archive_tmp, 'wb');
        if (!$fh) {
            Foxblock_Backup_StateManager::clear_backup_state();
            return ['done' => false, 'error' => 'Không thể tạo file tạm trong ' . $state['temp_dir']];
        }
        Foxblock_Backup_FileHandler::write_archive_header($fh);
        fclose($fh);

        $state['archive_tmp'] = $archive_tmp;
        Foxblock_Backup_StateManager::save_backup_state($state);

        return [
            'step'      => 'scan_files',
            'progress'  => 5,
            'next_step' => 'export_db',
            'done'      => false,
            'message'   => 'Đã tìm thấy ' . count($file_list) . ' file và ' . count($state['db_tables']) . ' bảng database.',
        ];
    }

    // ── Step 2: Export DB ─────────────────────────────────────────────────────

    private static function step_export_db(&$state, $start_time)
    {
        $archive_tmp     = $state['archive_tmp'];
        $tables          = $state['db_tables'];
        $table_index     = $state['db_table_index'];
        $row_offset      = $state['db_row_offset'];
        $total_tables    = count($tables);
        $sql_buffer      = '';

        // Mở archive để append
        $fh = fopen($archive_tmp, 'ab');
        if (!$fh) return ['done' => false, 'error' => 'Không thể mở file archive tạm'];

        // Ghi header DB nếu chưa ghi (table_index == 0, offset == 0)
        if ($table_index === 0 && $row_offset === 0) {
            $sql_buffer .= Foxblock_Backup_DatabaseExporter::get_dump_header();
        }

        $processed = 0;

        while ($table_index < $total_tables) {
            $table = $tables[$table_index];

            // Ghi CREATE TABLE khi bắt đầu bảng mới
            if ($row_offset === 0) {
                $sql_buffer .= Foxblock_Backup_DatabaseExporter::get_create_table($table);
            }

            // Xuất rows theo chunk
            $result = Foxblock_Backup_DatabaseExporter::export_table_chunk($table, $row_offset);
            $sql_buffer .= $result['sql'];
            $processed  += $result['count'];

            if ($result['done']) {
                $sql_buffer  .= "\n"; // separator giữa các bảng
                $table_index++;
                $row_offset = 0;
            } else {
                $row_offset += $result['count'];
            }

            // Flush buffer vào archive dưới dạng raw SQL (chưa chia block, sẽ gộp lại ở cuối)
            // Tránh bộ nhớ quá lớn: flush khi buffer > 512KB
            if (strlen($sql_buffer) > 524288) {
                $header = ['path' => 'database.sql', 'size' => strlen($sql_buffer), 'type' => 'sql_chunk'];
                fwrite($fh, pack('N', strlen(json_encode($header))));
                fwrite($fh, json_encode($header));
                fwrite($fh, $sql_buffer);
                $sql_buffer = '';
            }

            // Kiểm tra timeout
            if ((microtime(true) - $start_time) >= self::TIME_LIMIT) break;
        }

        // Flush phần còn lại
        if (!empty($sql_buffer)) {
            if ($table_index >= $total_tables) {
                $sql_buffer .= Foxblock_Backup_DatabaseExporter::get_dump_footer();
            }
            $header = ['path' => 'database.sql', 'size' => strlen($sql_buffer), 'type' => 'sql_chunk'];
            fwrite($fh, pack('N', strlen(json_encode($header))));
            fwrite($fh, json_encode($header));
            fwrite($fh, $sql_buffer);
        }

        fclose($fh);

        $state['db_table_index'] = $table_index;
        $state['db_row_offset']  = $row_offset;

        if ($table_index >= $total_tables) {
            // Xong DB, chuyển sang pack files
            $state['step']     = 'pack_files';
            $state['progress'] = 30;
            Foxblock_Backup_StateManager::save_backup_state($state);
            return [
                'step'      => 'export_db',
                'progress'  => 30,
                'next_step' => 'pack_files',
                'done'      => false,
                'message'   => 'Đã xuất xong ' . $total_tables . ' bảng database.',
            ];
        }

        $progress = 5 + intval(($table_index / $total_tables) * 25);
        $state['progress'] = $progress;
        Foxblock_Backup_StateManager::save_backup_state($state);

        return [
            'step'      => 'export_db',
            'progress'  => $progress,
            'next_step' => 'export_db',
            'done'      => false,
            'message'   => "Đang xuất database: bảng {$table_index}/{$total_tables} (" . $tables[$table_index] . ")",
        ];
    }

    // ── Step 3: Pack files ────────────────────────────────────────────────────

    private static function step_pack_files(&$state, $start_time)
    {
        $archive_tmp  = $state['archive_tmp'];
        $file_list    = $state['file_list'];
        $file_offset  = $state['file_offset'];
        $total_files  = count($file_list);
        $chunk_size   = Foxblock_Backup_FileHandler::FILES_CHUNK;

        $fh = fopen($archive_tmp, 'ab');
        if (!$fh) return ['done' => false, 'error' => 'Không thể mở file archive tạm'];

        $processed = 0;
        $current_file = '';

        while ($file_offset < $total_files) {
            $file_info    = $file_list[$file_offset];
            $abs          = $file_info['abs'];
            $rel          = $file_info['rel'];
            $size         = $file_info['size'];
            $current_file = $rel;

            if (!file_exists($abs)) {
                $file_offset++;
                continue;
            }

            // Ghi block vào archive: header + file data
            $header = ['path' => $rel, 'size' => $size, 'type' => 'file'];
            $header_json = json_encode($header, JSON_UNESCAPED_UNICODE);
            fwrite($fh, pack('N', strlen($header_json)));
            fwrite($fh, $header_json);

            $fh_src = fopen($abs, 'rb');
            if ($fh_src) {
                while (!feof($fh_src)) {
                    fwrite($fh, fread($fh_src, 65536));
                }
                fclose($fh_src);
            }

            $file_offset++;
            $processed++;

            // Timeout check
            if ($processed >= $chunk_size || (microtime(true) - $start_time) >= self::TIME_LIMIT) {
                break;
            }
        }

        fclose($fh);

        $state['file_offset'] = $file_offset;

        if ($file_offset >= $total_files) {
            $state['step']     = 'finalize';
            $state['progress'] = 90;
            Foxblock_Backup_StateManager::save_backup_state($state);
            return [
                'step'      => 'pack_files',
                'progress'  => 90,
                'next_step' => 'finalize',
                'done'      => false,
                'message'   => "Đã đóng gói {$total_files} file. Đang hoàn tất...",
            ];
        }

        $progress = 30 + intval(($file_offset / max(1, $total_files)) * 60);
        $state['progress'] = $progress;
        $state['current_file'] = $current_file;
        Foxblock_Backup_StateManager::save_backup_state($state);

        return [
            'step'      => 'pack_files',
            'progress'  => $progress,
            'next_step' => 'pack_files',
            'done'      => false,
            'message'   => "Đang đóng gói file: {$file_offset}/{$total_files} — {$current_file}",
        ];
    }

    // ── Step 4: Finalize ─────────────────────────────────────────────────────

    private static function step_finalize(&$state, $start_time)
    {
        // Ghi meta.json vào cuối archive
        $meta_json = json_encode($state['meta'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
        $fh = fopen($state['archive_tmp'], 'ab');
        if ($fh) {
            $header = ['path' => 'meta.json', 'size' => strlen($meta_json), 'type' => 'meta'];
            $header_enc = json_encode($header);
            fwrite($fh, pack('N', strlen($header_enc)));
            fwrite($fh, $header_enc);
            fwrite($fh, $meta_json);
            fclose($fh);
        }

        // Di chuyển archive sang thư mục backup chính thức
        $backup_dir  = Foxblock_Backup_SecurityHelper::get_backup_dir();
        $final_name  = 'backup-' . date('Y-m-d-H-i') . '.foxblock';
        $final_path  = $backup_dir . '/' . $final_name;

        // Xóa backup cũ nếu vượt giới hạn
        self::enforce_retention_limit($backup_dir);

        if (!rename($state['archive_tmp'], $final_path)) {
            // Thử copy nếu rename không được (cross-device)
            copy($state['archive_tmp'], $final_path);
            unlink($state['archive_tmp']);
        }

        // Dọn dẹp temp dir
        Foxblock_Backup_SecurityHelper::cleanup_temp_dir($state['temp_dir']);

        $state['archive_path'] = $final_path;
        
        $storage = get_option('foxblock_backup_storage', 'local');
        if ($storage === 'gdrive') {
            $state['step'] = 'upload_gdrive';
            $state['progress'] = 90;
            Foxblock_Backup_StateManager::save_backup_state($state);
            return [
                'step'      => 'upload_gdrive',
                'progress'  => 90,
                'done'      => false,
                'next_step' => 'upload_gdrive',
                'message'   => 'Đang tải file lên Google Drive...',
                'filename'  => $final_name
            ];
        }

        $state['step']         = 'done';
        $state['progress']     = 100;
        Foxblock_Backup_StateManager::save_backup_state($state);

        return [
            'step'     => 'finalize',
            'progress' => 100,
            'done'     => true,
            'next_step'=> 'done',
            'message'  => 'Backup hoàn tất! File: ' . $final_name,
            'filename' => $final_name,
            'size'     => file_exists($final_path) ? size_format(filesize($final_path)) : '',
        ];
    }

    private static function step_upload_gdrive(&$state, $start_time)
    {
        $final_path = $state['archive_path'];
        $final_name = basename($final_path);

        $service = new Foxblock_Backup_Google_Drive_Service();
        if (!$service->is_ready()) {
            Foxblock_Backup_StateManager::clear_backup_state();
            return [
                'error' => 'Google Drive chưa được kết nối. Quá trình backup bị hủy.'
            ];
        }

        $folder = get_option('foxblock_gdrive_folder', 'Foxblock Backups');
        $result = $service->upload_file($final_path, $folder);

        if (is_wp_error($result)) {
            Foxblock_Backup_StateManager::clear_backup_state();
            return [
                'error' => 'Lỗi upload lên Google Drive: ' . $result->get_error_message()
            ];
        }
        
        $size_str = file_exists($final_path) ? size_format(filesize($final_path)) : '';

        // Xóa bản local sau khi đã đẩy lên GDrive thành công
        if (file_exists($final_path)) {
            @unlink($final_path);
        }

        $state['step'] = 'done';
        $state['progress'] = 100;
        Foxblock_Backup_StateManager::save_backup_state($state);

        return [
            'step'     => 'upload_gdrive',
            'progress' => 100,
            'done'     => true,
            'next_step'=> 'done',
            'message'  => 'Backup hoàn tất! Đã tải lên Google Drive: ' . $final_name,
            'filename' => $final_name,
            'size'     => $size_str,
        ];
    }

    /**
     * Xóa backup cũ nếu vượt quá số lượng giữ lại
     */
    private static function enforce_retention_limit($backup_dir)
    {
        $limit  = intval(get_option('foxblock_backup_retention', 5));
        if ($limit <= 0) $limit = 5;

        $files = glob($backup_dir . '/*.foxblock');
        if (!$files || count($files) < $limit) return;

        // Sort oldest first
        usort($files, function($a, $b) { return filemtime($a) - filemtime($b); });

        $to_delete = count($files) - $limit + 1; // +1 vì sắp tạo thêm 1
        for ($i = 0; $i < $to_delete; $i++) {
            if (file_exists($files[$i])) unlink($files[$i]);
        }
    }
}
