<?php

namespace Velis\Test\Model\DataObject;

use PHPUnit\Framework\TestCase;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
use ReflectionProperty;
use Velis\Filter;
use Velis\Model\DataObject\DbExpressionTrait;
use Velis\Model\DataObject\DbStructureTrait;

/**
 * @author Jan Małysiak <jan.malysiak@velis.pl>
 */
class DbExpressionTraitTest extends TestCase
{
    /**
     * @var DbStructureTrait
     */
    private $instance;

    /**
     * @var ReflectionProperty
     */
    private $conditionsProperty;

    protected function setUp(): void
    {
        $this->instance = new class {
            const DEFAULT_LIST_COUNT_ATTRIBUTES = 'COUNT(*) list_items_found';

            use DbExpressionTrait;

            public static function hasField($field)
            {
                return true;
            }
        };

        $class = new ReflectionClass($this->instance);
        $this->conditionsProperty = $class->getProperty('_listConditions');
        $this->conditionsProperty->setAccessible(true);
    }

    /**
     * @param $params
     * @param $fields
     * @param $suffixes
     * @param $expectedConditions
     *
     * @dataProvider provideTestDataForBuildDateConditionsMethod
     */
    public function testBuildDateConditions($params, $fields, $suffixes, $expectedConditions)
    {
        $method = $this->getMethod('_buildDateConditions');

        $method->invoke(null, $params, $fields, $suffixes);

        $this->assertEquals($expectedConditions, $this->conditionsProperty->getValue());
    }

    public function provideTestDataForBuildDateConditionsMethod()
    {
        return [
            'params being an array' => [
                [
                    'date_added_from' => '2020-01-01',
                    'date_added_to' => '2020-04-01',
                ],
                [
                    'date_added',
                ],
                null,
                [
                    'date_trunc(\'day\', date_added) >= :date_added_from',
                    'date_trunc(\'day\', date_added) <= :date_added_to',
                ],
            ],
            'params being a Filter instance' => [
                new Filter([
                    'date_added_from' => '2020-01-01',
                    'date_added_to' => '2020-04-01',
                ]),
                [
                    'date_added',
                ],
                null,
                [
                    'date_trunc(\'day\', date_added) >= :date_added_from',
                    'date_trunc(\'day\', date_added) <= :date_added_to',
                ],
            ],
        ];
    }

    /**
     * @dataProvider provideTestDataForTruncateSortClauseMethod
     */
    public function testTruncateSortClause($order, $expectedOrder)
    {
        $method = $this->getMethod('truncateSortClause');

        $this->assertEquals($expectedOrder, $method->invoke(null, $order));
    }

    public function provideTestDataForTruncateSortClauseMethod()
    {
        return [
            ['first_name NULLS FIRST', 'first_name'],
            ['user_id DESC', 'user_id'],
            ['description DESC', 'description'],
            ['description ASC, ticket DESC', 'description, ticket'],
        ];
    }

    /**
     * @param string $methodName
     * @return ReflectionMethod
     * @throws ReflectionException
     */
    private function getMethod($methodName)
    {
        $class = new ReflectionClass($this->instance);
        $method = $class->getMethod($methodName);
        $method->setAccessible(true);

        return $method;
    }

    protected function tearDown(): void
    {
        $this->conditionsProperty->setValue(null);
    }
}
