$profile * @param array $guidance * @param array $evidenceBasis * @param array $metrics * @return array{ * identity:array{prepared_by:string,prepared_for:string,generated_by:string,generated_at:mixed,accent:string,logo:null}, * layout:array{mode:string,appendix_prominence:string,section_order:list,section_ranks:array}, * kpi_strip:list * } */ public static function resolve( ReviewPack $reviewPack, ManagedEnvironment $tenant, array $profile, array $guidance, array $evidenceBasis, array $metrics, ): array { $workspace = $tenant->workspace; return [ 'identity' => self::identityFor( $workspace instanceof Workspace ? $workspace->name : null, $tenant->name, $reviewPack->generated_at, ), 'layout' => self::layoutForProfile($profile), 'kpi_strip' => self::kpiStrip($guidance, $evidenceBasis, $metrics), ]; } /** * @return array{prepared_by:string,prepared_for:string,generated_by:string,generated_at:mixed,accent:string,logo:null} */ public static function identityFor(?string $workspaceName, ?string $environmentName, mixed $generatedAt = null): array { return [ 'prepared_by' => self::displayText($workspaceName, 'TenantPilot'), 'prepared_for' => self::displayText($environmentName, __('localization.review.tenant')), 'generated_by' => 'TenantPilot', 'generated_at' => $generatedAt, 'accent' => self::DEFAULT_ACCENT, 'logo' => null, ]; } /** * @param array $profile * @return array{mode:string,appendix_prominence:string,section_order:list,section_ranks:array} */ public static function layoutForProfile(array $profile): array { $profileKey = (string) ($profile['effective_key'] ?? ($profile['profile_key'] ?? ReportProfileRegistry::DEFAULT_PROFILE)); [$mode, $appendixProminence, $sectionOrder] = match ($profileKey) { ReportProfileRegistry::CUSTOMER_EXECUTIVE => [ 'executive', 'minimal', [ 'executive_summary', 'profile', 'limitations', 'findings', 'governance_decisions', 'accepted_risks', 'evidence_basis', 'next_actions', 'disclosure', 'appendix', 'technical_details', ], ], ReportProfileRegistry::CUSTOMER_TECHNICAL => [ 'technical', 'standard', [ 'executive_summary', 'profile', 'evidence_basis', 'limitations', 'findings', 'governance_decisions', 'accepted_risks', 'next_actions', 'disclosure', 'appendix', 'technical_details', ], ], ReportProfileRegistry::AUDITOR_APPENDIX => [ 'auditor_appendix', 'high', [ 'profile', 'evidence_basis', 'disclosure', 'limitations', 'appendix', 'technical_details', 'executive_summary', 'findings', 'governance_decisions', 'accepted_risks', 'next_actions', ], ], default => [ 'internal', 'standard', [ 'executive_summary', 'profile', 'limitations', 'evidence_basis', 'findings', 'governance_decisions', 'accepted_risks', 'next_actions', 'disclosure', 'appendix', 'technical_details', ], ], }; $sectionRanks = []; foreach ($sectionOrder as $rank => $sectionKey) { $sectionRanks[$sectionKey] = ($rank + 1) * 10; } return [ 'mode' => $mode, 'appendix_prominence' => $appendixProminence, 'section_order' => $sectionOrder, 'section_ranks' => $sectionRanks, ]; } /** * @param array $guidance * @param array $evidenceBasis * @param array $metrics * @return list */ public static function kpiStrip(array $guidance, array $evidenceBasis, array $metrics): array { return [ [ 'key' => 'governance_status', 'label' => __('localization.review.report_kpi_governance_status'), 'value' => self::displayText($guidance['boundary_label'] ?? null, __('localization.review.unavailable')), 'description' => self::displayText( $guidance['primary_reason'] ?? null, __('localization.review.report_summary_default_impact'), ), ], [ 'key' => 'evidence_coverage', 'label' => __('localization.review.report_kpi_evidence_coverage'), 'value' => self::displayText($evidenceBasis['label'] ?? null, __('localization.review.unavailable')), 'description' => self::displayText($evidenceBasis['description'] ?? null, __('localization.review.report_evidence_missing_description')), ], [ 'key' => 'key_risks', 'label' => __('localization.review.report_kpi_key_risks'), 'value' => self::countValue($metrics['top_findings'] ?? null, (bool) ($metrics['top_findings_measured'] ?? false)), 'description' => __('localization.review.report_kpi_key_risks_description'), ], [ 'key' => 'open_decisions', 'label' => __('localization.review.report_kpi_open_decisions'), 'value' => self::countValue($metrics['governance_decisions'] ?? null, (bool) ($metrics['governance_decisions_measured'] ?? false)), 'description' => __('localization.review.report_kpi_open_decisions_description'), ], ]; } private static function countValue(mixed $items, bool $measured): string { if (! $measured) { return __('localization.review.report_kpi_not_measured'); } if (! is_array($items)) { return __('localization.review.unavailable'); } return (string) count($items); } private static function displayText(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; } }