<?php

declare(strict_types=1);

namespace Tests\Framework\Http\Parser;

use App\Framework\Cache\Compression\NullCompression;
use App\Framework\Cache\CompressionCacheDecorator;
use App\Framework\Cache\Driver\InMemoryCache;
use App\Framework\Cache\GeneralCache;
use App\Framework\Http\Parser\ParserCache;
use App\Framework\Http\Parser\ParserConfig;
use App\Framework\Http\Parser\QueryStringParser;
use App\Framework\Serializer\Php\PhpSerializer;
use PHPUnit\Framework\TestCase;

final class QueryStringParserTest extends TestCase
{
    private QueryStringParser $parser;

    protected function setUp(): void
    {
        // Create parser cache with proper serialization
        $baseCache = new GeneralCache(new InMemoryCache(), new \App\Framework\Serializer\Php\PhpSerializer());
        $compressionCache = new CompressionCacheDecorator(
            $baseCache,
            new NullCompression(),
            new PhpSerializer()
        );
        $cache = new ParserCache($compressionCache);

        $this->parser = new QueryStringParser($cache, new ParserConfig());
    }

    public function testParseEmptyString(): void
    {
        $result = $this->parser->parse('');
        $this->assertSame([], $result);
    }

    public function testParseSimpleParameters(): void
    {
        $result = $this->parser->parse('foo=bar&baz=qux');
        $this->assertSame([
            'foo' => 'bar',
            'baz' => 'qux',
        ], $result);
    }

    public function testParseUrlEncodedValues(): void
    {
        $result = $this->parser->parse('name=John+Doe&city=New%20York');
        $this->assertSame([
            'name' => 'John Doe',
            'city' => 'New York',
        ], $result);
    }

    public function testParseSpecialCharacters(): void
    {
        $result = $this->parser->parse('email=test%40example.com&msg=Hello%21');
        $this->assertSame([
            'email' => 'test@example.com',
            'msg' => 'Hello!',
        ], $result);
    }

    public function testParseEmptyValues(): void
    {
        $result = $this->parser->parse('foo=&bar=value&baz');
        $this->assertSame([
            'foo' => '',
            'bar' => 'value',
            'baz' => '',
        ], $result);
    }

    public function testParseArrayNotation(): void
    {
        $result = $this->parser->parse('items[]=one&items[]=two&items[]=three');
        $this->assertSame([
            'items' => ['one', 'two', 'three'],
        ], $result);
    }

    public function testParseArrayWithKeys(): void
    {
        $result = $this->parser->parse('user[name]=John&user[email]=john@example.com');
        $this->assertSame([
            'user' => [
                'name' => 'John',
                'email' => 'john@example.com',
            ],
        ], $result);
    }

    public function testParseNestedArrays(): void
    {
        $result = $this->parser->parse('data[user][info][name]=John&data[user][info][age]=30');
        $this->assertSame([
            'data' => [
                'user' => [
                    'info' => [
                        'name' => 'John',
                        'age' => '30',
                    ],
                ],
            ],
        ], $result);
    }

    public function testParseMixedArrayNotations(): void
    {
        $result = $this->parser->parse('items[0]=first&items[]=second&items[2]=third');
        $this->assertSame([
            'items' => [
                '0' => 'first',
                1 => 'second',
                '2' => 'third',
            ],
        ], $result);
    }

    public function testParseComplexRealWorldExample(): void
    {
        $query = 'search=php+frameworks&' .
                 'filters[category][]=web&' .
                 'filters[category][]=api&' .
                 'filters[rating]=5&' .
                 'sort=popularity&' .
                 'page=2';

        $result = $this->parser->parse($query);
        $this->assertSame([
            'search' => 'php frameworks',
            'filters' => [
                'category' => ['web', 'api'],
                'rating' => '5',
            ],
            'sort' => 'popularity',
            'page' => '2',
        ], $result);
    }

    public function testHandlesDuplicateKeys(): void
    {
        // Later values override earlier ones for simple keys
        $result = $this->parser->parse('foo=bar&foo=baz');
        $this->assertSame(['foo' => 'baz'], $result);
    }

    public function testHandlesEmptyArrayKeys(): void
    {
        $result = $this->parser->parse('arr[]=one&arr[][nested]=two');
        $this->assertSame([
            'arr' => [
                0 => 'one',
                1 => ['nested' => 'two'],
            ],
        ], $result);
    }
}
