<?php

declare(strict_types=1);

namespace App\Services\Onboarding;

use App\Filament\Support\VerificationReportViewer;
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\ManagedEnvironment;
use App\Models\ManagedEnvironmentOnboardingSession;
use App\Services\Tenants\TenantOperabilityService;
use App\Support\Onboarding\OnboardingCheckpoint;
use App\Support\Onboarding\OnboardingLifecycleState;
use App\Support\OperationCatalog;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\ProductTelemetry\ProductTelemetryRecorder;
use App\Support\ProductTelemetry\ProductUsageEventCatalog;
use App\Support\Verification\VerificationReportOverall;

class OnboardingLifecycleService
{
    public function __construct(
        private readonly TenantOperabilityService $tenantOperabilityService,
        private readonly ProductTelemetryRecorder $productTelemetryRecorder,
    ) {}

    public function syncPersistedLifecycle(ManagedEnvironmentOnboardingSession $draft, bool $incrementVersion = false): ManagedEnvironmentOnboardingSession
    {
        $freshDraft = ManagedEnvironmentOnboardingSession::query()->whereKey($draft->getKey())->first();

        if (! $freshDraft instanceof ManagedEnvironmentOnboardingSession) {
            return $draft;
        }

        $changed = $this->applySnapshot($freshDraft, $incrementVersion);

        if ($changed) {
            $freshDraft->save();
            $this->recordCompletedCheckpointTelemetryIfNeeded($freshDraft);
        }

        return $freshDraft->refresh();
    }

    public function applySnapshot(ManagedEnvironmentOnboardingSession $draft, bool $incrementVersion = false): bool
    {
        $snapshot = $this->snapshot($draft);
        $lifecycleState = $draft->lifecycle_state instanceof OnboardingLifecycleState
            ? $draft->lifecycle_state
            : OnboardingLifecycleState::tryFrom((string) $draft->lifecycle_state);
        $currentCheckpoint = $draft->current_checkpoint instanceof OnboardingCheckpoint
            ? $draft->current_checkpoint
            : OnboardingCheckpoint::tryFrom((string) $draft->current_checkpoint);
        $lastCompletedCheckpoint = $draft->last_completed_checkpoint instanceof OnboardingCheckpoint
            ? $draft->last_completed_checkpoint
            : OnboardingCheckpoint::tryFrom((string) $draft->last_completed_checkpoint);

        $changed = false;

        if ($lifecycleState !== $snapshot['lifecycle_state']) {
            $draft->lifecycle_state = $snapshot['lifecycle_state'];
            $changed = true;
        }

        if ($currentCheckpoint !== $snapshot['current_checkpoint']) {
            $draft->current_checkpoint = $snapshot['current_checkpoint'];
            $changed = true;
        }

        if ($lastCompletedCheckpoint !== $snapshot['last_completed_checkpoint']) {
            $draft->last_completed_checkpoint = $snapshot['last_completed_checkpoint'];
            $changed = true;
        }

        if (($draft->reason_code ?? null) !== $snapshot['reason_code']) {
            $draft->reason_code = $snapshot['reason_code'];
            $changed = true;
        }

        if (($draft->blocking_reason_code ?? null) !== $snapshot['blocking_reason_code']) {
            $draft->blocking_reason_code = $snapshot['blocking_reason_code'];
            $changed = true;
        }

        $version = max(1, (int) ($draft->version ?? 1));

        if ((int) ($draft->version ?? 0) !== $version) {
            $draft->version = $version;
            $changed = true;
        }

        if ($changed && $incrementVersion) {
            $draft->version = $version + 1;
        }

        return $changed;
    }

