<?php

declare(strict_types=1);

namespace App\Services\Baselines;

final class BaselineSnapshotItemNormalizer
{
    /**
     * @param  list<array{subject_type: string, subject_external_id: string, subject_key: string, policy_type: string, baseline_hash: string, meta_jsonb: array<string, mixed>}>  $items
     * @return array{items: list<array{subject_type: string, subject_external_id: string, subject_key: string, policy_type: string, baseline_hash: string, meta_jsonb: array<string, mixed>}>, duplicates: int}
     */
    public function deduplicate(array $items): array
    {
        $uniqueItems = [];
        $duplicates = 0;

        foreach ($items as $item) {
            $key = trim((string) ($item['subject_type'] ?? '')).'|'.trim((string) ($item['subject_external_id'] ?? ''));

            if ($key === '|') {
                continue;
            }

            if (! array_key_exists($key, $uniqueItems)) {
                $uniqueItems[$key] = $item;

                continue;
            }

            $duplicates++;

            if ($this->shouldReplace($uniqueItems[$key], $item)) {
                $uniqueItems[$key] = $item;
            }
        }

        return [
            'items' => array_values($uniqueItems),
            'duplicates' => $duplicates,
        ];
    }

    /**
     * @param  array{meta_jsonb?: array<string, mixed>, baseline_hash?: string}  $current
     * @param  array{meta_jsonb?: array<string, mixed>, baseline_hash?: string}  $candidate
     */
    private function shouldReplace(array $current, array $candidate): bool
    {
        $currentFidelity = $this->fidelityRank($current);
        $candidateFidelity = $this->fidelityRank($candidate);

        if ($candidateFidelity !== $currentFidelity) {
            return $candidateFidelity > $currentFidelity;
        }

        $currentObservedAt = $this->observedAt($current);
        $candidateObservedAt = $this->observedAt($candidate);

        if ($candidateObservedAt !== $currentObservedAt) {
            return $candidateObservedAt > $currentObservedAt;
        }

        return strcmp((string) ($candidate['baseline_hash'] ?? ''), (string) ($current['baseline_hash'] ?? '')) > 0;
    }

    /**
     * @param  array{meta_jsonb?: array<string, mixed>}  $item
     */
    private function fidelityRank(array $item): int
    {
        $fidelity = data_get($item, 'meta_jsonb.evidence.fidelity');

        return match ($fidelity) {
            'content' => 2,
            'meta' => 1,
            default => 0,
        };
    }

    /**
     * @param  array{meta_jsonb?: array<string, mixed>}  $item
     */
    private function observedAt(array $item): string
    {
        $observedAt = data_get($item, 'meta_jsonb.evidence.observed_at');

        return is_string($observedAt) ? $observedAt : '';
    }
}
