<?php

declare(strict_types=1);

namespace App\Services\SupportCenter;

use App\Enums\SupportCenter\HostSystemUpdateExecutionStatus;
use App\Enums\SupportCenter\HostSystemUpdateExecutionStep;
use App\Exceptions\SupportCenter\HostSystemUpdateRecoveryException;
use App\Models\HostSystemUpdateExecution;

/**
 * 更新失败回滚编排（文件/env 恢复，不自动数据库回滚）。
 */
final class HostSystemUpdateRollbackService
{
    public function __construct(
        private readonly HostSystemUpdateExecutionStateMachine $stateMachine,
        private readonly HostSystemUpdateFileRestoreService $fileRestoreService,
        private readonly HostSystemUpdateEnvRestoreService $envRestoreService,
        private readonly HostSystemUpdateDatabaseRollbackBoundary $databaseRollbackBoundary,
        private readonly HostSystemUpdateMaintenanceModeService $maintenanceModeService,
        private readonly HostSystemUpdateExecutionLogger $executionLogger,
        private readonly SupportCenterAuditLogger $auditLogger,
    ) {}

    public function rollback(HostSystemUpdateExecution $execution): HostSystemUpdateExecution
    {
        $this->assertRollbackAllowed($execution);

        $this->auditLogger->logSystemUpdateRollbackStarted($execution);

        $needsManualDatabaseRecovery = $this->databaseRollbackBoundary->requiresManualDatabaseRecovery($execution);

        try {
            $execution = $this->stateMachine->startRollback(
                $execution,
                (string) __('dberp.system_update.recovery.log.rollback_started'),
            );

            if ($needsManualDatabaseRecovery) {
                $this->executionLogger->logTransition(
                    $execution,
                    HostSystemUpdateExecutionStep::Rollback->value,
                    $this->databaseRollbackBoundary->manualDatabaseRecoveryMessage(),
                    ['database_rollback' => 'manual_required'],
                );
            }

            $this->fileRestoreService->restoreFromBackup($execution->fresh(['backups']) ?? $execution);
            $this->envRestoreService->restoreVersionKeys($execution->fresh(['backups']) ?? $execution);

            $execution = $this->stateMachine->completeRollback(
                $execution->fresh(['backups']) ?? $execution,
                (string) __('dberp.system_update.recovery.log.rollback_completed'),
            );

            $this->auditLogger->logSystemUpdateRollbackCompleted($execution);

            return $execution->fresh(['backups']) ?? $execution;
        } catch (HostSystemUpdateRecoveryException $exception) {
            $execution = $this->stateMachine->failRollback(
                $execution->fresh() ?? $execution,
                $exception->errorCode(),
                $exception->userFacingMessage(),
            );

            $this->auditLogger->logSystemUpdateRollbackFailed($execution, $exception);

            throw $exception;
        } finally {
            $this->maintenanceModeService->attemptLeaveMaintenanceOnFailure($execution->fresh() ?? $execution);
        }
    }

    private function assertRollbackAllowed(HostSystemUpdateExecution $execution): void
    {
        $status = (string) ($execution->status ?? '');

        if (! in_array($status, [
            HostSystemUpdateExecutionStatus::RollbackRequired->value,
            HostSystemUpdateExecutionStatus::RollingBack->value,
        ], true)) {
            throw HostSystemUpdateRecoveryException::rollbackNotAllowed($status);
        }
    }
}
