<?php
/**
 * For the full copyright and license information, please view the
 * docs/licenses/LICENSE.txt file that was distributed with this source code.
 */

declare(strict_types=1);

namespace Tests\Unit\Core\Grid\Query;

use PHPUnit\Framework\TestCase;
use PrestaShop\PrestaShop\Core\Grid\Exception\UnsupportedParameterException;
use PrestaShop\PrestaShop\Core\Grid\Query\DoctrineQueryParser;
use stdClass;

class DoctrineQueryParserTest extends TestCase
{
    /**
     * @var DoctrineQueryParser
     */
    private $queryParser;

    /**
     * {@inheritdoc}
     */
    protected function setUp(): void
    {
        $this->queryParser = new DoctrineQueryParser();
    }

    public function testParseWithNamedParameters()
    {
        $preparedQuery = 'SELECT tests FROM pierre_rambaud WHERE motivation = :motivation AND energy = :energy';
        $queryParameters = [
            'motivation' => 'OK',
            'energy' => 'none',
        ];

        $expectedQuery = "SELECT tests FROM pierre_rambaud WHERE motivation = 'OK' AND energy = 'none'";

        $this->assertSame($expectedQuery, $this->queryParser->parse($preparedQuery, $queryParameters));
    }

    /**
     * @throws UnsupportedParameterException
     */
    public function testParseWithParametersMustThrowAnException()
    {
        $this->expectExceptionMessage('Only named parameters are supported in prepared queries.');
        $this->expectException(UnsupportedParameterException::class);

        $preparedQuery = 'SELECT tests FROM pierre_rambaud WHERE motivation = ? AND energy = ?';
        $queryParameters = ['OK', 'none'];

        $this->queryParser->parse($preparedQuery, $queryParameters);
    }

    public function testParseWithArrayNamedParameters()
    {
        $preparedQuery = 'SELECT tests FROM pierre_rambaud WHERE motivation IN (:motivation)';
        $queryParameters = [
            'motivation' => [
                'great',
                'good',
                'ok',
                'nok',
                'none',
            ],
        ];

        $expectedQuery = "SELECT tests FROM pierre_rambaud WHERE motivation IN ('great', 'good', 'ok', 'nok', 'none')";

        $this->assertSame($expectedQuery, $this->queryParser->parse($preparedQuery, $queryParameters));
    }

    /**
     * @throws UnsupportedParameterException
     */
    public function testParseWithArrayParametersMustThrowAnException()
    {
        $this->expectExceptionMessage('Only named parameters are supported in prepared queries.');
        $this->expectException(UnsupportedParameterException::class);

        $preparedQuery = 'SELECT tests FROM pierre_rambaud WHERE motivation IN (?)';
        $queryParameters = [
            [
                'great',
                'good',
                'ok',
                'nok',
                'none',
            ],
        ];

        $this->queryParser->parse($preparedQuery, $queryParameters);
    }

    public function testParseWithNullNamedParameters()
    {
        $preparedQuery = 'SELECT tests FROM pierre_rambaud WHERE motivation IS :motivation';
        $queryParameters = [
            'motivation' => null,
        ];

        $expectedQuery = 'SELECT tests FROM pierre_rambaud WHERE motivation IS NULL';

        $this->assertSame($expectedQuery, $this->queryParser->parse($preparedQuery, $queryParameters));
    }

    public function testParseWithBooleanNamedParameters()
    {
        $preparedQuery = 'SELECT tests FROM pierre_rambaud WHERE motivation = :motivation';
        $queryParameters = [
            'motivation' => false,
        ];

        $expectedQuery = 'SELECT tests FROM pierre_rambaud WHERE motivation = FALSE';

        $this->assertSame($expectedQuery, $this->queryParser->parse($preparedQuery, $queryParameters));

        $preparedQuery2 = 'SELECT tests FROM pierre_rambaud WHERE energy = :energy';
        $queryParameters2 = [
            'energy' => true,
        ];

        $expectedQuery2 = 'SELECT tests FROM pierre_rambaud WHERE energy = TRUE';

        $this->assertSame($expectedQuery2, $this->queryParser->parse($preparedQuery2, $queryParameters2));
    }

    public function testParseWithUnsupportedTypeMustThrowAnException()
    {
        $this->expectExceptionMessage('Unsupported value type: object');
        $this->expectException(UnsupportedParameterException::class);

        $preparedQuery = 'SELECT tests FROM pierre_rambaud WHERE motivation IN (:motivation)';
        $queryParameters = [
            'motivation' => new stdClass(),
        ];

        $this->queryParser->parse($preparedQuery, $queryParameters);
    }

    public function testParseWithNumericNamedParameters()
    {
        $preparedQuery = 'SELECT * FROM product WHERE id_product = :id_product';
        $queryParameters = [
            'id_product' => 2,
        ];

        $expectedQuery = 'SELECT * FROM product WHERE id_product = 2';

        $this->assertSame($expectedQuery, $this->queryParser->parse($preparedQuery, $queryParameters));

        $preparedQuery = 'SELECT * FROM product WHERE price = :price';
        $queryParameters = [
            'price' => 3.99,
        ];

        $expectedQuery = 'SELECT * FROM product WHERE price = 3.99';

        $this->assertSame($expectedQuery, $this->queryParser->parse($preparedQuery, $queryParameters));
    }
}
