<?php

namespace FCA\StockApi\Tests\Unit\Collection\Filter;

use FCA\StockApi\Collection\Filter\Builder;
use FCA\StockApi\Exception\ApiException;
use PHPUnit\Framework\TestCase;

/**
 * Class BuilderTest
 * @package FCA\StockApi\Tests\Unit\Collection\Filter
 */
class BuilderTest extends TestCase
{
    /**
     * Testy metody is z poprawnymi argumentami
     * @throws ApiException
     */
    public function testIsMethodWithValidArguments()
    {
        $b = new Builder();
        $this->assertEquals([Builder\Fields::ID => ['$lte' => 152]], $b->is(Builder\Fields::ID, "<=", 152));
        $this->assertEquals([Builder\Fields::ID => ['$lt' => 152]], $b->is(Builder\Fields::ID, "<", 152));
        $this->assertEquals([Builder\Fields::ID => ['$gte' => 152]], $b->is(Builder\Fields::ID, ">=", 152));
        $this->assertEquals([Builder\Fields::ID => ['$gt' => 152]], $b->is(Builder\Fields::ID, ">", 152));
        $this->assertEquals([Builder\Fields::ID => 152], $b->is(Builder\Fields::ID, "=", 152));
        $this->assertEquals([Builder\Fields::ID => ['$ne' => 152]], $b->is(Builder\Fields::ID, "!=", 152));
        $this->assertEquals([Builder\Fields::ID => ['$in' => [152, 153]]], $b->is(Builder\Fields::ID, "in", [152, 153]));
        $this->assertEquals([Builder\Fields::ID => ['$nin' => [152, 153]]], $b->is(Builder\Fields::ID, "!in", [152, 153]));
    }

    /**
     * Trest metody is z niepoprawnym typem danych
     * @throws ApiException
     */
    public function testIsMethodWithInvalidValueType()
    {
        $b = new Builder();

        $this->expectException(ApiException::class);
        $b->is(Builder\Fields::ID, "=", "152");
    }

    /**
     * Test metody is z niepoprawnym operatorem
     * @throws ApiException
     */
    public function testIsMethodWithInvalidOperator()
    {
        $b = new Builder();

        $this->expectException(ApiException::class);
        $b->is(Builder\Fields::ID, "==", 152);
    }

    /**
     * Test metody is z niepoprawną nazwą pola
     * @throws ApiException
     */
    public function testIsMethodWithInvalidField()
    {
        $b = new Builder();

        $this->expectException(ApiException::class);
        $b->is("bad.field", "=", "54");
    }

    /**
     * Test metody not
     * @throws ApiException
     */
    public function testNotMethod()
    {
        $b = new Builder();
        $filter = $b->is(Builder\Fields::ID, "=", 152);
        $this->assertEquals([Builder\Fields::ID => ['$not' => 152]], $b->not($filter));
    }

    /**
     * Test metody andWhere bez argumentów
     * @throws ApiException
     */
    public function testAndWhereMethodWithoutArgument()
    {
        $b = new Builder();

        $this->assertEquals([], $b->andWhere());
    }

    /**
     * Test metody andWhere z jednym argumentem
     * @depends testIsMethodWithValidArguments
     * @throws ApiException
     */
    public function testAndWhereMethodWithOneArgument()
    {
        $b = new Builder();
        $tmpFilter = $b->is(Builder\Fields::ID, "<=", 152);

        $filter = $b->andWhere(
            $tmpFilter
        );

        $this->assertEquals($tmpFilter, $filter);
    }

    /**
     * Test metody andWhere z kilkoma argumentami
     * @depends testIsMethodWithValidArguments
     * @throws ApiException
     */
    public function testAndWhereMethodWithMoreArguments()
    {
        $b = new Builder();
        $filter = $b->andWhere(
            $b->is(Builder\Fields::ID, "<=", 152),
            $b->is(Builder\Fields::ID, "=", 152)
        );

        $array = [
            '$and' =>
                [
                    [Builder\Fields::ID => ['$lte' => 152]],
                    [Builder\Fields::ID => 152],
                ]
        ];

        $this->assertEquals($array, $filter);
    }

