<?php

declare(strict_types=1);

namespace App\Services\SupportCenter;

use App\Enums\SupportCenter\HostSystemUpdateStepPhase;
use App\Exceptions\SupportCenter\HostSystemUpdateFileExecutionException;
use App\Models\HostSystemUpdateExecution;

/**
 * 编排 staging 文件替换、删除与 vendor 策略（不执行 migrate/cache/版本写入）。
 */
final class HostSystemUpdateApplyService
{
    public function __construct(
        private readonly HostSystemUpdateExecutionStateMachine $stateMachine,
        private readonly HostSystemUpdateBatchPhaseRunner $batchPhaseRunner,
    ) {}

    public function applyStagedChanges(HostSystemUpdateExecution $execution): HostSystemUpdateExecution
    {
        return $this->applyVendorPhase($this->applyFilesPhase($execution));
    }

    public function applyFilesPhase(HostSystemUpdateExecution $execution): HostSystemUpdateExecution
    {
        $partialChangesApplied = false;

        try {
            $execution = $this->runBatchesUntilComplete(
                $execution->fresh(['package', 'backups']) ?? $execution,
                HostSystemUpdateStepPhase::ReplaceFiles,
            );

            return $execution->fresh(['package', 'backups']) ?? $execution;
        } catch (HostSystemUpdateFileExecutionException $exception) {
            $this->handleFileExecutionFailure($execution, $exception, $exception->partialChangesApplied());

            throw $exception;
        }
    }

    public function applyVendorPhase(HostSystemUpdateExecution $execution): HostSystemUpdateExecution
    {
        try {
            $execution = $this->runBatchesUntilComplete(
                $execution->fresh(['package', 'backups']) ?? $execution,
                HostSystemUpdateStepPhase::ReplaceVendor,
            );

            return $execution->fresh(['package', 'backups']) ?? $execution;
        } catch (HostSystemUpdateFileExecutionException $exception) {
            $this->handleFileExecutionFailure($execution, $exception, false);

            throw $exception;
        }
    }

    private function handleFileExecutionFailure(
        HostSystemUpdateExecution $execution,
        HostSystemUpdateFileExecutionException $exception,
        bool $partialChangesApplied,
    ): void {
        $fresh = $execution->fresh() ?? $execution;
        $shouldRollback = $partialChangesApplied || $exception->partialChangesApplied();

        if ($shouldRollback) {
            $this->stateMachine->requireRollback(
                $fresh,
                $exception->errorCode(),
                $exception->userFacingMessage(),
                ['partial_changes_applied' => true],
            );
        } else {
            $this->stateMachine->fail(
                $fresh,
                $exception->errorCode(),
                $exception->userFacingMessage(),
            );
        }

    }

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