loadMissing([ 'tenant', 'environmentReview.evidenceSnapshot', 'environmentReview.currentExportReviewPack', 'environmentReview.sections', ]); $tenant = $reviewPack->tenant; $review = $reviewPack->environmentReview; $hasReadyArtifact = $this->hasReadyArtifact($reviewPack); $canViewReviewPack = $tenant instanceof ManagedEnvironment && $actor instanceof User && $actor->can(Capabilities::REVIEW_PACK_VIEW, $tenant); $canManageReviewPack = $tenant instanceof ManagedEnvironment && $actor instanceof User && $actor->can(Capabilities::REVIEW_PACK_MANAGE, $tenant); if ($this->isExpired($reviewPack)) { return new CustomerOutputGateDecision( state: self::STATE_EXPIRED, guidanceState: ReviewPackOutputResolutionGuidance::STATE_EXPORT_NOT_READY, hasReadyArtifact: false, canStreamCustomerOutput: false, canStreamInternalPreview: false, reason: __('localization.review.expired'), ); } if (! $hasReadyArtifact) { return new CustomerOutputGateDecision( state: self::STATE_NOT_AVAILABLE, guidanceState: ReviewPackOutputResolutionGuidance::STATE_EXPORT_NOT_READY, hasReadyArtifact: false, canStreamCustomerOutput: false, canStreamInternalPreview: false, reason: __('localization.review.export_not_ready'), ); } if (! $review instanceof EnvironmentReview || ! $this->isCurrentReviewExport($reviewPack, $review)) { return new CustomerOutputGateDecision( state: self::STATE_UNKNOWN, guidanceState: ReviewPackOutputResolutionGuidance::STATE_UNKNOWN, hasReadyArtifact: true, canStreamCustomerOutput: false, canStreamInternalPreview: $canManageReviewPack, reason: __('localization.review.requires_review'), ); } $readiness = ReviewPackOutputResolutionGuidance::readinessForReview($review); $guidance = ReviewPackOutputResolutionGuidance::fromReadiness($readiness); $guidanceState = (string) ($guidance['state'] ?? ReviewPackOutputResolutionGuidance::STATE_UNKNOWN); $canStreamCustomerOutput = $guidanceState === ReviewPackOutputResolutionGuidance::STATE_CUSTOMER_SAFE_READY && $canViewReviewPack; return new CustomerOutputGateDecision( state: $this->stateForGuidance($guidanceState), guidanceState: $guidanceState, hasReadyArtifact: true, canStreamCustomerOutput: $canStreamCustomerOutput, canStreamInternalPreview: $canManageReviewPack, reason: (string) ($guidance['primary_reason'] ?? __('localization.review.requires_review')), readiness: $readiness, guidance: $guidance, ); } private function hasReadyArtifact(ReviewPack $reviewPack): bool { if ($reviewPack->status !== ReviewPackStatus::Ready->value) { return false; } return filled($reviewPack->file_path) && filled($reviewPack->file_disk); } private function isExpired(ReviewPack $reviewPack): bool { return $reviewPack->status === ReviewPackStatus::Expired->value || ($reviewPack->expires_at !== null && $reviewPack->expires_at->isPast()); } private function isCurrentReviewExport(ReviewPack $reviewPack, EnvironmentReview $review): bool { return (int) $review->managed_environment_id === (int) $reviewPack->managed_environment_id && (int) ($review->current_export_review_pack_id ?? 0) === (int) $reviewPack->getKey(); } private function stateForGuidance(string $guidanceState): string { return match ($guidanceState) { ReviewPackOutputResolutionGuidance::STATE_CUSTOMER_SAFE_READY => self::STATE_READY, ReviewPackOutputResolutionGuidance::STATE_PUBLICATION_BLOCKED => self::STATE_BLOCKED, ReviewPackOutputResolutionGuidance::STATE_INTERNAL_ONLY => self::STATE_INTERNAL_ONLY, ReviewPackOutputResolutionGuidance::STATE_EXPORT_NOT_READY => self::STATE_NOT_AVAILABLE, ReviewPackOutputResolutionGuidance::STATE_PUBLISHED_WITH_LIMITATIONS => self::STATE_NEEDS_ATTENTION, default => self::STATE_UNKNOWN, }; } }