<?php

namespace QueryTest;

use Doctrine\DBAL\Query\QueryBuilder;
use Query\Operator;
use Query\Operator as Op;

class OperatorTest extends \DatabaseTestCase
{
    public function testAndX()
    {
        $noName = function (QueryBuilder $qb) {
            return $this->expr()->isNull('alias.name');
        };

        $hasPrice = function (QueryBuilder $qb) {
            return $this->expr()->isNotNull('alias.price');
        };

        $andExpr = Op::andX($noName, $hasPrice);
        $where = $andExpr(sqlQueryBuilder());

        $this->assertSame('(alias.name IS NULL) AND (alias.price IS NOT NULL)', (string) $where);
    }

    /**
     * @dataProvider dataSet_testBetween
     */
    public function testBetween($min, $max, $expected)
    {
        $between = Op::between('field', new \Range($min, $max));
        $where = sqlQueryBuilder()->evaluateClosures([$between])[0];

        $this->assertRegExp("@{$expected}@", (string) $where);
    }

    public function dataSet_testBetween()
    {
        return [
            [1, 2, '\(field >= :between_min_\d+\) AND \(field <= :between_max_\d+\)'],
            [1, null, 'field >= :between_min_\d+'],
            [null, 2, 'field <= :between_max_\d+'],
            [null, null, ''],
        ];
    }

    /**
     * @expectedException \InvalidArgumentException
     */
    public function testFindInSetThrows()
    {
        Operator::findInSet([1, 2, 3], 'column', 'x');
    }

    public function testFindInSetAnd()
    {
        $expr = Operator::findInSet([1, 2], 'column', 'AND');

        $this->assertRegExp(
            '@\(FIND_IN_SET\(:needle_\d+, `column`\)\) AND \(FIND_IN_SET\(:needle_\d+, `column`\)\)@',
            (string) sqlQueryBuilder()->evaluateClosures([$expr])[0]
        );
    }

    public function testFindInSetOr()
    {
        $expr = Operator::findInSet([1, 2], 'column', 'OR');

        $this->assertRegExp(
            '@\(FIND_IN_SET\(:needle_\d+, `column`\)\) OR \(FIND_IN_SET\(:needle_\d+, `column`\)\)@',
            (string) sqlQueryBuilder()->evaluateClosures([$expr])[0]
        );
    }

    public function testNot()
    {
        $isNull = function (QueryBuilder $qb) {
            return $this->expr()->isNull('alias.name');
        };
        $expr = Operator::not($isNull);
        $this->assertSame(
            'NOT (alias.name IS NULL)',
            (string) sqlQueryBuilder()->evaluateClosures([$expr])[0]
        );
    }

    private function expr()
    {
        return sqlQueryBuilder()->expr();
    }
}
