<?php

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

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

class ValidatorTest extends TestCase
{
    /**
     * Testy metody isValidValue
     */
    public function testIsValidValueMethod()
    {
        $this->assertTrue(Validator::isValidValue(Fields::ID, 15));
        $this->assertFalse(Validator::isValidValue(Fields::ID, "15"));
    }

    /**
     * Testy metody isValidField
     */
    public function testIsValidFieldMethod()
    {
        $this->assertTrue(Validator::isValidField(Fields::ID));
        $this->assertFalse(Validator::isValidField('bad.field'));
    }



    /**
     * Testy metody validateFilter z poprawnymi filtrami
     * @throws ApiException
     */
    public function testValidFilterMethod()
    {
        $this->assertTrue(Validator::validateFilter((array)null));
        $this->assertTrue(Validator::validateFilter([]));
        $this->assertTrue(Validator::validateFilter([Fields::ID => 451]));
        $this->assertTrue(Validator::validateFilter([Fields::ID => ['$lt' => 451]]));
        $this->assertTrue(Validator::validateFilter([Fields::ID => 451, Fields::ID => 452]));
        $this->assertTrue(Validator::validateFilter(
            [
                '$and' =>
                    [
                        [Fields::ID => ['$lt' => 451]]
                    ]
            ]
        ));
        $this->assertTrue(Validator::validateFilter(
            [
                '$and' =>
                    [
                        [Fields::ID => ['$lt' => 451]],
                        [Fields::ID => ['$gt' => 300]]
                    ]
            ]
        ));
        $this->assertTrue(Validator::validateFilter(
            [
                '$or' =>
                    [
                        [Fields::ID => ['$lt' => 451]]
                    ]
            ]
        ));
        $this->assertTrue(Validator::validateFilter(
            [
                '$or' =>
                    [
                        [Fields::ID => ['$lt' => 451]],
                        [Fields::ID => ['$gt' => 300]]
                    ]
            ]
        ));

        $this->assertTrue(Validator::validateFilter(
            [
                Fields::ID => ['$not' => ['$lt' => 451]]
            ]
        ));

        $this->assertTrue(Validator::validateFilter(
            [
                Fields::ID => ['$not' => 451]
            ]
        ));

        $this->assertTrue(Validator::validateFilter(
            [
                Fields::ID => ['$not' => ['$not' => ['$not' => ['$not' => 451]]]]
            ]
        ));
    }

    /**
     * Pole powinno mieć poprawną nazwę
     * @throws ApiException
     */
    public function testValidFilterMethodWithBadField()
    {
        $this->expectException(ApiException::class);
        Validator::validateFilter(['bad.field' => 'value']);
    }

    /**
     * Wartość powinna mieć poprawny typ
     * @throws ApiException
     */
    public function testValidFilterMethodWithBadValue()
    {
        $this->expectException(ApiException::class);
        Validator::validateFilter([Fields::ID => 'bad.value']);
    }

    /**
     * Operator powinien być poprawny
     * @throws ApiException
     */
    public function testValidFilterMethodWithInvalidOperator()
    {
        $this->expectException(ApiException::class);
        Validator::validateFilter([Fields::ID => ['$ls' => 126]]);
    }

    /**
     * tablica '$and' powinna zawierać listę tablic
     * @throws ApiException
     */
    public function testValidFilterMethodWithBadAndArray()
    {
        $this->expectException(ApiException::class);
        Validator::validateFilter(
            [
                '$and' =>
                    [
                        Fields::ID => ['$lt' => 451]
                    ]
            ]
        );
    }

    /**
     * tablica '$and' powinna zawierać listę tablic
     * @throws ApiException
     */
    public function testValidFilterMethodWithBadAndArray2()
    {
        $this->expectException(ApiException::class);
        Validator::validateFilter(
            [
                '$and' =>
                    [
                        Fields::ID => ['$lt' => 451],
                        Fields::ID => ['$gt' => 300]
                    ]
            ]
        );
    }

    /**
     * tablica '$or' powinna zawierać listę tablic
     * @throws ApiException
     */
    public function testValidFilterMethodWithBadOrArray()
    {
        $this->expectException(ApiException::class);
        Validator::validateFilter(
            [
                '$or' =>
                    [
                        Fields::ID => ['$lt' => 451]
                    ]
            ]
        );
    }

    /**
     * tablica '$or' powinna zawierać listę tablic
     * @throws ApiException
     */
    public function testValidFilterMethodWithBadOrArray2()
    {
        $this->expectException(ApiException::class);
        Validator::validateFilter(
            [
                '$or' =>
                    [
                        Fields::ID => ['$gt' => 451],
                        Fields::ID => ['$lt' => 300]
                    ]
            ]
        );
    }

    /**
     * tablica '$not' nie powinna zawierać listy tablic
     * @throws ApiException
     */
    public function testValidFilterMethodWithBadNotArray()
    {
        $this->expectException(ApiException::class);
        $this->assertTrue(Validator::validateFilter(
            [
                '$not' =>
                    [
                        [Fields::ID => ['$lt' => 451]],
                        [Fields::ID => ['$gt' => 300]]
                    ]
            ]
        ));
    }

    /**
     * tablica '$not' nie powinna zawierać pustej tablicy
     * @throws ApiException
     */
    public function testValidFilterMethodWithBadNotArray2()
    {
        $this->expectException(ApiException::class);
        $this->assertTrue(Validator::validateFilter(
            [
                '$not' => []
            ]
        ));
    }

    /**
     * tablica '$not' nie powinna zawierać klauzuli $and
     * @throws ApiException
     */
    public function testValidFilterMethodWithBadNotArray3()
    {
        $this->expectException(ApiException::class);
        $this->assertTrue(Validator::validateFilter(
            [
                '$not' => [
                    '$and' => [
                        [Fields::ID => ['$lt' => 451]],
                        [Fields::ID => ['$gt' => 300]]
                    ]
                ]
            ]
        ));
    }

    /**
     * tablica '$not' nie powinna zawierać klauzuli $or
     * @throws ApiException
     */
    public function testValidFilterMethodWithBadNotArray4()
    {
        $this->expectException(ApiException::class);
        $this->assertTrue(Validator::validateFilter(
            [
                '$not' => [
                    '$or' => [
                        [Fields::ID => ['$lt' => 451]],
                        [Fields::ID => ['$gt' => 300]]
                    ]
                ]
            ]
        ));
    }

    public function testValidSortArray()
    {
        $this->assertTrue(Validator::isValidSortArray([Fields::ID => Sort::ASC]));
        $this->assertTrue(Validator::isValidSortArray([Fields::ID => Sort::ASC, Fields::FINAL_PRICE_NETTO => Sort::DESC]));
        $this->assertTrue(Validator::isValidSortArray(['random' => true]));

        $this->assertTrue(Validator::isValidSortArray([]));
        $this->assertFalse(Validator::isValidSortArray(['bad.field' => 'some value']));
        $this->assertFalse(Validator::isValidSortArray([Fields::ID => Sort::ASC, 'other' => 5]));
    }

    public function testInvalidSortArray1()
    {
        $this->expectException(\TypeError::class);
        Validator::isValidSortArray("bad.value");
    }

    public function testInvalidSortArray2()
    {
        $this->expectException(\TypeError::class);
        Validator::isValidSortArray(['bad.field' , Sort::ASC]);
    }

    public function testInvalidSortArray3()
    {
        $this->expectException("TypeError");
        Validator::isValidSortArray([Fields::ID, 5]);

    }
}