    public function recordCompletedCheckpointTelemetryIfNeeded(ManagedEnvironmentOnboardingSession $draft): void
    {
        if (! $draft->wasChanged('last_completed_checkpoint')) {
            return;
        }

        $checkpoint = $draft->last_completed_checkpoint instanceof OnboardingCheckpoint
            ? $draft->last_completed_checkpoint
            : OnboardingCheckpoint::tryFrom((string) $draft->last_completed_checkpoint);

        if (! $checkpoint instanceof OnboardingCheckpoint) {
            return;
        }

        $workspaceId = (int) ($draft->workspace_id ?? 0);
        $tenantId = (int) ($draft->managed_environment_id ?? 0);
        $userId = (int) ($draft->updated_by_user_id ?? 0);

        if ($workspaceId <= 0 || $tenantId <= 0 || $userId <= 0) {
            return;
        }

        $occurredAt = $draft->updated_at ?? now();

        $this->productTelemetryRecorder->record(
            eventName: ProductUsageEventCatalog::ONBOARDING_CHECKPOINT_COMPLETED,
            workspaceId: $workspaceId,
            tenantId: $tenantId,
            userId: $userId,
            subjectType: 'managed_environment_onboarding_session',
            subjectId: (int) $draft->getKey(),
            metadata: [
                'checkpoint_key' => $checkpoint->value,
                'lifecycle_state' => $draft->lifecycleState()->value,
                'completed_at' => $occurredAt,
            ],
            occurredAt: $occurredAt,
        );
    }

