$profile * @param array $readiness * @param array $metadata * @return array{ * mandatory_disclosures:list, * warnings:list, * blocking_reasons:list, * proof_states:array{audience_boundary:string,evidence_basis:string,protected_values:string,non_certification:string}, * show_section_appendix:bool, * show_technical_details:bool * } */ public static function evaluate(array $profile, array $readiness, array $metadata = []): array { $isCustomerFacing = (bool) ($profile['is_customer_facing'] ?? false); $containsPii = (bool) ($readiness['contains_pii'] ?? false); $protectedValuesHidden = (bool) ($readiness['protected_values_hidden'] ?? false); $disclosurePresent = (bool) ($readiness['disclosure_present'] ?? false); $displayedDisclosure = self::plainText( $metadata['non_certification_disclosure'] ?? null, __('localization.review.non_certification_disclosure_text'), ); $proofStates = [ 'audience_boundary' => self::PROOF_VERIFIED, 'evidence_basis' => self::evidenceBasisProofState((string) ($readiness['evidence_completeness_state'] ?? '')), 'protected_values' => self::protectedValuesProofState( isCustomerFacing: $isCustomerFacing, containsPii: $containsPii, protectedValuesHidden: $protectedValuesHidden, ), 'non_certification' => $disclosurePresent ? self::PROOF_ASSUMED : self::PROOF_MISSING, ]; $blockingReasons = []; if ($isCustomerFacing && $containsPii) { $blockingReasons[] = [ 'key' => 'customer_profile_internal_only', 'label' => __('localization.review.report_disclosure_customer_profile_internal_only'), 'summary' => __('localization.review.report_disclosure_customer_profile_internal_only_summary'), ]; } $warnings = []; if ((bool) ($profile['is_fallback'] ?? false)) { $warnings[] = [ 'key' => 'profile_fallback', 'label' => __('localization.review.report_profile_fallback_notice'), 'summary' => __('localization.review.report_profile_fallback_summary'), ]; } if ($isCustomerFacing && (string) ($readiness['customer_safe_state'] ?? '') !== ReviewPackOutputReadiness::STATE_CUSTOMER_SAFE_READY) { $warnings[] = [ 'key' => 'customer_profile_requires_review', 'label' => __('localization.review.report_external_sharing_warning'), 'summary' => __('localization.review.report_disclosure_customer_profile_requires_review'), ]; } if ($proofStates['non_certification'] === self::PROOF_MISSING) { $warnings[] = [ 'key' => 'non_certification_missing', 'label' => __('localization.review.non_certification_disclosure'), 'summary' => __('localization.review.report_disclosure_non_certification_missing'), ]; } $showDetailedContent = ! ($isCustomerFacing && $containsPii); return [ 'mandatory_disclosures' => [ [ 'key' => 'audience_boundary', 'label' => __('localization.review.report_disclosure_audience_boundary'), 'summary' => __('localization.review.report_disclosure_audience_boundary_summary', [ 'audience' => (string) ($profile['audience_label'] ?? __('localization.review.unavailable')), ]), 'proof_state' => $proofStates['audience_boundary'], ], [ 'key' => 'evidence_basis', 'label' => __('localization.review.report_disclosure_evidence_basis'), 'summary' => match ($proofStates['evidence_basis']) { self::PROOF_VERIFIED => __('localization.review.report_disclosure_evidence_verified'), self::PROOF_MISSING => __('localization.review.report_disclosure_evidence_missing'), default => __('localization.review.report_disclosure_evidence_unknown'), }, 'proof_state' => $proofStates['evidence_basis'], ], [ 'key' => 'protected_values', 'label' => __('localization.review.report_disclosure_protected_values'), 'summary' => match ($proofStates['protected_values']) { self::PROOF_ASSUMED => __('localization.review.report_disclosure_protected_values_assumed'), self::PROOF_NOT_APPLICABLE => __('localization.review.report_disclosure_protected_values_not_applicable'), self::PROOF_MISSING => __('localization.review.report_disclosure_protected_values_missing'), default => __('localization.review.report_disclosure_protected_values_unknown'), }, 'proof_state' => $proofStates['protected_values'], ], [ 'key' => 'non_certification', 'label' => __('localization.review.non_certification_disclosure'), 'summary' => $displayedDisclosure, 'proof_state' => $proofStates['non_certification'], ], ], 'warnings' => $warnings, 'blocking_reasons' => $blockingReasons, 'proof_states' => $proofStates, 'show_section_appendix' => (bool) ($profile['show_section_appendix'] ?? false) && $showDetailedContent, 'show_technical_details' => (bool) ($profile['show_technical_details'] ?? false) && $showDetailedContent, ]; } private static function evidenceBasisProofState(string $evidenceCompletenessState): string { return match ($evidenceCompletenessState) { 'complete' => self::PROOF_VERIFIED, 'missing', 'partial', 'stale' => self::PROOF_MISSING, default => self::PROOF_UNKNOWN, }; } private static function protectedValuesProofState( bool $isCustomerFacing, bool $containsPii, bool $protectedValuesHidden, ): string { if (! $isCustomerFacing) { return self::PROOF_NOT_APPLICABLE; } if ($containsPii || ! $protectedValuesHidden) { return self::PROOF_MISSING; } return self::PROOF_ASSUMED; } private static function plainText(mixed $value, string $fallback): string { if (! is_scalar($value) && $value !== null) { return $fallback; } $text = preg_replace('/\s+/', ' ', trim((string) $value)); if (! is_string($text) || $text === '') { return $fallback; } return str_starts_with($text, 'localization.') ? $fallback : $text; } }