<?php declare(strict_types=1);

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

use Doctrine\Common\Collections\ArrayCollection;
use Neos\ContentRepository\Domain\Model\Node;
use Neos\Flow\Http\Response;
use Newland\Toubiz\Api\ObjectAdapter\Concern\ArticleConstants;
use Newland\Toubiz\Poi\Neos\Tests\Integration\IntegrationTestCase;
use Newland\Toubiz\Sync\Neos\Domain\Model\Article;
use Newland\Toubiz\Sync\Neos\Tests\Factory\MediaFactory;
use Newland\Toubiz\Sync\Neos\Tests\Factory\UriFactory;
use Ramsey\Uuid\Uuid;

abstract class AbstractDetailTestCase extends IntegrationTestCase
{

    protected abstract function articleType(): int;

    protected function article(array $attributes = []): Article
    {
        $attributes['mainType'] = $this->articleType();
        return $this->articleFactory->create($attributes);
    }

    protected function articleUrl(Article $article): string
    {
        return $this->articleUrlService->generateUrlByCurrentNode($article, $this->node);
    }

    protected function requestArticleDetailPage(Article $article): Response
    {
        return $this->browser->request(
            $this->articleUrl($article)
        );
    }

    public function testDoesNotThrowError(): void
    {
        $response = $this->requestArticleDetailPage($this->article());
        $this->assertResponseOk($response);
        $this->assertResponseNotContains('Exception.*', $response);
    }

    public function testDisplaysBookingLink(): void
    {
        if ($this->articleType() === ArticleConstants::TYPE_CITY) {
            $this->assertTrue(true);
            return;
        }

        $response = $this->requestArticleDetailPage($this->article([
            'bookingUris' => [
                (new UriFactory($this->objectManager))->create([ 'label' => 'Jetzt buchen', 'uri' => 'https://google.com', 'originalId' => 'foo' ]),
                (new UriFactory($this->objectManager))->create([ 'label' => 'Jetzt informieren', 'uri' => 'https://yahoo.com', 'originalId' => 'bar' ]),
            ]
        ]));

        $this->assertResponseOk($response);
        $this->assertResponseContains('Jetzt buchen', $response);
        $this->assertResponseContains('href="https:\/\/google.com"', $response);
        $this->assertResponseContains('Jetzt informieren', $response);
        $this->assertResponseContains('href="https:\/\/yahoo.com"', $response);
    }
    public function testUrlsWithoutUrlIdentifiersRedirectCorrectly(): void
    {
        $article = $this->article([ 'language' => 'de', 'urlIdentifier' => md5(random_bytes(32)) ]);
        $url = $this->articleUrl($article);
        $urlWithoutHash = str_replace('-' . $article->getUrlIdentifier(), '', $url);

        $this->browser->setFollowRedirects(false);
        $response = $this->browser->request($urlWithoutHash);

        $this->assertEquals(301, $response->getStatusCode());
        $this->assertEquals($url, $response->getHeader('Location'));
    }

    public function testUrlsWithIncorrectPathRedirectToCorrectUrlIdentifier(): void
    {
        $article = $this->article([ 'language' => 'de', 'urlIdentifier' => md5(random_bytes(32)) ]);
        $url = $this->articleUrl($article);

        $urlParts = explode('/', $url);
        $urlParts[count($urlParts) - 1] = 'foo-bar-' . $article->getUrlIdentifier();
        $urlWithIncorrectNamePart = implode('/', $urlParts);

        $this->browser->setFollowRedirects(false);
        $response = $this->browser->request($urlWithIncorrectNamePart);

        $this->assertEquals(301, $response->getStatusCode());
        $this->assertEquals($url, $response->getHeader('Location'));
    }
    public function testDoesNotShowHiddenArticles(): void
    {
        $hidden = $this->article([ 'hidden' => true ]);
        $response = $this->requestArticleDetailPage($hidden);
        $this->assertEquals(404, $response->getStatusCode());
    }

    public function testContainsLanguageMenuLinkToAlternativeLanguage(): void
    {
        $this->createNodeLanguageVariants($this->siteNode, [ 'en', 'fr' ]);

        $id = (string) Uuid::uuid4();
        $german = $this->article([ 'languageGrouping' => $id, 'name' => 'the-german-one', 'language' => 'de', 'urlIdentifier' => md5(random_bytes(32)) ]);
        $english = $this->article([ 'languageGrouping' => $id, 'name' => 'the-english-one', 'language' => 'en', 'urlIdentifier' => md5(random_bytes(32)) ]);
        $french = $this->article([ 'languageGrouping' => $id, 'name' => 'the-french-one', 'language' => 'fr', 'urlIdentifier' => md5(random_bytes(32)) ]);

        $response = $this->requestArticleDetailPage($german);
        $this->assertResponseOk($response);

        $this->assertResponseContains(
            sprintf('<a href=".*?%s"', $german->getUrlIdentifier()),
            $response,
            'Should contain language navigation link to german version'
        );
        $this->assertResponseContains(
            sprintf('<a href=".*?%s"', $english->getUrlIdentifier()),
            $response,
            'Should contain language navigation link to english version'
        );
        $this->assertResponseContains(
            sprintf('<a href=".*?%s"', $french->getUrlIdentifier()),
            $response,
            'Should contain language navigation link to french version'
        );
    }

    public function testContainsHrefLangLinkToAlternativeLanguage(): void
    {
        $this->createNodeLanguageVariants($this->siteNode, [ 'en', 'fr' ]);

        $id = (string) Uuid::uuid4();
        $german = $this->article([ 'originalId' => $id, 'name' => 'the-german-one', 'language' => 'de', 'urlIdentifier' => md5(random_bytes(32)) ]);
        $english = $this->article([ 'originalId' => $id, 'name' => 'the-english-one', 'language' => 'en', 'urlIdentifier' => md5(random_bytes(32)) ]);
        $french = $this->article([ 'originalId' => $id, 'name' => 'the-french-one', 'language' => 'fr', 'urlIdentifier' => md5(random_bytes(32)) ]);

        $response = $this->requestArticleDetailPage($german);
        $this->assertResponseOk($response);

        $this->assertResponseContains(
            sprintf('<link rel="alternate" hreflang="%s" href=".*?%s"', 'de', $german->getUrlIdentifier()),
            $response,
            'Should contain hreflang link to german version'
        );
        $this->assertResponseContains(
            sprintf('<link rel="alternate" hreflang="%s" href=".*?%s"', 'en', $english->getUrlIdentifier()),
            $response,
            'Should contain hreflang link to english version'
        );
        $this->assertResponseContains(
            sprintf('<link rel="alternate" hreflang="%s" href=".*?%s"', 'fr', $french->getUrlIdentifier()),
            $response,
            'Should contain hreflang link to french version'
        );
    }


    public function testRendersSlots(): void
    {
        $article = $this->article();

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

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

    public function provideSlotsThatShouldExist(): array
    {
        return [
            [ 'aboveAll' ],
            [ 'belowAll' ],
            [ 'aboveTitle' ],
            [ 'belowTitle' ],
            [ 'aboveDetail' ],
            [ 'belowDetail' ],
            [ 'aboveDetailGallery' ],
            [ 'belowDetailGallery' ],
            [ 'aboveDetailMainContent' ],
            [ 'belowDetailMainContent' ],
            [ 'aboveDetailAside' ],
            [ 'belowDetailAside' ],
        ];
    }
}