    /**
     * @return array{
     *     lifecycle_state: OnboardingLifecycleState,
     *     current_checkpoint: OnboardingCheckpoint|null,
     *     last_completed_checkpoint: OnboardingCheckpoint|null,
     *     reason_code: string|null,
     *     blocking_reason_code: string|null
     * }
     */
    public function snapshot(ManagedEnvironmentOnboardingSession $draft): array
    {
        $selectedProviderConnectionId = $this->selectedProviderConnectionId($draft);
        $verificationRun = $this->verificationRun($draft);
        $verificationStatus = $this->verificationStatus($draft, $selectedProviderConnectionId, $verificationRun);
        $bootstrapState = $this->bootstrapState($draft, $selectedProviderConnectionId);
        $hasIdentity = $this->hasTenantIdentity($draft);
        $hasProviderConnection = $selectedProviderConnectionId !== null;
        $connectionRecentlyUpdated = $this->connectionRecentlyUpdated($draft);

        $currentCheckpoint = OnboardingCheckpoint::Identify;
        $lastCompletedCheckpoint = null;
        $lifecycleState = OnboardingLifecycleState::Draft;
        $reasonCode = null;
        $blockingReasonCode = null;

        if ($hasIdentity) {
            $currentCheckpoint = OnboardingCheckpoint::ConnectProvider;
            $lastCompletedCheckpoint = OnboardingCheckpoint::Identify;
        }

        if ($hasProviderConnection) {
            $currentCheckpoint = OnboardingCheckpoint::VerifyAccess;
            $lastCompletedCheckpoint = OnboardingCheckpoint::ConnectProvider;
        }

        if ($draft->completed_at !== null) {
            return [
                'lifecycle_state' => OnboardingLifecycleState::Completed,
                'current_checkpoint' => OnboardingCheckpoint::CompleteActivate,
                'last_completed_checkpoint' => OnboardingCheckpoint::CompleteActivate,
                'reason_code' => null,
                'blocking_reason_code' => null,
            ];
        }

        if ($draft->cancelled_at !== null) {
            return [
                'lifecycle_state' => OnboardingLifecycleState::Cancelled,
                'current_checkpoint' => $currentCheckpoint,
                'last_completed_checkpoint' => $lastCompletedCheckpoint,
                'reason_code' => null,
                'blocking_reason_code' => null,
            ];
        }

        if (! $hasIdentity) {
            return [
                'lifecycle_state' => $lifecycleState,
                'current_checkpoint' => $currentCheckpoint,
                'last_completed_checkpoint' => $lastCompletedCheckpoint,
                'reason_code' => null,
                'blocking_reason_code' => null,
            ];
        }

        if (! $hasProviderConnection) {
            return [
                'lifecycle_state' => $lifecycleState,
                'current_checkpoint' => $currentCheckpoint,
                'last_completed_checkpoint' => $lastCompletedCheckpoint,
                'reason_code' => null,
                'blocking_reason_code' => null,
            ];
        }

        if ($verificationRun instanceof OperationRun && $verificationRun->status !== OperationRunStatus::Completed->value) {
            return [
                'lifecycle_state' => OnboardingLifecycleState::Verifying,
                'current_checkpoint' => OnboardingCheckpoint::VerifyAccess,
                'last_completed_checkpoint' => OnboardingCheckpoint::ConnectProvider,
                'reason_code' => null,
                'blocking_reason_code' => null,
            ];
        }

        if ($connectionRecentlyUpdated && ! ($verificationRun instanceof OperationRun)) {
            return [
                'lifecycle_state' => OnboardingLifecycleState::ActionRequired,
                'current_checkpoint' => OnboardingCheckpoint::VerifyAccess,
                'last_completed_checkpoint' => OnboardingCheckpoint::ConnectProvider,
                'reason_code' => 'provider_connection_changed',
                'blocking_reason_code' => 'provider_connection_changed',
            ];
        }

        if ($verificationRun instanceof OperationRun && ! $this->verificationRunMatchesSelectedConnection($verificationRun, $selectedProviderConnectionId)) {
            $staleReason = $connectionRecentlyUpdated
                ? 'provider_connection_changed'
                : 'verification_result_stale';

            return [
                'lifecycle_state' => OnboardingLifecycleState::ActionRequired,
                'current_checkpoint' => OnboardingCheckpoint::VerifyAccess,
                'last_completed_checkpoint' => OnboardingCheckpoint::ConnectProvider,
                'reason_code' => $staleReason,
                'blocking_reason_code' => $staleReason,
            ];
        }

        if ($verificationRun instanceof OperationRun && $verificationStatus === 'blocked') {
            $blockingReasonCode = $this->verificationBlockingReasonCode($verificationRun);

            return [
                'lifecycle_state' => OnboardingLifecycleState::ActionRequired,
                'current_checkpoint' => OnboardingCheckpoint::VerifyAccess,
                'last_completed_checkpoint' => OnboardingCheckpoint::ConnectProvider,
                'reason_code' => $blockingReasonCode,
                'blocking_reason_code' => $blockingReasonCode,
            ];
        }

        if (! $this->verificationCanProceed($draft, $selectedProviderConnectionId, $verificationRun)) {
            return [
                'lifecycle_state' => $lifecycleState,
                'current_checkpoint' => OnboardingCheckpoint::VerifyAccess,
                'last_completed_checkpoint' => OnboardingCheckpoint::ConnectProvider,
                'reason_code' => null,
                'blocking_reason_code' => null,
            ];
        }

        $lastCompletedCheckpoint = OnboardingCheckpoint::VerifyAccess;

        if (! $bootstrapState['has_selected_types']) {
            return [
                'lifecycle_state' => OnboardingLifecycleState::ReadyForActivation,
                'current_checkpoint' => OnboardingCheckpoint::CompleteActivate,
                'last_completed_checkpoint' => $lastCompletedCheckpoint,
                'reason_code' => null,
                'blocking_reason_code' => null,
            ];
        }

        if ($bootstrapState['has_active_runs']) {
            return [
                'lifecycle_state' => OnboardingLifecycleState::Bootstrapping,
                'current_checkpoint' => OnboardingCheckpoint::Bootstrap,
                'last_completed_checkpoint' => $lastCompletedCheckpoint,
                'reason_code' => null,
                'blocking_reason_code' => null,
            ];
        }

        if ($bootstrapState['has_partial_failure']) {
            $reasonCode = 'bootstrap_partial_failure';

            return [
                'lifecycle_state' => OnboardingLifecycleState::ActionRequired,
                'current_checkpoint' => OnboardingCheckpoint::Bootstrap,
                'last_completed_checkpoint' => $lastCompletedCheckpoint,
                'reason_code' => $reasonCode,
                'blocking_reason_code' => $reasonCode,
            ];
        }

        if ($bootstrapState['has_failure']) {
            $reasonCode = 'bootstrap_failed';

            return [
                'lifecycle_state' => OnboardingLifecycleState::ActionRequired,
                'current_checkpoint' => OnboardingCheckpoint::Bootstrap,
                'last_completed_checkpoint' => $lastCompletedCheckpoint,
                'reason_code' => $reasonCode,
                'blocking_reason_code' => $reasonCode,
            ];
        }

        if (! $bootstrapState['all_selected_types_completed']) {
            return [
                'lifecycle_state' => OnboardingLifecycleState::Draft,
                'current_checkpoint' => OnboardingCheckpoint::Bootstrap,
                'last_completed_checkpoint' => $lastCompletedCheckpoint,
                'reason_code' => null,
                'blocking_reason_code' => null,
            ];
        }

        return [
            'lifecycle_state' => OnboardingLifecycleState::ReadyForActivation,
            'current_checkpoint' => OnboardingCheckpoint::CompleteActivate,
            'last_completed_checkpoint' => OnboardingCheckpoint::Bootstrap,
            'reason_code' => null,
            'blocking_reason_code' => null,
        ];
    }

