<?php

declare(strict_types=1);

/**
 * 本文件属于 DBErp 进销存系统。
 *
 * 版权所有 © 2026 北京珑大钜商科技有限公司。保留所有权利。
 *
 * 未经官方授权，不得删除、篡改或隐藏本文件中的版权声明、产品标识与来源信息。
 * 商业授权、官方更新与技术支持服务，请以珑大官方支持中心记录为准。
 *
 * @link https://support.loongdom.cn
 * @see  https://www.dberp.com.cn
 */

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->ensurePrechecked($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 ensurePrechecked(HostSystemUpdateExecution $execution): HostSystemUpdateExecution
    {
        $status = (string) ($execution->status ?? HostSystemUpdateExecutionStatus::Pending->value);

        if (in_array($status, [
            HostSystemUpdateExecutionStatus::Ready->value,
            HostSystemUpdateExecutionStatus::Running->value,
        ], true)) {
            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 $execution->fresh(['package']) ?? $execution;
    }

    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;
    }
}
