<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\AdminBundle\Tests\Components;

use Ferienpass\AdminBundle\Components\SearchableQueryableList;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

final class SearchableQueryableListTest extends TestCase
{
    /**
     * Malformed UTF-8 anywhere in the query string must be rejected before it
     * reaches the LiveProp dehydration (json_encode with JSON_THROW_ON_ERROR),
     * which would otherwise raise an uncaught 500 at render time.
     *
     * @param array<array-key, mixed> $query
     */
    #[DataProvider('queryProvider')]
    public function testIsValidUtf8(bool $expected, array $query): void
    {
        self::assertSame($expected, $this->invokeIsValidUtf8($query));
    }

    public static function queryProvider(): iterable
    {
        $malformed = "\xc3\x28"; // invalid 2-byte sequence

        yield 'empty' => [true, []];
        yield 'plain values' => [true, ['q' => 'Sömmer', 'page' => '2']];
        yield 'nested filter ok' => [true, ['filter' => ['name' => 'Ärger', 'age' => ['12', '13']]]];
        yield 'numeric values' => [true, ['page' => 1, 'flags' => [true, null]]];

        yield 'malformed top-level value' => [false, ['q' => $malformed]];
        yield 'malformed nested value' => [false, ['filter' => ['name' => $malformed]]];
        yield 'malformed deep value' => [false, ['filter' => ['ids' => ['ok', $malformed]]]];
        yield 'malformed key' => [false, [$malformed => 'value']];
        yield 'malformed nested key' => [false, ['filter' => [$malformed => 'value']]];
    }

    private function invokeIsValidUtf8(array $query): bool
    {
        $method = new \ReflectionMethod(SearchableQueryableList::class, 'isValidUtf8');

        return $method->invoke(null, $query);
    }
}
