<?php
namespace Newland\Toubiz\Poi\Neos\Controller;

/*
 * This file is part of the "toubiz-poi-neos" package.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 */

use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Property\Exception\TargetNotFoundException;
use Neos\Flow\Property\TypeConverter\Error\TargetNotFoundError;
use Neos\FluidAdaptor\View\StandaloneView;
use Newland\PageFrameProvider\Service\PageFrameLinkingService;
use Newland\Toubiz\Api\ObjectAdapter\ArticleAdapterInterface;
use Newland\Toubiz\Api\ObjectAdapter\Concern\ArticleConstants;
use Newland\Toubiz\Poi\Neos\Service\ArticleFilterService;
use Newland\Toubiz\Poi\Neos\Service\ArticleUrlService;
use Newland\Toubiz\Poi\Neos\Service\LinkListService;
use Newland\Toubiz\Poi\Neos\Service\WidgetService;
use Newland\Toubiz\Sync\Neos\Domain\Filter\ArticleFilter;
use Newland\Toubiz\Sync\Neos\Domain\Model\Article;
use Newland\ToubizWidgetRendering\Renderer\ToubizWidgetRenderer;

class PointOfInterestsController extends AbstractActionController
{

    /**
     * @var ArticleFilterService
     * @Flow\Inject()
     */
    protected $articleFilterService;

    /**
     * @var ArticleUrlService
     * @Flow\Inject()
     */
    protected $articleUrlService;

    /**
     * @var ToubizWidgetRenderer
     * @Flow\Inject()
     */
    protected $widgetRenderer;

    /**
     * @var LinkListService
     * @Flow\Inject()
     */
    protected $linkListService;

    /**
     * Teaser action.
     *
     * Renders a single teaser.
     *
     * @return void
     */
    public function teaserAction(): void
    {
        if (array_key_exists('article', $this->properties)) {
            $article = $this->articleRepository->find($this->properties['article']);
            if ($article) {
                $this->view->assign('article', $article);
            }
        }

        $this->view->assignMultiple(
            [
                'wrapStart' => $this->request->getInternalArgument('__wrapStart'),
                'wrapEnd' => $this->request->getInternalArgument('__wrapEnd'),
            ]
        );
    }

    /**
     * Renders the `Newland.Toubiz.Poi.Neos:TeaserList` NodeType.
     *
     * @return void
     * @deprecated since 1.13, will be disabled by default in 1.13 and removed in 1.15
     *
     */
    public function teaserListAction(): void
    {
        $filter = $this->createArticleFilter();
        $articles = $this->articleRepository->findByFilter($filter);
        $displayStyle = $this->properties['displayStyle'] ?? '';
        $this->view->assign('articles', $articles);
        $this->view->assign('enableSwiper', $displayStyle === 'swiper');
    }

    /** @return string|void */
    public function showAction(Article $article = null)
    {
        $this->redirectToStaticDetailPageIfNotOnIt($article);
        if ($article === null) {
            return '';
        }

        if ($article->getSourceSystem() === ArticleAdapterInterface::SOURCE_TOUBIZ) {
            /** @var NodeInterface $node */
            $node = $this->request->getInternalArgument('__node');
            $elementLinkData = $this->linkListService->getLinks(
                $node,
                $article,
                $this->getControllerContext()
            );

            $view = new StandaloneView();
            $view->setControllerContext($this->controllerContext);
            $view->setTemplate('ShowWidget');
            $view->assignMultiple(
                [
                    'article' => $article,
                    'component' => WidgetService::getDetailComponentName($article),
                    'elementLinkData' => $elementLinkData,
                ]
            );
            return $view->render();
        }

        $this->showActionLegacy($article);
    }


    private function showActionLegacy(Article $article): void
    {
        $insertGalleryAfterLinks = false;

        if ($this->properties['article'] ?? null) {
            $article = $this->articleRepository->find($this->properties['article']);

            if ($article) {
                $insertGalleryAfterLinks =
                    $article->getMainType() === ArticleConstants::TYPE_CITY &&
                    ($this->settings['cityDetails']['gallery']['insertAfterLinks'] ?? false);
            }
        }

        $this->view->assignMultiple(
            [
                'url' => (string) $this->request->getHttpRequest()->getUri(),
                'article' => $article,
                'similarTours' => $this->getSimilarToursIfApplicable($article),
                'insertGalleryAfterLinks' => $insertGalleryAfterLinks,
            ]
        );
    }

    /**
     * Creates a new article filter based on the given properties array
     * (or the properties of the current node if no argument is provided).
     *
     * @param array|null $properties
     * @return ArticleFilter
     */
    private function createArticleFilter(array $properties = null): ArticleFilter
    {
        $properties = $properties ?: $this->properties;

        $filter = new ArticleFilter();
        $filter->setCurrentSite($this->currentSite->getNodeName());

        if (array_key_exists('topicFilter', $properties)) {
            $filter = $this->articleFilterService->setFilterFromTopics($properties['topicFilter'], $filter);
        } elseif (array_key_exists('categoryFilter', $properties)) {
            $filter = $this->articleFilterService->setFilterFromTopics($properties['categoryFilter'], $filter);
        } elseif (array_key_exists('categories', $properties) && is_array($properties['categories'])) {
            $filter->setCategories($properties['categories']);
        }

        if (array_key_exists('recordLimit', $properties)) {
            $filter->setLimit($properties['recordLimit']);
        }

        if (array_key_exists('articleType', $properties)) {
            $filter->setMainType((int) $properties['articleType']);
        }

        /**
         * @deprecated
         * The "cities" attribute cannot be edited anymore but might still be
         * configured. Use "regions" instead.
         */
        if (array_key_exists('cities', $properties)) {
            $filter->setZips($this->properties['cities']);
        }

        if (array_key_exists('regions', $properties)) {
            $filter->setZips($this->regionService->getZipsFromRegionKeys($properties['regions']));
        }

        return $filter;
    }


    /**
     * @param Article|null $article
     * @return void
     */
    private function redirectToStaticDetailPageIfNotOnIt(Article $article = null): void
    {
        if ($article === null) {
            return;
        }

        if (strtolower($this->request->getMainRequest()->getControllerName()) !== PageFrameLinkingService::CONTROLLER) {
            $this->redirectToUri(
                $this->articleUrlService->generateUrl($article, $this->controllerContext),
                0,
                301
            );
        }
    }

    private function getSimilarToursIfApplicable(?Article $article): ?array
    {
        if ($article === null) {
            return null;
        }

        if ($article->getMainType() !== ArticleConstants::TYPE_TOUR) {
            return null;
        }

        $filter = new ArticleFilter();
        $filter->setCurrentSite($this->currentSite->getNodeName());
        $filter->setCategoryCollection($article->getCategories())
            ->setExcludes([ $article ])
            ->setLimit(4);

        return (array) $this->articleRepository->findByFilter($filter);
    }

    protected function handleTargetNotFoundError(): void
    {
        foreach (array_keys($this->request->getArguments()) as $argumentName) {
            /** @var TargetNotFoundError $targetNotFoundError */
            $targetNotFoundError = $this->arguments
                ->getValidationResults()
                ->forProperty($argumentName)
                ->getFirstError(TargetNotFoundError::class);

            if ($targetNotFoundError !== false) {
                $this->redirectToUri($this->configuration['notFoundPage'] ?? '/404', 0, 404);
            }
        }
    }
}
