<?php declare(strict_types=1);

namespace Newland\NeosFiltering\Tests\Unit\Items;

use Newland\NeosFiltering\Items\Root;
use Newland\NeosFiltering\Items\SelectBox;
use Newland\NeosFiltering\Tests\Factory\ExampleEntityFactory;
use Newland\NeosFiltering\Tests\Fixture\DataSource\ExampleDataSource;

class SelectBoxTest extends ItemTestCase
{
    /** @var SelectBox */
    protected $subject;

    public function setUp(): void
    {
        parent::setUp();

        $this->subject = new SelectBox();
        $this->subject->setRoot(new Root());
        $this->subject->setDatabaseColumn('entity.database_column');
        $this->subject->setQueryString('query_string');
        $this->subject->setTitle('Title');
        $this->subject->setDataSource(new ExampleDataSource());
        $this->subject->setDataSourceArguments(
            [
                [ 'label' => 'Foo', 'value' => 'foo' ],
                [ 'label' => 'Bar', 'value' => 'bar' ],
                [ 'label' => 'Baz', 'value' => 'baz' ],
            ]
        );
    }

    public function testRendersSelect(): void
    {
        $result = $this->renderSubject();
        $this->assertContains('<select', $result);
        $this->assertContains('name="query_string"', $result);
    }

    public function testRendersOptionsForEachDataItem(): void
    {
        $result = $this->renderSubject();
        $this->assertContains('<option value="foo"', $result);
        $this->assertContains('<option value="bar"', $result);
        $this->assertContains('<option value="baz"', $result);
    }

    public function testSelectsOptionAccordingToState(): void
    {
        $this->subject->setState('foo');
        $result = $this->renderSubject();
        $this->assertRegExp('/\<option value="foo"(.|\n)*selected/m', $result);
    }

    public function testUsesLabelsFromDataSource(): void
    {
        $this->subject->setDataSource(new ExampleDataSource());
        $this->subject->setDataSourceArguments(
            [
                [ 'label' => 'LabelFirst', 'value' => 'first' ],
                [ 'label' => 'LabelSecond', 'value' => 'second' ],
                [ 'label' => 'LabelThird', 'value' => 'third' ],
            ]
        );


        $rendered = $this->renderSubject();
        $this->assertContains('LabelFirst', $rendered);
        $this->assertContains('LabelSecond', $rendered);
        $this->assertContains('LabelThird', $rendered);
    }

    public function testRendersTitle(): void
    {
        $this->subject->setTitle('THIS IS A TITLE');
        $this->assertContains('THIS IS A TITLE', $this->renderSubject());
    }

    public function testDoesNotCreateExpressionIfStateEmpty(): void
    {
        $this->subject->setState('');
        $expression = $this->subject->queryExpression($this->entityManager->getExpressionBuilder());
        $this->assertNull($expression->having);
        $this->assertNull($expression->where);
    }

    public function testFindsSpecificThingIfStateGiven(): void
    {
        (new ExampleEntityFactory($this->objectManager))->createMultiple(2, [ 'tag' => 'foo' ]);
        (new ExampleEntityFactory($this->objectManager))->createMultiple(2, [ 'tag' => 'bar' ]);

        $this->subject->setDatabaseColumn('entity.tag');
        $this->subject->setState('foo');
        $this->assertCount(2, $this->queryForSubject()->getQuery()->execute());
    }


    public function testUsesCustomQueryStringInRenderedOutput(): void
    {
        $this->subject->setDataSource(new ExampleDataSource());
        $this->subject->setDatabaseColumn('entity.tag');
        $this->subject->setDataSourceArguments(
            [
                [ 'label' => 'First tag', 'value' => 'first', 'queryString' => '__first__' ],
                [ 'label' => 'Second tag', 'value' => 'second', 'queryString' => '__second__' ],
                [ 'label' => 'Third tag', 'value' => 'third', 'queryString' => '__third__' ],
            ]
        );

        $this->subject->setState('__first__');
        $this->assertContains('value="__first__"', $this->renderSubject());
        $this->assertContains('value="__second__"', $this->renderSubject());
        $this->assertContains('value="__third__"', $this->renderSubject());
    }

