<?php

declare(strict_types=1);

namespace App\Services\SupportCenter;

/**
 * 系统更新重阶段分批进度（会话 JSON 与执行任务工作区双写）。
 */
final readonly class HostSystemUpdateBatchProgress
{
    public const STATUS_IN_PROGRESS = 'in_progress';

    public const STATUS_COMPLETED = 'completed';

    public const SUBSTEP_EXTRACT = 'extract';

    public const SUBSTEP_VERIFY = 'verify';

    public const SUBSTEP_REPLACE = 'replace';

    public const SUBSTEP_DELETE = 'delete';

    public const SUBSTEP_FILES = 'files';

    public const SUBSTEP_ENV = 'env';

    public function __construct(
        public string $phase,
        public string $status,
        public int $total,
        public int $processed,
        public int $cursor,
        public ?string $current,
        public int $batchSize,
        public ?string $subStep = null,
        public ?string $lastError = null,
    ) {}

    public static function percentComplete(self $progress): int
    {
        if ($progress->total <= 0) {
            return $progress->status === self::STATUS_COMPLETED ? 100 : 0;
        }

        return (int) min(100, max(0, (int) round(($progress->processed / $progress->total) * 100)));
    }

    public function isCompleted(): bool
    {
        return $this->status === self::STATUS_COMPLETED;
    }

    /**
     * @param  array<string, mixed>  $payload
     */
    public static function fromArray(array $payload): self
    {
        return new self(
            phase: (string) ($payload['phase'] ?? ''),
            status: (string) ($payload['status'] ?? self::STATUS_IN_PROGRESS),
            total: max(0, (int) ($payload['total'] ?? 0)),
            processed: max(0, (int) ($payload['processed'] ?? 0)),
            cursor: max(0, (int) ($payload['cursor'] ?? 0)),
            current: isset($payload['current']) && is_string($payload['current']) && $payload['current'] !== ''
                ? $payload['current']
                : null,
            batchSize: max(1, (int) ($payload['batch_size'] ?? 1)),
            subStep: isset($payload['sub_step']) && is_string($payload['sub_step']) && $payload['sub_step'] !== ''
                ? $payload['sub_step']
                : null,
            lastError: isset($payload['last_error']) && is_string($payload['last_error']) && $payload['last_error'] !== ''
                ? $payload['last_error']
                : null,
        );
    }

    /**
     * @return array<string, mixed>
     */
    public function toArray(): array
    {
        return [
            'phase' => $this->phase,
            'status' => $this->status,
            'total' => $this->total,
            'processed' => $this->processed,
            'cursor' => $this->cursor,
            'current' => $this->current,
            'batch_size' => $this->batchSize,
            'sub_step' => $this->subStep,
            'last_error' => $this->lastError,
        ];
    }

    public function withCursor(int $cursor, int $processed, ?string $current = null): self
    {
        return new self(
            phase: $this->phase,
            status: $this->status,
            total: $this->total,
            processed: $processed,
            cursor: $cursor,
            current: $current,
            batchSize: $this->batchSize,
            subStep: $this->subStep,
            lastError: $this->lastError,
        );
    }

    public function withSubStep(string $subStep, int $total, int $cursor = 0, int $processed = 0): self
    {
        return new self(
            phase: $this->phase,
            status: self::STATUS_IN_PROGRESS,
            total: $total,
            processed: $processed,
            cursor: $cursor,
            current: null,
            batchSize: $this->batchSize,
            subStep: $subStep,
            lastError: null,
        );
    }

    public function markCompleted(): self
    {
        return new self(
            phase: $this->phase,
            status: self::STATUS_COMPLETED,
            total: $this->total,
            processed: $this->total,
            cursor: $this->total,
            current: null,
            batchSize: $this->batchSize,
            subStep: $this->subStep,
            lastError: null,
        );
    }
}