    public function verificationRun(ManagedEnvironmentOnboardingSession $draft): ?OperationRun
    {
        $state = is_array($draft->state) ? $draft->state : [];
        $runId = $this->normalizeInteger($state['verification_operation_run_id'] ?? $state['verification_run_id'] ?? null);

        if ($runId === null) {
            return null;
        }

        $query = OperationRun::query()
            ->whereKey($runId)
            ->where('workspace_id', (int) $draft->workspace_id);

        if ($draft->managed_environment_id !== null) {
            $query->where('managed_environment_id', (int) $draft->managed_environment_id);
        }

        return $query->first();
    }

    public function verificationStatus(
        ManagedEnvironmentOnboardingSession $draft,
        ?int $selectedProviderConnectionId = null,
        ?OperationRun $run = null,
    ): string {
        $selectedProviderConnectionId ??= $this->selectedProviderConnectionId($draft);
        $run ??= $this->verificationRun($draft);

        if (! $run instanceof OperationRun) {
            return 'not_started';
        }

        if (! $this->verificationRunMatchesSelectedConnection($run, $selectedProviderConnectionId)) {
            return 'needs_attention';
        }

        if ($run->status !== OperationRunStatus::Completed->value) {
            return 'in_progress';
        }

        $overall = $this->verificationReportOverall($run);

        return match ($overall) {
            VerificationReportOverall::Blocked->value => 'blocked',
            VerificationReportOverall::NeedsAttention->value => 'needs_attention',
            VerificationReportOverall::Ready->value => 'ready',
            VerificationReportOverall::Running->value => 'in_progress',
            default => $this->verificationStatusFromRunOutcome($run),
        };
    }

    public function verificationCanProceed(
        ManagedEnvironmentOnboardingSession $draft,
        ?int $selectedProviderConnectionId = null,
        ?OperationRun $run = null,
    ): bool {
        $selectedProviderConnectionId ??= $this->selectedProviderConnectionId($draft);
        $run ??= $this->verificationRun($draft);

        if (! $run instanceof OperationRun) {
            return false;
        }

        if ($run->status !== OperationRunStatus::Completed->value) {
            return false;
        }

        if (! $this->verificationRunMatchesSelectedConnection($run, $selectedProviderConnectionId)) {
            return false;
        }

        return in_array($this->verificationStatus($draft, $selectedProviderConnectionId, $run), ['ready', 'needs_attention'], true);
    }

    public function verificationIsBlocked(ManagedEnvironmentOnboardingSession $draft, ?int $selectedProviderConnectionId = null): bool
    {
        return $this->verificationStatus($draft, $selectedProviderConnectionId) === 'blocked';
    }

    /**
     * @return array<int, array{
     *     type: string,
     *     run_id: int|null,
     *     status: string|null,
     *     outcome: string|null,
     *     is_active: bool,
     *     is_failure: bool,
     *     is_partial_failure: bool,
     *     is_completed: bool
     * }>
     */
    public function bootstrapRunSummaries(ManagedEnvironmentOnboardingSession $draft, ?int $selectedProviderConnectionId = null): array
    {
        $selectedProviderConnectionId ??= $this->selectedProviderConnectionId($draft);

        return $this->bootstrapState($draft, $selectedProviderConnectionId)['summaries'];
    }