    public function testAllowsSettingStateUsingValueEventIfUsingCustomQueryString(): void
    {
        $this->subject->setDataSource(new ExampleDataSource());
        $this->subject->setDataSourceArguments(
            [
                [ 'label' => 'First tag', 'value' => 'first', 'queryString' => '__first__' ],
                [ 'label' => 'Second tag', 'value' => 'second', 'queryString' => '__second__' ],
                [ 'label' => 'Third tag', 'value' => 'third', 'queryString' => '__third__' ],
            ]
        );

        $this->subject->setState('first');
        $this->assertEquals('__first__', $this->subject->getState()['queryString'] ?? null);
    }

    public function testCorrectlyGetsResultsWhenUsingCustomQueryStrings(): void
    {
        $first = (new ExampleEntityFactory($this->objectManager))->create([ 'tag' => 'first' ]);
        $second = (new ExampleEntityFactory($this->objectManager))->create([ 'tag' => 'second' ]);

        // Notice how tag=fourth exists but is not in the data source
        $this->subject->setDataSource(new ExampleDataSource());
        $this->subject->setDatabaseColumn('entity.tag');
        $this->subject->setDataSourceArguments(
            [
                [ 'label' => 'First tag', 'value' => 'first', 'queryString' => '__first__' ],
                [ 'label' => 'Second tag', 'value' => 'second', 'queryString' => '__second__' ],
                [ 'label' => 'Third tag', 'value' => 'third', 'queryString' => '__third__' ],
            ]
        );

        $this->subject->setState('__first__');
        $ids = array_map(
            function ($entity) {
                return $entity->uuid;
            },
            $this->queryForSubject()->getQuery()->execute()
        );

        $this->assertContains($first->uuid, $ids);
        $this->assertNotContains($second->uuid, $ids);
    }


    public function testSuppliesStatusIndicatorsContainingLabel(): void
    {
        $this->subject->setDataSource(new ExampleDataSource());
        $this->subject->setDataSourceArguments(
            [
                [ 'label' => 'First tag', 'value' => 'first', 'queryString' => '__first__' ],
                [ 'label' => 'Second tag', 'value' => 'second', 'queryString' => '__second__' ],
                [ 'label' => 'Third tag', 'value' => 'third', 'queryString' => '__third__' ],
            ]
        );
        $this->subject->setState('__first__');

        $indicators = $this->subject->getStatusIndicators();
        $this->assertCount(1, $indicators);
        $this->assertEquals('First tag', $indicators[0]->getTitle());
    }

    public function testUsesSameIdsForCheckboxesAsForStatusIndicators(): void
    {
        $this->subject->setDataSource(new ExampleDataSource());
        $this->subject->setDataSourceArguments(
            [
                [ 'label' => 'First tag', 'value' => 'first', 'queryString' => '__first__' ],
                [ 'label' => 'Second tag', 'value' => 'second', 'queryString' => '__second__' ],
                [ 'label' => 'Third tag', 'value' => 'third', 'queryString' => '__third__' ],
            ]
        );
        $this->subject->setState('second');

        $indicators = $this->subject->getStatusIndicators();
        $rendered = $this->renderSubject();

        $this->assertContains($indicators[0]->getId(), $rendered);
    }

    public function testDoesNotRenderSameItemTwiceIfInMultipleGroups(): void
    {
        $this->subject->setDataSource(new ExampleDataSource());
        $this->subject->setDataSourceArguments(
            [
                [ 'label' => 'First tag', 'value' => 'first', 'group' => 'foo' ],
                [ 'label' => 'First tag', 'value' => 'first', 'group' => 'bar' ],
                [ 'label' => 'Second tag', 'value' => 'second', 'group' => 'foo' ],
            ]
        );

        $rendered = $this->renderSubject();
        $this->assertContainsCount('value="first"', 1, $rendered);
    }

    public function testDoesNotProvideMultipleFitlerItemsForItemsInSameGroup(): void
    {
        $this->subject->setDataSource(new ExampleDataSource());
        $this->subject->setDataSourceArguments(
            [
                [ 'label' => 'First tag', 'value' => 'first', 'group' => 'foo' ],
                [ 'label' => 'First tag', 'value' => 'first', 'group' => 'bar' ],
                [ 'label' => 'Second tag', 'value' => 'second', 'group' => 'foo' ],
            ]
        );


        $this->subject->setState([ 'first' ]);
        $indicators = $this->subject->getStatusIndicators();
        $this->assertCount(1, $indicators);
    }
}
