<?php

declare(strict_types=1);

namespace App\Services\SupportCenter;

use App\Enums\SupportCenter\HostSystemUpdateExecutionStatus;
use App\Enums\SupportCenter\HostSystemUpdateExecutionStep;
use App\Enums\SupportCenter\HostSystemUpdateStepPhase;
use App\Exceptions\SupportCenter\HostSystemUpdateStagingException;
use App\Models\HostSystemUpdateExecution;

/**
 * 编排 staging 解压校验与更新前备份（不替换项目文件、不进入 completed）。
 */
final class HostSystemUpdatePrepareService
{
    public function __construct(
        private readonly HostSystemUpdateExecutionStateMachine $stateMachine,
        private readonly HostSystemUpdateEnvironmentPrecheckService $precheckService,
        private readonly HostSystemUpdateBatchPhaseRunner $batchPhaseRunner,
    ) {}

    public function prepareStagingAndBackup(HostSystemUpdateExecution $execution): HostSystemUpdateExecution
    {
        $execution = $this->runPrecheckPhase($execution);

        return $this->runBackupPhase($this->runExtractStagingPhase($execution));
    }

    public function runPrecheckPhase(HostSystemUpdateExecution $execution): HostSystemUpdateExecution
    {
        return $this->ensureRunning($execution);
    }

    public function runExtractStagingPhase(HostSystemUpdateExecution $execution): HostSystemUpdateExecution
    {
        try {
            $status = (string) ($execution->status ?? '');

            if ($status !== HostSystemUpdateExecutionStatus::Running->value) {
                $execution = $this->stateMachine->transition(
                    $execution->fresh() ?? $execution,
                    HostSystemUpdateExecutionStatus::Running,
                    HostSystemUpdateExecutionStep::ExtractStaging,
                    (string) __('dberp.system_update.staging.log.running_started'),
                );
            }

            $execution = $this->runBatchesUntilComplete($execution, HostSystemUpdateStepPhase::ExtractStaging);

            return $execution->fresh(['package']) ?? $execution;
        } catch (HostSystemUpdateStagingException $exception) {
            $this->stateMachine->fail(
                $execution->fresh() ?? $execution,
                $exception->errorCode(),
                $exception->userFacingMessage(),
            );

            throw $exception;
        }
    }

    public function runBackupPhase(HostSystemUpdateExecution $execution): HostSystemUpdateExecution
    {
        try {
            $execution = $this->runBatchesUntilComplete(
                $execution->fresh() ?? $execution,
                HostSystemUpdateStepPhase::Backup,
            );

            return $execution->fresh(['package', 'backups']) ?? $execution;
        } catch (HostSystemUpdateStagingException $exception) {
            $this->stateMachine->fail(
                $execution->fresh() ?? $execution,
                $exception->errorCode(),
                $exception->userFacingMessage(),
            );

            throw $exception;
        }
    }

    private function ensureRunning(HostSystemUpdateExecution $execution): HostSystemUpdateExecution
    {
        $status = (string) ($execution->status ?? HostSystemUpdateExecutionStatus::Pending->value);

        if ($status === HostSystemUpdateExecutionStatus::Running->value) {
            return $execution;
        }

        if ($status === HostSystemUpdateExecutionStatus::Pending->value) {
            $execution = $this->stateMachine->transition(
                $execution,
                HostSystemUpdateExecutionStatus::Prechecking,
                HostSystemUpdateExecutionStep::Precheck,
                (string) __('dberp.system_update.staging.log.precheck_started'),
            );
        }

        if (($execution->fresh() ?? $execution)->status === HostSystemUpdateExecutionStatus::Prechecking->value) {
            $this->precheckService->run($execution->fresh(['package']) ?? $execution);

            $execution = $this->stateMachine->transition(
                $execution->fresh() ?? $execution,
                HostSystemUpdateExecutionStatus::Ready,
                HostSystemUpdateExecutionStep::Precheck,
                (string) __('dberp.system_update.staging.log.precheck_completed'),
            );
        }

        return $this->stateMachine->transition(
            $execution->fresh() ?? $execution,
            HostSystemUpdateExecutionStatus::Running,
            HostSystemUpdateExecutionStep::ExtractStaging,
            (string) __('dberp.system_update.staging.log.running_started'),
        );
    }

    private function runBatchesUntilComplete(
        HostSystemUpdateExecution $execution,
        HostSystemUpdateStepPhase $phase,
    ): HostSystemUpdateExecution {
        while (! $this->batchPhaseRunner->isPhaseComplete($execution, $phase)) {
            $result = $this->batchPhaseRunner->runNextBatch($execution, $phase);
            $execution = $result->execution ?? $execution;
        }

        $this->batchPhaseRunner->clearPhaseProgress($execution, $phase);

        return $execution;
    }
}