    public function isReadyForActivation(ManagedEnvironmentOnboardingSession $draft): bool
    {
        return $this->snapshot($draft)['lifecycle_state'] === OnboardingLifecycleState::ReadyForActivation;
    }

    public function hasActiveCheckpoint(ManagedEnvironmentOnboardingSession $draft): bool
    {
        $snapshot = $this->snapshot($draft);

        return in_array($snapshot['lifecycle_state'], [OnboardingLifecycleState::Verifying, OnboardingLifecycleState::Bootstrapping], true);
    }

    public function canResumeDraft(ManagedEnvironmentOnboardingSession $draft): bool
    {
        if (! $draft->isWorkflowResumable()) {
            return false;
        }

        $tenant = $draft->managedEnvironment;

        if (! $tenant instanceof ManagedEnvironment) {
            return true;
        }

        return $this->tenantOperabilityService->canResumeOnboarding($tenant);
    }

    public function syncLinkedTenantAfterCancellation(ManagedEnvironmentOnboardingSession $draft): ?ManagedEnvironment
    {
        $tenant = $draft->managedEnvironment;

        if (! $tenant instanceof ManagedEnvironment) {
            return null;
        }

        if ($tenant->trashed() || $tenant->status !== ManagedEnvironment::STATUS_ONBOARDING || ! $draft->isCancelled()) {
            return null;
        }

        $hasOtherResumableDrafts = ManagedEnvironmentOnboardingSession::query()
            ->where('workspace_id', (int) $draft->workspace_id)
            ->where('managed_environment_id', (int) $tenant->getKey())
            ->whereKeyNot((int) $draft->getKey())
            ->resumable()
            ->exists();

        if ($hasOtherResumableDrafts) {
            return null;
        }

        $tenant->forceFill(['status' => ManagedEnvironment::STATUS_DRAFT])->save();

        return $tenant->fresh();
    }

    private function hasTenantIdentity(ManagedEnvironmentOnboardingSession $draft): bool
    {
        if ($draft->managed_environment_id !== null) {
            return true;
        }

        $state = is_array($draft->state) ? $draft->state : [];
        $entraTenantId = $state['entra_tenant_id'] ?? $draft->entra_tenant_id;

        return is_string($entraTenantId) && trim($entraTenantId) !== '';
    }

    private function selectedProviderConnectionId(ManagedEnvironmentOnboardingSession $draft): ?int
    {
        $state = is_array($draft->state) ? $draft->state : [];

        $providerConnectionId = $this->normalizeInteger($state['provider_connection_id'] ?? $state['selected_provider_connection_id'] ?? null);

        if ($providerConnectionId === null || $draft->managed_environment_id === null) {
            return null;
        }

        $exists = ProviderConnection::query()
            ->whereKey($providerConnectionId)
            ->where('workspace_id', (int) $draft->workspace_id)
            ->where('managed_environment_id', (int) $draft->managed_environment_id)
            ->exists();

        return $exists ? $providerConnectionId : null;
    }

    private function connectionRecentlyUpdated(ManagedEnvironmentOnboardingSession $draft): bool
    {
        $state = is_array($draft->state) ? $draft->state : [];

        return (bool) ($state['connection_recently_updated'] ?? false);
    }

    private function verificationRunMatchesSelectedConnection(OperationRun $run, ?int $selectedProviderConnectionId): bool
    {
        if ($selectedProviderConnectionId === null) {
            return false;
        }

        $context = is_array($run->context ?? null) ? $run->context : [];
        $runProviderConnectionId = $this->normalizeInteger($context['provider_connection_id'] ?? null);

        if ($runProviderConnectionId === null) {
            return false;
        }

        return $runProviderConnectionId === $selectedProviderConnectionId;
    }

