loadMissing(['baselineProfile', 'items']); $summary = is_array($snapshot->summary_jsonb) ? $snapshot->summary_jsonb : []; $items = $snapshot->items instanceof EloquentCollection ? $snapshot->items->sortBy([ ['policy_type', 'asc'], ['id', 'asc'], ])->values() : collect(); $groups = $items ->groupBy(static fn (BaselineSnapshotItem $item): string => (string) $item->policy_type) ->map(fn (Collection $groupItems, string $policyType): RenderedSnapshotGroup => $this->presentGroup($policyType, $groupItems)) ->sortBy(static fn (RenderedSnapshotGroup $group): string => mb_strtolower($group->label)) ->values() ->all(); $summaryRows = array_map( static fn (RenderedSnapshotGroup $group): array => [ 'policyType' => $group->policyType, 'label' => $group->label, 'itemCount' => $group->itemCount, 'fidelity' => $group->fidelity->value, 'gapCount' => $group->gapSummary->count, 'capturedAt' => $group->capturedAt, 'coverageHint' => $group->coverageHint, ], $groups, ); $overallGapCount = $this->summaryGapCount($summary); $overallFidelity = FidelityState::fromSummary($summary, $items->isNotEmpty()); return new RenderedSnapshot( snapshotId: (int) $snapshot->getKey(), baselineProfileName: $snapshot->baselineProfile?->name, capturedAt: $snapshot->captured_at?->toIso8601String(), snapshotIdentityHash: is_string($snapshot->snapshot_identity_hash) && trim($snapshot->snapshot_identity_hash) !== '' ? trim($snapshot->snapshot_identity_hash) : null, stateLabel: $overallGapCount > 0 ? 'Captured with gaps' : 'Complete', fidelitySummary: $this->fidelitySummary($summary), overallFidelity: $overallFidelity, overallGapCount: $overallGapCount, summaryRows: $summaryRows, groups: $groups, technicalDetail: [ 'defaultCollapsed' => true, 'summaryPayload' => $summary, 'groupPayloads' => array_map( static fn (RenderedSnapshotGroup $group): array => [ 'label' => $group->label, 'renderingError' => $group->renderingError, 'payload' => $group->technicalPayload, ], $groups, ), ], hasItems: $items->isNotEmpty(), ); } /** * @param Collection $items */ private function presentGroup(string $policyType, Collection $items): RenderedSnapshotGroup { $renderer = $this->registry->rendererFor($policyType); $fallbackRenderer = $this->registry->fallbackRenderer(); $renderingError = null; $technicalPayload = $this->technicalPayload($items); try { $renderedItems = $items ->map(fn (BaselineSnapshotItem $item): RenderedSnapshotItem => $renderer->render($item)) ->all(); } catch (Throwable) { $renderedItems = $items ->map(fn (BaselineSnapshotItem $item): RenderedSnapshotItem => $fallbackRenderer->render($item)) ->all(); $renderingError = 'Structured rendering failed for this policy type. Fallback metadata is shown instead.'; } /** @var array $renderedItems */ $groupFidelity = FidelityState::aggregate(array_map( static fn (RenderedSnapshotItem $item): FidelityState => $item->fidelity, $renderedItems, )); $gapSummary = GapSummary::merge(array_map( static fn (RenderedSnapshotItem $item): GapSummary => $item->gapSummary, $renderedItems, )); if ($renderingError !== null) { $gapSummary = $gapSummary->withMessage($renderingError); } $capturedAt = collect($renderedItems) ->pluck('observedAt') ->filter(static fn (mixed $value): bool => is_string($value) && trim($value) !== '') ->sortDesc() ->first(); $coverageHint = $groupFidelity->coverageHint(); if ($coverageHint === null && $gapSummary->messages !== []) { $coverageHint = $gapSummary->messages[0]; } return new RenderedSnapshotGroup( policyType: $policyType, label: $this->typeLabel($policyType), itemCount: $items->count(), fidelity: $groupFidelity, gapSummary: $gapSummary, initiallyCollapsed: true, items: $renderedItems, renderingError: $renderingError, coverageHint: $coverageHint, capturedAt: is_string($capturedAt) ? $capturedAt : null, technicalPayload: $technicalPayload, ); } /** * @param Collection $items * @return array */ private function technicalPayload(Collection $items): array { return [ 'items' => $items ->map(static fn (BaselineSnapshotItem $item): array => [ 'snapshot_item_id' => (int) $item->getKey(), 'policy_type' => (string) $item->policy_type, 'meta_jsonb' => is_array($item->meta_jsonb) ? $item->meta_jsonb : [], ]) ->all(), ]; } /** * @param array $summary */ private function summaryGapCount(array $summary): int { $gaps = is_array($summary['gaps'] ?? null) ? $summary['gaps'] : []; $count = $gaps['count'] ?? 0; return is_numeric($count) ? (int) $count : 0; } /** * @param array $summary */ private function fidelitySummary(array $summary): string { $counts = is_array($summary['fidelity_counts'] ?? null) ? $summary['fidelity_counts'] : []; $content = is_numeric($counts['content'] ?? null) ? (int) $counts['content'] : 0; $meta = is_numeric($counts['meta'] ?? null) ? (int) $counts['meta'] : 0; return sprintf('Content %d, Meta %d', $content, $meta); } private function typeLabel(string $policyType): string { return InventoryPolicyTypeMeta::baselineCompareLabel($policyType) ?? InventoryPolicyTypeMeta::label($policyType) ?? Str::headline($policyType); } }