trim() ->lower() ->replace(['-', ' '], '_') ->toString(); return match ($normalized) { 'content', 'full' => self::Full, 'partial' => self::Partial, 'meta', 'metadata_only', 'reference_only' => self::ReferenceOnly, 'unsupported' => self::Unsupported, default => $usesFallback ? self::Unsupported : self::Partial, }; } /** * @param array $summary */ public static function fromSummary(array $summary, bool $hasItems): self { if (! $hasItems) { return self::Unsupported; } $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; if ($content > 0 && $meta === 0) { return self::Full; } if ($content > 0 && $meta > 0) { return self::Partial; } if ($meta > 0) { return self::ReferenceOnly; } return self::Unsupported; } /** * @param array $states */ public static function aggregate(array $states): self { if ($states === []) { return self::Unsupported; } $worst = self::Full; foreach ($states as $state) { if ($state->rank() < $worst->rank()) { $worst = $state; } } return $worst; } public function label(): string { return match ($this) { self::Full => 'Full', self::Partial => 'Partial', self::ReferenceOnly => 'Reference only', self::Unsupported => 'Unsupported', }; } public function coverageHint(): ?string { return match ($this) { self::Full => null, self::Partial => 'Mixed evidence fidelity across this group.', self::ReferenceOnly => 'Metadata-only evidence is available.', self::Unsupported => 'Fallback metadata rendering is being used.', }; } private function rank(): int { return match ($this) { self::Full => 4, self::Partial => 3, self::ReferenceOnly => 2, self::Unsupported => 1, }; } }