<?php declare(strict_types=1);


namespace Newland\Toubiz\Poi\Neos\Controller;

use Neos\ContentRepository\Domain\Model\Node;
use Neos\ContentRepository\Domain\Service\ContextFactoryInterface;
use Neos\ContentRepository\Exception\PageNotFoundException;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\Routing\ObjectPathMapping;
use Neos\Flow\Mvc\Routing\ObjectPathMappingRepository;
use Neos\Flow\Utility\Now;
use Neos\Neos\Domain\Service\ContentContext;
use Newland\NeosCommon\Logging\DeprecationLog;
use Newland\Toubiz\Poi\Neos\Service\ArticleUrlService;
use Newland\Toubiz\Sync\Neos\Domain\Model\Article;

class RedirectController extends AbstractActionController
{

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

    /**
     * @var ObjectPathMappingRepository
     * @Flow\Inject()
     */
    protected $objectPathMappingRepository;

    /**
     * @var ContextFactoryInterface
     * @Flow\Inject()
     */
    protected $contextFactory;

    /**
     * @var DeprecationLog
     * @Flow\Inject()
     */
    protected $deprecationLog;


    public function articleDetailAction(string $uriSegment): void
    {
        $article = $this->findPossibleRedirectionTarget($uriSegment);

        $node = $this->getCurrentSiteNode($article->getLanguage());
        if ($node === null) {
            throw new PageNotFoundException(sprintf(
                'No site node can be resolved for article %s',
                $article->getPersistenceObjectIdentifier()
            ));
        }

        $uri = $this->articleUrlService->generateUrlByCurrentNode($article, $node);
        $this->deprecationLog->logDeprecation('Deprecated article URL ' . $uriSegment . ' redirected to ' . $uri);
        $this->redirectToUri($uri, 0, 301);
    }

    private function findPossibleRedirectionTarget(string $uriSegment): Article
    {
        $article = $this->findArticle($uriSegment, '{urlIdentifier}-{name}', true, false)
            ?? $this->findArticle($uriSegment, '{name}-{urlIdentifier}', false, true);

        if ($article === null) {
            throw new PageNotFoundException(sprintf('Uri segment %s could not be resolved to article', $uriSegment));
        }

        return $article;
    }

    private function findArticle(
        string $uriSegment,
        string $uriPattern,
        bool $wildcardBefore,
        bool $wildcardAfter
    ): ?Article {
        $query = $this->objectPathMappingRepository->createQuery();
        $pathSegmentLike = ($wildcardBefore ? '%' : '') . $uriSegment . ($wildcardAfter ? '%' : '');
        $query->matching(
            $query->logicalAnd([
                $query->equals('objectType', Article::class),
                $query->equals('uriPattern', $uriPattern),
                $query->like('pathSegment', $pathSegmentLike)
            ])
        );
        $query->setLimit(1);

        /** @var ObjectPathMapping|null $mapping */
        $mapping = $query->execute()->getFirst();
        if ($mapping === null) {
            return null;
        }

        return $this->articleRepository->findByIdentifier($mapping->getIdentifier());
    }

    protected function getCurrentSiteNode(?string $language): ?Node
    {
        $dimensions = $language ? [ 'language' => [ $language ] ] : [];
        $context = $this->contextFactory->create(
            [
                'workspaceName' => 'live',
                'currentDateTime' => new Now(),
                'dimensions' => $dimensions,
                'invisibleContentShown' => false,
                'removedContentShown' => false,
                'inaccessibleContentShown' => false,
            ]
        );

        if (!($context instanceof ContentContext)) {
            return null;
        }

        $node = $context->getCurrentSiteNode();
        if (!($node instanceof Node)) {
            return null;
        }

        return $node;
    }
}
