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

/*
 * 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\Cache\Frontend\FrontendInterface;
use Neos\ContentRepository\Domain\Model\Node;
use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Cache\CacheManager;
use Neos\Flow\Mvc\Controller\ControllerContext;
use Newland\NeosCommon\Service\ControllerContextFactory;
use Newland\PageFrameProvider\Service\PageFrameLinkingService;
use Newland\Toubiz\Poi\Neos\Routing\ArticleDetailRoutePart;
use Newland\Toubiz\Sync\Neos\Domain\Model\Article;

/**
 * Generates detail URLs for articles.
 *
 * This Service is tightly coupled to the `PageFrameController` functionality of `Newland.NeosCommon` to have
 * detail pages that are independent of the PageTree as well as the `ArticleTypeHandler` which handles the URLs
 * generated by this service in order to provide nice looking URLs.
 *
 * @Flow\Scope("singleton")
 */
class ArticleUrlService
{
    //TODO: Check which projects still use deprecated properties.
    /**
     * @Flow\InjectConfiguration(package="Newland.Toubiz.Poi.Neos.lodging.targetUri")
     * @var string
     * @deprecated since 1.3.0, superseeded by $lodgingBaseUri
     */
    protected $lodgingUri;

    /**
     * @Flow\InjectConfiguration(package="Newland.Toubiz.Poi.Neos.lodging.targetBaseUri")
     * @var string
     * @deprecated since 1.8.2, superseeded by $lodgingBaseUris
     */
    protected $lodgingBaseUri;

    /**
     * @Flow\InjectConfiguration(package="Newland.Toubiz.Poi.Neos.lodging.clients")
     * @var array
     */
    protected $lodgingClientConfigurations;

    /**
     * @var ControllerContextFactory
     * @Flow\Inject()
     */
    protected $controllerContextFactory;

    /**
     * @var PageFrameLinkingService
     * @Flow\Inject()
     */
    protected $pageFrameLinkingService;

    /**
     * @var FrontendInterface
     */
    protected $cache;
    public function injectCache(CacheManager $cacheManager): void
    {
        $this->cache = $cacheManager->getCache('Newland_Toubiz_Poi_Neos_ArticleUrls');
    }

    public function openingTimesApiUrl(Article $article, NodeInterface $referenceNode): string
    {
        return $this->apiUrl($article, $referenceNode, 'openingTimes');
    }

    public function tourGeometryApiUrl(Article $article, NodeInterface $referenceNode): string
    {
        return $this->apiUrl($article, $referenceNode, 'tourGeometry');
    }

    private function apiUrl(Article $article, NodeInterface $referenceNode, string $action): string
    {
        return $this->controllerContextFactory
            ->initializeFakeUriBuilder($referenceNode)
            ->reset()
            ->setFormat('json')
            ->uriFor(
                $action,
                [ 'article' => $article ],
                'Api',
                'Newland.Toubiz.Poi.Neos'
            );
    }

    public function generateUrl(
        Article $article,
        ControllerContext $context,
        NodeInterface $referenceNode = null,
        bool $absolute = false
    ): ?string {
        $key = $this->cacheKey($article, $context, $referenceNode);
        if ($this->cache->has($key)) {
            return $this->cache->get($key);
        }

        $url = $this->generateLegacyLodgingUrl($article)
            ?? $article->getDetailUri()
            ?? $this->generateDetailViewUrl($article, $context, $referenceNode, $absolute);

        $this->cache->set($key, $url);
        return $url;
    }

    /**
     * Some requests are within a controller context that has no "current node".
     * So we pass the current node manually and create a fake controller context around that.
     */
    public function generateUrlByCurrentNode(Article $article, NodeInterface $node, bool $absolute = false): ?string
    {
        $context = $this->controllerContextFactory->initializeFakeControllerContext($node);

        return $this->generateUrl($article, $context, $node, $absolute);
    }

    private function generateDetailViewUrl(
        Article $article,
        ControllerContext $context,
        NodeInterface $referenceNode = null,
        bool $absolute = false
    ): string {
        return $this->pageFrameLinkingService->build(
            $context->getUriBuilder(),
            ArticleDetailRoutePart::PACKAGE,
            ArticleDetailRoutePart::CONTROLLER,
            ArticleDetailRoutePart::ACTION,
            'poiDetail',
            [
                'article' => $article,
                ArticleDetailRoutePart::TYPE_ARGUMENT => $article->getMainType(),
            ],
            ArticleDetailRoutePart::ARGUMENT_NAMESPACE,
            $referenceNode,
            $absolute
        );
    }

    /**
     * Fallback that will generate detail urls based on old-style configuration.
     * TODO Remove this method together with legacy uri handling in LodgingAdapter
     *
     * @deprecated
     * @param Article $article
     * @return string|null
     */
    private function generateLegacyLodgingUrl(Article $article): ?string
    {
        if ($this->lodgingUri) {
            return str_replace(':id', (string) $article->getOriginalId(), $this->lodgingUri);
        }

        if ($this->lodgingBaseUri && strpos((string) $article->getDetailUri(), '/') === 0) {
            return $this->lodgingBaseUri . $article->getDetailUri();
        }

        return null;
    }

    private function cacheKey(
        Article $article,
        ControllerContext $context,
        ?NodeInterface $referenceNode
    ): string {
        $key = $article->getPersistenceObjectIdentifier()
            . $article->getLanguage()
            . $context->getUriBuilder()->getRequest()->getHttpRequest()->getUri()->getHost();
        if ($referenceNode) {
            $key .= $referenceNode->getContextPath();
        }
        return md5($key);
    }
}
