# Backup Progress System Architecture

## Current System (Before Refactoring)

The existing progress system uses status codes directly as progress percentages. When package status changes from STATUS_DBSTART (20) to STATUS_DBDONE (39), the progress interpolates between these values based on row count. The `setProgressPercent()` method is called during database dump and archive creation to store intermediate progress values in the `progressPercent` property.

The `getStatusProgress()` method in DupPackage returns different values depending on status:
- Database/Archive phases: returns stored `progressPercent` value (interpolated between status codes)
- Storage phase: calculates percentage based on completed uploads vs total uploads
- Other phases: returns 0

Progress messages are only provided during storage transfer phase via `getActionText()`. Other phases show no descriptive text, only the numeric percentage derived from status codes.

This approach creates uneven progress distribution (database uses 20-39 range while archive uses 40-65) and requires database updates just to persist progress values.

## New System Overview

The refactored progress system tracks package creation through independent phases. Each phase reports its own 0-100% completion percentage along with a descriptive message indicating which operation is currently running. Progress is calculated at runtime from build state data rather than being stored.

## Core Method

The `getProgress()` method in `AbstractPackage` returns progress information:

```php
[
    'percent' => 60.0,                    // float: 0-100% completion of current phase
    'message' => 'Exporting database',    // string: localized description of current operation
    'phase' => 'database'                 // string: machine-readable phase identifier
]
```

The method examines the package status code to determine the active phase, retrieves tracking data from the appropriate build progress object, and calculates that phase's completion percentage from 0 to 100.

## Phase Progression

Backup creation proceeds through four phases, each with independent 0-100% progress tracking:

| Phase | Status Codes | Progress Calculation |
|-------|--------------|----------------------|
| Initialization | STATUS_PRE_PROCESS to STATUS_START | Fixed at 0% (instant phase) |
| Database Export | STATUS_DBSTART to STATUS_DBDONE | Rows dumped / total rows |
| Archive Creation | STATUS_ARCSTART to STATUS_ARCDONE | Files processed / total files |
| Storage Transfer | STATUS_STORAGE_PROCESSING | Bytes uploaded / archive size per storage |

Each phase starts at 0% when it begins and reaches 100% when complete. The next phase then starts at 0% again.

## Data Sources

**Initialization**: This phase is nearly instantaneous. Progress remains fixed at 0% until database export begins. Status codes were originally designed as percentages (STATUS_PRE_PROCESS=0, STATUS_COMPLETE=100).

**Database Export**: Row counters in `db_build_progress->countCheckData` track dumped rows against the total count. Progress percentage = (countCheckData['countTotal'] / countCheckData['impreciseTotalRows']) × 100.

**Archive Creation**: File index in `build_progress->next_archive_file_index` tracks files already processed (starts at 0, incremented per file). Total file count is in `Archive->FileCount` (populated during scanning). Progress percentage = (next_archive_file_index / Archive->FileCount) × 100.

**Storage Transfer**: Individual `UploadInfo` objects in `upload_infos` array maintain their own `progress` property (0-100). Current active storage is found by iterating `upload_infos` where `hasStarted() == true && hasCompleted() == false`. Storage position "[Storage X of Y]" uses array index+1 and count.

## Storage Transfer with Multiple Destinations

When multiple storages are configured, each storage upload is tracked separately. The current active storage displays its individual 0-100% progress.

Progress messages include storage position in the queue:
- "Upload Google Drive 45% [Storage 1 of 3]"
- "Upload FTP 82% [Storage 2 of 3]"
- "Upload Dropbox 15% [Storage 3 of 3]"

Each storage shows its own upload completion percentage, not a partitioned range. Storage 1 goes from 0-100%, then Storage 2 goes from 0-100%, and so on. Storage name is retrieved via `$uploadInfo->getStorage()->getName()`.

## Message Format

Progress messages identify the current operation and provide context:

- **Initialization**: "Initializing backup"
- **Database**: "Exporting database"
- **Archive**: "Creating archive"
- **Storage**: "Upload [StorageName] [Percent]% [Storage X of Y]"

Messages are localized through WordPress translation functions. Storage messages dynamically insert the storage provider name and queue position.

## Implementation

### Trait Organization

Progress tracking logic is encapsulated in `src/Package/TraitPackageProgress.php` to separate concerns and improve maintainability. The trait is used exclusively by `AbstractPackage`.

