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

/*
 * This file is part of the "toubiz-events-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\Node;
use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Neos\Domain\Service\ContentContext;
use Neos\Neos\Service\TransliterationService;
use Newland\NeosCommon\Service\ControllerContextFactory;
use Newland\PageFrameProvider\Service\PageFrameLinkingService;
use Newland\Toubiz\Events\Neos\Routing\EventDetailRoutePart;
use Newland\Toubiz\Sync\Neos\Domain\Model\Event;
use Newland\Toubiz\Sync\Neos\Domain\Model\EventDate;

/**
 * Generates detail URLs for events.
 *
 * This Service is tightly coupled to the `PageFrameController` functionality of `Newland.PageFrameProvider` to have
 * detail pages that are independent of the PageTree.
 *
 * @Flow\Scope("singleton")
 */
class EventUrlService
{
    /**
     * @Flow\Inject()
     * @var ControllerContextFactory
     */
    protected $controllerContextFactory;

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

    /**
     * @Flow\InjectConfiguration("detailPages")
     * @var array
     */
    protected $configuration;


    /**
     * @Flow\Inject()
     * @var TransliterationService
     */
    protected $transliterationService;

    public function generateUrl(
        Event $event,
        ControllerContext $context,
        ?NodeInterface $referenceNode = null,
        ?EventDate $forDate = null,
        bool $absolute = false
    ): ?string {
        $configuration = $this->configuration ?? null;

        $configuredUrl = null;
        if ($configuration) {
            $configuredUrl = $this->overrideUrlDetail($referenceNode, $event, $context);
        }

        if ($configuredUrl) {
            return $configuredUrl;
        }

        return $this->getDateUrl($event, $forDate)
            ?? $this->generateDetailViewUrl($event, $context, $referenceNode, $absolute);
    }

    /**
     * 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.
     *
     * @return mixed|string|null
     */
    public function generateUrlByCurrentNode(
        Event $event,
        NodeInterface $node,
        EventDate $forDate = null,
        bool $absolute = false
    ) {
        $context = $this->controllerContextFactory->initializeFakeControllerContext($node);

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

    private function getDateUrl(Event $event, ?EventDate $date = null): ?string
    {
        $date = $date ?? $event->getEventDates()[0] ?? null;
        if ($date) {
            return $date->getDetailUri();
        }
        return null;
    }

    private function generateDetailViewUrl(
        Event $event,
        ControllerContext $context,
        NodeInterface $referenceNode = null,
        bool $absolute = false
    ): string {
        return $this->pageFrameLinkingService->build(
            $context->getUriBuilder(),
            EventDetailRoutePart::PACKAGE,
            EventDetailRoutePart::CONTROLLER,
            EventDetailRoutePart::ACTION,
            'eventDetail',
            [
                'event' => $event,
                // events don't currently have types
                EventDetailRoutePart::TYPE_ARGUMENT => 'default',
            ],
            EventDetailRoutePart::ARGUMENT_NAMESPACE,
            $referenceNode,
            $absolute
        );
    }

    private function overrideUrlDetail(
        ?NodeInterface $node,
        Event $event,
        ControllerContext $controllerContext
    ): ?string {
        if ($node === null) {
            return '';
        }
        /** @var ContentContext $context */
        $context = $node->getContext();
        $site = $context->getCurrentSite();
        $clientConfiguration = $this->configuration['clients'][$site->getName()]
            ?? $this->configuration['clients']['Default'] ?? null;
        if ($clientConfiguration['redirectToTportal'] ?? null) {
            return $this->simpleTemplate(
                $clientConfiguration['detailUri'],
                [
                    'name' => $this->sanitizeTitle($event->getTitle()),
                    'id' => $event->getOriginalId(),
                    'language' => $event->getLanguage(),
                ]
            );
        }
        return null;
    }

    private function simpleTemplate(
        string $template,
        array $replacements,
        array $enclosure = [ '{', '}' ]
    ): string {
        foreach ($replacements as $before => $after) {
            $template = str_replace(
                $enclosure[0] . $before . $enclosure[1],
                $after,
                $template
            );
        }

        return $template;
    }

    /**
     * Based of Neos\Flow\Mvc\Routing\IdentityRoutePart->rewriteForUri()
     * But correctly use the transliterate method mentioned on the todo
     */
    private function sanitizeTitle(string $title): string
    {
        $title = $this->transliterationService->transliterate($title);
        $result = \Safe\preg_replace('/[^A-Za-z0-9\-\s]/u', '', $title);
        $spaceCharacter = '-';

        $result = \Safe\preg_replace('/[ \-+_]+/', $spaceCharacter, $result);

        if (is_array($result)) {
            $result = implode('', $result);
        }
        return strtolower($result ?? '');
    }
}