    private function verificationStatusFromRunOutcome(OperationRun $run): string
    {
        return match ($run->outcome) {
            OperationRunOutcome::Blocked->value => 'blocked',
            OperationRunOutcome::Succeeded->value => 'ready',
            OperationRunOutcome::PartiallySucceeded->value => 'needs_attention',
            OperationRunOutcome::Failed->value => $this->failedVerificationStatus($run),
            default => 'needs_attention',
        };
    }

    private function failedVerificationStatus(OperationRun $run): string
    {
        foreach ($this->runReasonCodes($run) as $reasonCode) {
            if (str_contains($reasonCode, 'permission') || str_contains($reasonCode, 'consent') || str_contains($reasonCode, 'auth')) {
                return 'blocked';
            }
        }

        return 'needs_attention';
    }

    private function verificationReportOverall(OperationRun $run): ?string
    {
        $report = VerificationReportViewer::report($run);
        $summary = is_array($report['summary'] ?? null) ? $report['summary'] : null;
        $overall = $summary['overall'] ?? null;

        if (! is_string($overall) || ! in_array($overall, VerificationReportOverall::values(), true)) {
            return null;
        }

        return $overall;
    }

    private function verificationBlockingReasonCode(OperationRun $run): string
    {
        foreach ($this->runReasonCodes($run) as $reasonCode) {
            if (str_contains($reasonCode, 'permission') || str_contains($reasonCode, 'consent') || str_contains($reasonCode, 'auth')) {
                return 'verification_blocked_permissions';
            }
        }

        return 'verification_failed';
    }

    /**
     * @return array<int, string>
     */
    private function runReasonCodes(OperationRun $run): array
    {
        $context = is_array($run->context ?? null) ? $run->context : [];
        $codes = [];

        if (is_string($context['reason_code'] ?? null) && trim((string) $context['reason_code']) !== '') {
            $codes[] = strtolower(trim((string) $context['reason_code']));
        }

        $failures = is_array($run->failure_summary ?? null) ? $run->failure_summary : [];

        foreach ($failures as $failure) {
            if (! is_array($failure)) {
                continue;
            }

            foreach (['reason_code', 'code'] as $key) {
                $candidate = $failure[$key] ?? null;

                if (! is_string($candidate) || trim($candidate) === '') {
                    continue;
                }

                $codes[] = strtolower(trim($candidate));
            }
        }

        return array_values(array_unique($codes));
    }