    /**
     * Test metody andWhere ze złym argumentem
     * @depends testIsMethodWithValidArguments
     * @throws ApiException
     */
    public function testAndWhereMethodWithBadArgument()
    {
        $b = new Builder();
        $this->expectException(ApiException::class);
        $b->andWhere(
            $b->is(Builder\Fields::ID, "<=", 152),
            [Builder\Fields::ID => ['$bad' => 16564]]
        );
    }

    /**
     * Test metody andWhere ze złym argumentem
     * @depends testIsMethodWithValidArguments
     * @throws ApiException
     */
    public function testAndWhereMethodWithBadArgument2()
    {
        $b = new Builder();

        $this->expectException(ApiException::class);
        $b->andWhere(
            $b->is(Builder\Fields::ID, "<=", 152),
            [Builder\Fields::ID => "16564"]
        );
    }


    /**
     * Test metody orWhere bez argumentów
     * @throws ApiException
     */
    public function testOrWhereMethodWithoutArgument()
    {
        $b = new Builder();

        $this->assertEquals([], $b->orWhere());
    }

    /**
     * Test metody orWhere z jednym argumentem
     * @depends testIsMethodWithValidArguments
     * @throws ApiException
     */
    public function testOrWhereMethodWithOneArgument()
    {
        $b = new Builder();
        $tmpFilter = $b->is(Builder\Fields::ID, "<=", 152);

        $filter = $b->orWhere(
            $tmpFilter
        );

        $this->assertEquals($tmpFilter, $filter);
    }

    /**
     * Test metody orWhere z wieloma argumentami
     * @depends testIsMethodWithValidArguments
     * @throws ApiException
     */
    public function testOrWhereMethodWithMoreArguments()
    {
        $b = new Builder();
        $filter = $b->orWhere(
            $b->is(Builder\Fields::ID, "<=", 152),
            $b->is(Builder\Fields::ID, "=", 152)
        );

        $array = [
            '$or' =>
                [
                    [Builder\Fields::ID => ['$lte' => 152]],
                    [Builder\Fields::ID => 152],
                ]
        ];

        $this->assertEquals($array, $filter);
    }

    /**
     * Test metody orWhere ze złym argumentem
     * @depends testIsMethodWithValidArguments
     * @throws ApiException
     */
    public function testOrWhereMethodWithBadArgument()
    {
        $b = new Builder();
        $this->expectException(ApiException::class);
        $b->orWhere(
            $b->is(Builder\Fields::ID, "<=", 152),
            [Builder\Fields::ID => ['$bad' => 16564]]
        );
    }

    /**
     * Test metody orWhere ze złym argumentem
     * @depends testIsMethodWithValidArguments
     * @throws ApiException
     */
    public function testOrWhereMethodWithBadArgument2()
    {
        $b = new Builder();

        $this->expectException(ApiException::class);
        $b->orWhere(
            $b->is(Builder\Fields::ID, "<=", 152),
            [Builder\Fields::ID => "16564"]
        );
    }

    /**
     * @throws ApiException
     */
    public function testIsNear()
    {
        $b = new Builder();
        $f = $b->isNear(Builder\Fields::DEALER_COORDINATES, 51.256145, 19.15641, 100);
        $array = [
            Builder\Fields::DEALER_COORDINATES => [
                '$near' => [
                    '$geometry' => ['type' => "Point", 'coordinates' => [51.256145, 19.15641]],
                    '$maxDistance' => 100
                ]
            ]
        ];

        $this->assertEquals($array, $f);

        // Podanie nieprawidłowego pola powinno wyrzucić wyjątek
        $this->expectException(ApiException::class);
        $f = $b->isNear(Builder\Fields::DEALER_CITY, 51.256145, 19.15641, 100);
    }
}
