<?php

namespace Newland\Toubiz\Poi\Neos\Routing;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\Routing\AbstractRoutePart;
use Neos\Flow\Mvc\Routing\Dto\MatchResult;
use Neos\Flow\Mvc\Routing\Dto\ResolveResult;
use Neos\Flow\Mvc\Routing\DynamicRoutePartInterface;
use Newland\PageFrameProvider\Definition\RoutingConfigurationHelper;
use Newland\PageFrameProvider\Service\PageFrameContextService;
use Newland\PageFrameProvider\Service\PageFrameLinkingService;

/**
 * Article Type Routing handler.
 *
 * Handles URLs that are built for PointOfInterests::showAction inside of a PageFrame.
 * The URLs can be configured in the `Newland.Toubiz.Poi.Neos.detailPage` path which can contain
 * the article type as a key and the URL as a value.
 *
 * This Handler is tightly coupled to `ArticleUrlService` which generates URLs handled by this Route
 * Handler.
 *
 * @Flow\Scope("prototype")
 */
class ArticleDetailRoutePart extends AbstractRoutePart implements DynamicRoutePartInterface
{
    // Constants that identify the POI plugin.
    public const PACKAGE = 'newland.toubiz.poi.neos';
    public const CONTROLLER = 'pointofinterests';
    public const ACTION = 'show';

    // Plugin namespace as setup by the Fusion Object.
    public const ARGUMENT_NAMESPACE = PageFrameLinkingService::DEFAULT_ARGUMENT_NAMESPACE;

    // The plugin argument that is used to determine the article type.
    public const TYPE_ARGUMENT = '_type';

    // Configuration key to use if there is no detail URL for the specific article type.
    protected const TYPE_FALLBACK = '_fallback';

    // Url that is used if there is not even a `_fallback` configured (instead of simply failing altogether).
    protected const ULTIMATE_FALLBACK_URL = 'poi/detail';

    /**
     * @var array
     * @Flow\InjectConfiguration(package="Newland.Toubiz.Poi.Neos", path="detailPage")
     */
    protected $typeConfiguration;

    /**
     * @var RoutingConfigurationHelper
     * @Flow\Inject()
     */
    protected $configurationHelper;

    /**
     * @var PageFrameContextService
     * @Flow\Inject()
     */
    protected $pageFrameContext;

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


    /**
     * Checks whether this Route Part corresponds to the given $routePath.
     * This method does not only check if the Route Part matches. It can also
     * shorten the $routePath by the matching substring when matching is successful.
     * This is why $routePath has to be passed by reference.
     *
     * @param string $routePath The request path to be matched - without query parameters, host and fragment.
     * @return bool|MatchResult true or an instance of MatchResult if Route Part matched $routePath, otherwise false.
     */
    public function match(&$routePath)
    {
        $this->configurationHelper->setTypeConfiguration($this->typeConfiguration);

        $routePathBefore = $routePath;
        $routePath = $this->configurationHelper->removeUriSegmentsFromRoutePath($routePath);

        return $routePathBefore !== $routePath;
    }

    /**
     * Checks whether this Route Part corresponds to the given $routeValues.
     * This method does not only check if the Route Part matches. It also
     * removes resolved elements from $routeValues-Array.
     * This is why $routeValues has to be passed by reference.
     *
     * @param array $routeValues An array with key/value pairs to be resolved by Dynamic Route Parts.
     * @return bool|ResolveResult true or an instance of ResolveResult if Route Part can resolve one or more
     *     $routeValues elements, otherwise false.
     */
    public function resolve(array &$routeValues)
    {
        $this->configurationHelper->setTypeConfiguration($this->typeConfiguration);
        if (!$this->isTryingToBuildArticleUrl($routeValues)) {
            return false;
        }

        $type = $routeValues[static::ARGUMENT_NAMESPACE][static::TYPE_ARGUMENT]
            ?? static::TYPE_FALLBACK;
        unset($routeValues[static::ARGUMENT_NAMESPACE][static::TYPE_ARGUMENT]);
        return new ResolveResult(
            $this->configurationHelper->urlFragmentForType($type) ?? static::ULTIMATE_FALLBACK_URL
        );
    }

    public function setSplitString($splitString): void
    {
        // Does nothing.
    }

    /**
     * @param array $routeValues
     * @return bool
     */
    private function isTryingToBuildArticleUrl(array $routeValues): bool
    {
        $type = $routeValues[static::ARGUMENT_NAMESPACE][static::TYPE_ARGUMENT] ?? null;

        $isPageFrameActionLink = $this->pageFrameLinkingService->isPageFrameActionLink(
            $routeValues,
            static::PACKAGE,
            static::CONTROLLER,
            static::ACTION,
            static::ARGUMENT_NAMESPACE
        );

        return $isPageFrameActionLink && $type !== null;
    }
}