    /**
     * @return array{
     *     has_selected_types: bool,
     *     all_selected_types_completed: bool,
     *     has_active_runs: bool,
     *     has_failure: bool,
     *     has_partial_failure: bool,
     *     summaries: array<int, array{
     *         type: string,
     *         run_id: int|null,
     *         status: string|null,
     *         outcome: string|null,
     *         is_active: bool,
     *         is_failure: bool,
     *         is_partial_failure: bool,
     *         is_completed: bool
     *     }>
     * }
     */
    private function bootstrapState(ManagedEnvironmentOnboardingSession $draft, ?int $selectedProviderConnectionId): array
    {
        $selectedTypes = $this->bootstrapOperationTypes($draft);
        $runMap = $this->bootstrapRunMap($draft, $selectedTypes);

        if ($selectedTypes === []) {
            return [
                'has_selected_types' => false,
                'all_selected_types_completed' => false,
                'has_active_runs' => false,
                'has_failure' => false,
                'has_partial_failure' => false,
                'summaries' => [],
            ];
        }

        $runs = OperationRun::query()
            ->where('workspace_id', (int) $draft->workspace_id)
            ->whereIn('id', array_values($runMap))
            ->get()
            ->keyBy(static fn (OperationRun $run): int => (int) $run->getKey());

        $summaries = [];
        $hasActiveRuns = false;
        $hasFailure = false;
        $hasPartialFailure = false;
        $allSelectedTypesCompleted = true;

        foreach ($selectedTypes as $type) {
            $runId = $runMap[$type] ?? null;
            $run = $runId !== null ? $runs->get($runId) : null;

            if ($run instanceof OperationRun && $selectedProviderConnectionId !== null) {
                $context = is_array($run->context ?? null) ? $run->context : [];
                $runProviderConnectionId = $this->normalizeInteger($context['provider_connection_id'] ?? null);

                if ($runProviderConnectionId !== null && $runProviderConnectionId !== $selectedProviderConnectionId) {
                    $run = null;
                }
            }

            $status = $run instanceof OperationRun ? (string) $run->status : null;
            $outcome = $run instanceof OperationRun ? (string) $run->outcome : null;
            $isActive = $run instanceof OperationRun && $status !== OperationRunStatus::Completed->value;
            $isCompleted = $run instanceof OperationRun && $status === OperationRunStatus::Completed->value;
            $isPartialFailure = $outcome === OperationRunOutcome::PartiallySucceeded->value;
            $isFailure = in_array($outcome, [OperationRunOutcome::Blocked->value, OperationRunOutcome::Failed->value], true);

            $summaries[] = [
                'type' => $type,
                'run_id' => $run instanceof OperationRun ? (int) $run->getKey() : null,
                'status' => $status,
                'outcome' => $outcome,
                'is_active' => $isActive,
                'is_failure' => $isFailure,
                'is_partial_failure' => $isPartialFailure,
                'is_completed' => $isCompleted,
            ];

            $hasActiveRuns = $hasActiveRuns || $isActive;
            $hasFailure = $hasFailure || $isFailure;
            $hasPartialFailure = $hasPartialFailure || $isPartialFailure;
            $allSelectedTypesCompleted = $allSelectedTypesCompleted && $isCompleted && ! $isFailure && ! $isPartialFailure;
        }

        return [
            'has_selected_types' => true,
            'all_selected_types_completed' => $allSelectedTypesCompleted,
            'has_active_runs' => $hasActiveRuns,
            'has_failure' => $hasFailure,
            'has_partial_failure' => $hasPartialFailure,
            'summaries' => $summaries,
        ];
    }

    /**
     * @return array<int, string>
     */
    private function bootstrapOperationTypes(ManagedEnvironmentOnboardingSession $draft): array
    {
        $state = is_array($draft->state) ? $draft->state : [];
        $types = $state['bootstrap_operation_types'] ?? [];

        if (! is_array($types)) {
            return [];
        }

        return array_values(array_filter(
            array_map(static fn (mixed $value): string => is_string($value) ? OperationCatalog::canonicalCode($value) : '', $types),
            static fn (string $value): bool => $value !== '',
        ));
    }

    /**
     * @param  array<int, string>  $selectedTypes
     * @return array<string, int>
     */
    private function bootstrapRunMap(ManagedEnvironmentOnboardingSession $draft, array $selectedTypes): array
    {
        $state = is_array($draft->state) ? $draft->state : [];
        $runs = $state['bootstrap_operation_runs'] ?? null;
        $runMap = [];

        if (is_array($runs)) {
            foreach ($runs as $type => $runId) {
                if (! is_string($type) || trim($type) === '') {
                    continue;
                }

                $normalizedRunId = $this->normalizeInteger($runId);

                if ($normalizedRunId === null) {
                    continue;
                }

                $runMap[OperationCatalog::canonicalCode($type)] = $normalizedRunId;
            }
        }

        if ($runMap !== []) {
            return $runMap;
        }

        $legacyRunIds = $state['bootstrap_run_ids'] ?? null;

        if (! is_array($legacyRunIds)) {
            return [];
        }

        foreach (array_values($selectedTypes) as $index => $type) {
            $runId = $this->normalizeInteger($legacyRunIds[$index] ?? null);

            if ($runId === null) {
                continue;
            }

            $runMap[$type] = $runId;
        }

        return $runMap;
    }

    private function normalizeInteger(mixed $value): ?int
    {
        if (is_int($value) && $value > 0) {
            return $value;
        }

        if (is_string($value) && ctype_digit(trim($value))) {
            $normalized = (int) trim($value);

            return $normalized > 0 ? $normalized : null;
        }

        if (is_numeric($value)) {
            $normalized = (int) $value;

            return $normalized > 0 ? $normalized : null;
        }

        return null;
    }
}