### Core Progress Method

`getProgress()` validates the calling context and switches on status code ranges to delegate to phase-specific protected methods:

```php
public function getProgress(): array
{
    if (!$this instanceof AbstractPackage) {
        throw new Exception('This method can only be called on an instance of AbstractPackage');
    }

    $status = $this->getStatus();

    if ($status >= self::STATUS_PRE_PROCESS && $status < self::STATUS_DBSTART) {
        return $this->getInitProgress();
    }
    if ($status >= self::STATUS_DBSTART && $status < self::STATUS_ARCSTART) {
        return $this->getDatabaseProgress();
    }
    if ($status >= self::STATUS_ARCSTART && $status < self::STATUS_STORAGE_PROCESSING) {
        return $this->getArchiveProgress();
    }
    if ($status == self::STATUS_STORAGE_PROCESSING) {
        return $this->getStorageProgress();
    }
    // ...handle complete/error states
}
```

### Phase-Specific Methods

Each protected helper method:
1. Retrieves tracking data from build state objects
2. Calculates current phase completion percentage (0-100)
3. Constructs localized message
4. Returns array with percent, message, and phase identifier

Example patterns for each phase:

```php
protected function getInitProgress(): array
{
    return ['percent' => 0.0, 'message' => __('Initializing backup', 'duplicator-pro'), 'phase' => 'init'];
}

protected function getDatabaseProgress(): array
{
    $totalRows = $this->db_build_progress->countCheckData['impreciseTotalRows'] ?? 0;
    $dumpedRows = $this->db_build_progress->countCheckData['countTotal'] ?? 0;
    $percent = $totalRows > 0 ? ($dumpedRows / $totalRows) * 100.0 : 0.0;
    return ['percent' => $percent, 'message' => __('Exporting database', 'duplicator-pro'), 'phase' => 'database'];
}

protected function getArchiveProgress(): array
{
    $totalFiles = $this->Archive->FileCount;
    $processedFiles = $this->build_progress->next_archive_file_index;
    $percent = $totalFiles > 0 ? ($processedFiles / $totalFiles) * 100.0 : 0.0;
    return ['percent' => $percent, 'message' => __('Creating archive', 'duplicator-pro'), 'phase' => 'archive'];
}

protected function getStorageProgress(): array
{
    // Find active: hasStarted() && !hasCompleted()
    // Use: $uploadInfo->progress, $uploadInfo->getStorage()->getName()
    // Format: "Upload [Name] [%]% [Storage X of Y]"
}
```

### Frontend Integration

The AJAX endpoint `duplicator_pro_get_package_statii` calls `getProgress()` and includes both values in the response:

```php
$progress = $package->getProgress();
$status['status_progress'] = $progress['percent'];      // 0-100 for current phase
$status['status_progress_text'] = $progress['message']; // Phase description
```

JavaScript displays the percentage and message separately:
- During build phases: shows percentage only
- During storage phase: shows both message and percentage

## Runtime Calculation Benefits

Progress is computed on demand from existing build state rather than stored as a separate value. This ensures:

- Progress always reflects actual build state accurately
- No database writes needed just to update progress display
- Logic changes apply immediately without migrating stored data
- Single source of truth from status code and tracking counters

The deprecated `progressPercent` property is no longer used or updated.

## Modified Components

**TraitPackageProgress.php** (new file):
- Implements `getProgress()` method with instanceof validation and phase delegation logic
- Implements four protected phase methods: `getInitProgress()`, `getDatabaseProgress()`, `getArchiveProgress()`, `getStorageProgress()`

**AbstractPackage.php**:
- Add `use TraitPackageProgress;`
- Deprecate `setProgressPercent()` to no-op
- Remove `setProgressPercent()` call from `setStatus()` method

**DupPackage.php**:
- Replace `getStatusProgress()` logic with simple `getProgress()['percent']` call
- Add `getStatusProgressText()` method returning `getProgress()['message']`

**ServicesPackage.php**:
- Update `getPackageStatusInfo()` to call `getProgress()` once
- Set both `status_progress` and `status_progress_text` from result
- Remove conditional logic for storage-specific text

**DatabasePkg.php, PackageArchiveZip.php, PackageDupArchive.php**:
- Remove all `setProgressPercent()` calls (no longer needed)
