<?php

namespace App\Services\Operations;

final class BulkSelectionIdentity
{
    /**
     * @param  array<int, mixed>  $ids
     * @return array{kind: 'ids', ids_hash: string, ids_count: int}
     */
    public function fromIds(array $ids): array
    {
        $normalized = [];

        foreach ($ids as $id) {
            if (is_int($id)) {
                $normalized[] = (string) $id;

                continue;
            }

            if (! is_string($id)) {
                continue;
            }

            $id = trim($id);
            if ($id === '') {
                continue;
            }

            $normalized[] = $id;
        }

        $normalized = array_values(array_unique($normalized));
        sort($normalized);

        $json = json_encode($normalized, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);

        return [
            'kind' => 'ids',
            'ids_hash' => hash('sha256', $json),
            'ids_count' => count($normalized),
        ];
    }

    /**
     * @param  array<string, mixed>  $queryPayload
     * @return array{kind: 'query', query_hash: string}
     */
    public function fromQuery(array $queryPayload): array
    {
        $json = $this->canonicalJson($queryPayload);

        return [
            'kind' => 'query',
            'query_hash' => hash('sha256', $json),
        ];
    }

    /**
     * @param  array<string, mixed>  $payload
     */
    public function canonicalJson(array $payload): string
    {
        $normalized = $this->ksortRecursive($payload);

        return (string) json_encode($normalized, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    }

    private function ksortRecursive(mixed $value): mixed
    {
        if (! is_array($value)) {
            return $value;
        }

        $isList = array_is_list($value);
        if (! $isList) {
            ksort($value);
        }

        foreach ($value as $key => $child) {
            $value[$key] = $this->ksortRecursive($child);
        }

        return $value;
    }
}
