<?php

/**
 * Lifestyle Webconsulting GmbH
 *
 * LICENSE: This Software is the property of Lifestyle Webconsulting GmbH (Aschaffenburg, Germany)
 * and is private by copyright law - it is NOT Freeware.
 *
 * Any unauthorized use of this software without a valid license
 * is a violation of the license agreement and will be prosecuted by
 * civil and criminal law.
 *
 * @copyright  2016 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 */

namespace WebserviceBundle\Tests\Services\UserSearch\Advanced\DataProvider\Doctrine\QueryBuilder;

use Sso\WebserviceBundle\Services\UserSearch\Advanced\DataProvider\Doctrine\QueryBuilder\Helper\Transformer;
use Sso\WebserviceBundle\Services\UserSearch\Advanced\DataProvider\Doctrine\QueryBuilder\QueryBuilder;
use Sso\WebserviceBundle\Services\UserSearch\Advanced\RequestData as Request;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

/**
 * Class HandlerTest
 *
 * @copyright  2016 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 * @package WebserviceBundle\Tests\Services\UserSearch\Advanced\DataProvider\Doctrine
 */
class QueryBuilderTest extends KernelTestCase
{
    /**
     * @var QueryBuilder
     */
    private $queryBuilder;
    /**
     * @var \Doctrine\ORM\EntityManager
     */
    private $em;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * {@inheritDoc}
     */
    protected function setUp()
    {
        self::bootKernel();

        $this->em = static::$kernel->getContainer()
            ->get('doctrine')
            ->getManager('Webservice');

        $this->logger = $this->getMockBuilder(LoggerInterface::class)->getMock();

        $this->queryBuilder = new QueryBuilder($this->em, $this->logger, new Transformer());
    }

    /**
     * Call protected/private method of a class.
     *
     * @param object &$object Instantiated object that we will run method on.
     * @param string $methodName Method name to call
     * @param array $parameters Array of parameters to pass into method.
     *
     * @return mixed Method return.
     */
    public function invokeMethod(&$object, $methodName, array $parameters = array())
    {
        $reflection = new \ReflectionClass(get_class($object));
        $method = $reflection->getMethod($methodName);
        $method->setAccessible(true);

        return $method->invokeArgs($object, $parameters);
    }

    public function testSimpleQueryResult()
    {
        $requestDTO = $this->getSimpleRegExDTO();
        $result = $this->invokeMethod($this->queryBuilder, 'buildConditions', array($requestDTO));

        $sql = [
            'SELECT COUNT(*) c',
            'FROM `user` u',
            $result,
        ];

        $this->assertEquals($this->simpleRegExSQL(), implode(' ', $sql));
    }


    public function testComplexQueryResult()
    {
        $requestDTO = $this->getComplexDTO();
        $result = $this->invokeMethod($this->queryBuilder, 'buildConditions', array($requestDTO));

        $sql = [
            'SELECT COUNT(*) c',
            'FROM `user` u',
            $result,
        ];

        $this->assertEquals($this->complexSQL(), implode(' ', $sql));
    }

    /**
     * @return Request\UserSearch
     */
    private function getComplexDTO()
    {
        $userSearch = new Request\UserSearch();

        $userSearch->setLimit(10);
        $userSearch->setOffset(10);

        $attribute = new Request\AttributeType();
        $attribute->setName('foo');
        $attribute->setValue('bar');

        $app = new Request\ApplicationType();
        $app->setName('App4711');
        $app->setAttributeType($attribute);

        $filter = new Request\Filter();
        $filter->setApplicationType($app);
        $filter->setOperator('AND');
        $filter->setStrategy('EQ');

        $userSearch->addFilter($filter);

        $user = new Request\UserType();
        $user->setUsername('Oli42');

        $filter = new Request\Filter();
        $filter->setUserType($user);
        $filter->setOperator('AND');
        $filter->setStrategy('EQ');

        $userSearch->addFilter($filter);

        $user = new Request\UserType();
        $user->setLastname('Fri');

        $filter = new Request\Filter();
        $filter->setUserType($user);
        $filter->setOperator('AND');
        $filter->setStrategy('EQ');

        $userSearch->addFilter($filter);

        $user = new Request\UserType();
        $user->setEmail('Oli42@example.com');

        $filter = new Request\Filter();
        $filter->setUserType($user);
        $filter->setOperator('OR');
        $filter->setStrategy('EQ');

        $userSearch->addFilter($filter);

        $user = new Request\UserType();
        $user->setFirstname('Oli');

        $filter = new Request\Filter();
        $filter->setUserType($user);
        $filter->setOperator('AND');
        $filter->setStrategy('EQ');

        $userSearch->addFilter($filter);


        $subFilter = new Request\Filter();
        $subFilter->setOperator('OR');

        $userSearch->addFilter($subFilter);


        $user->setUsername('Oli42');

        $filter = new Request\Filter();
        $filter->setUserType($user);
        $filter->setOperator('AND');
        $filter->setStrategy('EQ');

        $subFilter->addFilter($filter);


        $user = new Request\UserType();
        $user->setUsername('Oli42');

        $filter = new Request\Filter();
        $filter->setUserType($user);
        $filter->setOperator('AND');
        $filter->setStrategy('EQ');

        $subFilter->addFilter($filter);

        return $userSearch;
    }

    private function complexSQL()
    {
        return 'SELECT COUNT(*) c FROM `user` u LEFT JOIN `user_application` as ua1 USING (`user_id`) LEFT JOIN `user_application_attribute` as uaa1 ON (`ua1`.`id` = `uaa1`.`userapplication_id`) WHERE 1 AND (`ua1`.`application_name` = ? AND `uaa1`.`attribute_name` = ? AND `uaa1`.`attribute_value` = ?) AND `u`.`user_lowerusername` = ? AND `u`.`user_lastname` = ? OR `u`.`user_loweremail` = ? AND `u`.`user_lowerusername` = ? OR (1 AND `u`.`user_lowerusername` = ? AND `u`.`user_lowerusername` = ?)';
    }

    /**
     * @return Request\UserSearch
     */
    private function getSimpleRegExDTO()
    {
        $userSearch = new Request\UserSearch();

        $userSearch->setLimit(10);
        $userSearch->setOffset(10);


        $user = new Request\UserType();
        $user->setUsername('Stefan');

        $filter = new Request\Filter();
        $filter->setUserType($user);
        $filter->setOperator('AND');
        $filter->setStrategy('REGEX');

        $userSearch->addFilter($filter);

        return $userSearch;
    }

    private function simpleRegExSQL()
    {
        return 'SELECT COUNT(*) c FROM `user` u WHERE 1 AND `u`.`user_lowerusername` REGEXP ?';
    }
    /**
     * {@inheritDoc}
     */
    protected function tearDown()
    {
        parent::tearDown();

        $this->em->close();
        $this->em = null; // avoid memory leaks
    }
}
