<?php
namespace Newland\NeosCommon\LinkHandler;

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

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\ObjectManagement\ObjectManager;
use Neos\Flow\Property\PropertyMapper;
use Neos\ContentRepository\Domain\Model\NodeInterface;
use Newland\NeosCommon\LinkHandler\LinkHandlerFactory;

/**
 * A service for creating URIs pointing to nodes, assets and records.
 *
 * The target node can be provided as string or as a Node object; if not specified
 * at all, the generated URI will refer to the current document node inside the Fusion context.
 *
 * When specifying the ``node`` argument as string, the following conventions apply:
 *
 * *``node`` starts with ``/``:*
 * The given path is an absolute node path and is treated as such.
 * Example: ``/sites/acmecom/home/about/us``
 *
 * *``node`` does not start with ``/``:*
 * The given path is treated as a path relative to the current node.
 * Examples: given that the current node is ``/sites/acmecom/products/``,
 * ``stapler`` results in ``/sites/acmecom/products/stapler``,
 * ``../about`` results in ``/sites/acmecom/about/``,
 * ``./neos/info`` results in ``/sites/acmecom/products/neos/info``.
 *
 * *``node`` starts with a tilde character (``~``):*
 * The given path is treated as a path relative to the current site node.
 * Example: given that the current node is ``/sites/acmecom/products/``,
 * ``~/about/us`` results in ``/sites/acmecom/about/us``,
 * ``~`` results in ``/sites/acmecom``.
 *
 * @Flow\Scope("singleton")
 */
class LinkingService extends \Neos\Neos\Service\LinkingService
{

    /**
     * Pattern to match supported URIs.
     *
     * @var string
     */
    // phpcs:ignore Generic.Files.LineLength.TooLong
    const PATTERN_SUPPORTED_URIS = '/(node|asset|record):\/\/([a-z0-9\-]{8}\-[a-z0-9\-]{4}\-[a-z0-9\-]{4}\-[a-z0-9\-]{4}\-[a-z0-9\-]{12}|([a-zA-Z\-]+):([a-z0-9\-]+))/';

    /**
     * @var LinkHandlerFactory
     * @Flow\Inject()
     */
    protected $linkHandlerFactory;

    /**
     * Resolves a given record:// URI to a "normal" HTTP(S) URI for the detail page displaying the addressed record.
     *
     * @param string $uri
     * @param NodeInterface $contextNode
     * @param ControllerContext $controllerContext
     * @param bool $absolute
     * @return null|string
     */
    public function resolveRecordUri(
        string $uri,
        NodeInterface $contextNode,
        ControllerContext $controllerContext,
        $absolute = false
    ) {
        try {
            preg_match(self::PATTERN_SUPPORTED_URIS, $uri, $matches);
            $nodeType = $matches[3];
            $nodeId = $matches[4];

            $handler = $this->linkHandlerFactory->get($nodeType);
            $record = $handler->findByIdentifier($nodeId);
            if (!$record) {
                return null;
            }

            return $handler->generateRecordLink(
                $record,
                $controllerContext,
                $contextNode,
                $absolute
            );
        } catch (\Exception $e) {
            // TODO Log exception
            return null;
        }
    }

    /**
     * Linkhandler URIs need a workaround in order to be rendered correctly in the backend
     * react component. This workaround requires that record URLs are prefixed with
     * `node://record:` instead of the correct `record://`.
     *
     * This method reverses that workaround in order to produce a string with correct
     * URIs in order for the workaround to be temporary and pluggable.
     *
     * see DataLoaderHelper#registerDataLoaderWithValueResolveWorkaround (JS) for more information
     *
     * @param string $uri
     * @return string
     */
    public function nodeUriWorkaround(string $uri): string
    {
        return str_replace('node://record:', 'record://', $uri);
    }
}
