<?php

namespace Tests\Unit;

use App\Services\MeilisearchService;
use PHPUnit\Framework\TestCase;

class MeilisearchFilterStringTest extends TestCase
{
    private function buildFilterString(array $filters): string
    {
        $service = new MeilisearchService();
        $reflection = new \ReflectionClass($service);
        $method = $reflection->getMethod('buildFilterString');
        $method->setAccessible(true);

        return $method->invoke($service, $filters);
    }

    public function test_plain_string_value_is_quoted(): void
    {
        $result = $this->buildFilterString(['make_slug' => 'ford']);

        $this->assertEquals('make_slug = "ford"', $result);
    }

    public function test_array_of_values_produces_in_clause(): void
    {
        $result = $this->buildFilterString(['make_slug' => ['ford', 'bmw']]);

        $this->assertEquals('make_slug IN ["ford", "bmw"]', $result);
    }

    public function test_numeric_values_are_not_quoted(): void
    {
        $this->assertEquals('price = 1000', $this->buildFilterString(['price' => 1000]));
        $this->assertEquals('franchise_id IN [1, 2]', $this->buildFilterString(['franchise_id' => [1, 2]]));
    }

    public function test_boolean_values_render_as_keywords(): void
    {
        $this->assertEquals('is_published = true', $this->buildFilterString(['is_published' => true]));
    }

    /**
     * A crafted single value must not be able to break out of its quoted literal
     * to inject an OR clause exposing unpublished stock. Every internal double quote
     * is escaped to \" so Meilisearch treats the whole payload as inert string data.
     */
    public function test_injection_via_single_value_is_escaped(): void
    {
        $result = $this->buildFilterString([
            'is_published' => true,
            'make_slug' => 'x" OR is_published = false OR make_slug = "y',
        ]);

        $this->assertEquals(
            'is_published = true AND make_slug = "x\\" OR is_published = false OR make_slug = \\"y"',
            $result
        );
    }

    /**
     * The same breakout attempt inside an IN array must also be neutralised.
     */
    public function test_injection_via_array_value_is_escaped(): void
    {
        $result = $this->buildFilterString([
            'is_published' => true,
            'make_slug' => ['foo"] OR is_published = false OR make_slug IN ["bar'],
        ]);

        $this->assertEquals(
            'is_published = true AND make_slug IN ["foo\\"] OR is_published = false OR make_slug IN [\\"bar"]',
            $result
        );
    }

    /**
     * A backslash must be escaped first so it cannot be used to smuggle in an unescaped quote.
     */
    public function test_backslash_is_escaped(): void
    {
        $result = $this->buildFilterString(['make_slug' => 'a\\b']);

        $this->assertEquals('make_slug = "a\\\\b"', $result);
    }
}
