<?php declare(strict_types=1);

namespace Newland\Toubiz\Poi\Neos\Tests\Integration\Lists;

use Neos\ContentRepository\Domain\Model\NodeData;
use Newland\Toubiz\Api\ObjectAdapter\Concern\ArticleConstants;
use Newland\Toubiz\Poi\Neos\Tests\Integration\IntegrationTestCase;
use Newland\Toubiz\Sync\Neos\Domain\Model\Article;

abstract class AbstractListTestCase extends IntegrationTestCase
{
    protected abstract function articleType(): int;

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

        $this->node->setNodeType($this->nodeTypeManager->getNodeType('Newland.Toubiz.Poi.Neos:List'));
        $this->node->setProperty('articleType', $this->articleType());
        $this->persistNode($this->node);
    }

    public function testRendersListWithAnyMoreConfigurationToIt(): void
    {
        $articles = [];
        for($i = 0; $i < 10; $i++) {
            $articles[] = $this->articleFactory->create([ 'mainType' => $this->articleType(), 'name' => 'article' . $i ]);
        }

        $response = $this->browser->request($this->nodeUri);
        $this->assertResponseOk($response);

        foreach ($articles as $article) {
            /** @var Article $article */
            $this->assertResponseContains($article->getName(), $response, sprintf('List of type %s should contain all articles of that type', $this->articleType()));
        }
    }

    public function testDoesNotRenderArticlesOfOtherTypes(): void
    {
        $articles = [];
        foreach (ArticleConstants::ALL_TYPES as $type) {
            $articles[$type] = $this->articleFactory->create([ 'mainType' => $type, 'name' => 'article' . $type ]);
        }

        $response = $this->browser->request($this->nodeUri);
        $this->assertResponseOk($response);

        foreach ($articles as $type => $article) {
            /** @var Article $article */
            $stringToMatch = sprintf('<h3.*?>\s*%s\s*<\/h3>', $article->getName());
            if ($type === $this->articleType()) {
                $this->assertResponseContains($stringToMatch, $response, sprintf('List of type %s should contain title of article of same type', $type));
            } else {
                $this->assertResponseNotContains($stringToMatch, $response, sprintf('List of type %s should not contain article of type %s', $this->articleType(), $type));
            }
        }
    }

    public function testContainsDetailLinksOfArticles(): void
    {
        $articles = $this->articleFactory->createMultiple(10, [ 'mainType' => $this->articleType() ]);

        $response = $this->browser->request($this->nodeUri());
        $this->assertResponseOk($response);

        foreach ($articles as $article) {
            /** @var Article $article */
            $url = $this->articleUrlService->generateUrlByCurrentNode($article, $this->node);
            $this->assertResponseContains(preg_quote($url, '/'), $response, sprintf('List of type %s should contain all articles of that type', $this->articleType()));
        }
    }

    public function testDoesNotContainHiddenArticles(): void
    {
        $visible = $this->articleFactory->create([ 'mainType' => $this->articleType(), 'hidden' => false, 'name' => 'visible_' . md5(random_bytes(32)) ]);
        $hidden = $this->articleFactory->create([ 'mainType' => $this->articleType(), 'hidden' => true, 'name' => 'hidden_' . md5(random_bytes(32)) ]);

        $response = $this->browser->request($this->nodeUri());
        $this->assertResponseOk($response);

        $this->assertResponseContains($visible->getName(), $response);
        $this->assertResponseNotContains($hidden->getName(), $response);
    }

    public function testRendersSlots(): void
    {
        $this->articleFactory->createMultiple(1, [ 'mainType' => $this->articleType() ]);
        $response = $this->browser->request($this->nodeUri());
        $this->assertResponseOk($response);

        foreach ($this->provideSlotsThatShouldExist() as [ $slot ]) {
            $this->assertResponseContains(sprintf('Slot(%s)', $slot), $response, null, false);
        }
    }

    public function provideSlotsThatShouldExist(): array
    {
        return [
            [ 'aboveAll' ],
            [ 'belowAll' ],
            [ 'aboveTeaser' ],
            [ 'belowTeaser' ],
            [ 'aboveTitle' ],
            [ 'belowTitle' ],
            [ 'aboveTeaserTitle' ],
            [ 'belowTeaserTitle' ],
            [ 'aboveImage' ],
            [ 'belowImage' ],
            [ 'aboveTeaserImage' ],
            [ 'belowTeaserImage' ],
        ];
    }
}
