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

/*
 * 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\I18n\Locale;
use Neos\Flow\I18n\Service;
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Flow\Mvc\View\ViewInterface;
use Neos\Flow\Utility\Now;
use Neos\Neos\Service\LinkingService;
use Newland\NeosCommon\Service\NodeService;

abstract class AbstractActionController extends ActionController
{
    /**
     * The current document node (page node).
     *
     * @var Node|null
     */
    protected $documentNode;

    /**
     * The current node (mostly the plugin).
     *
     * @var Node
     */
    protected $node;

    /**
     * @Flow\Inject()
     * @var LinkingService
     */
    protected $linkingService;

    /**
     * @var array
     */
    protected $properties = [];

    /**
     * @Flow\Inject()
     * @var Service
     */
    protected $i18nService;

    /**
     * @var NodeService
     * @Flow\Inject()
     */
    protected $nodeService;

    /**
     * @var Now
     * @Flow\Inject()
     */
    protected $now;

    public function initializeAction(): void
    {
        $this->initializeNode();
    }

    /**
     * View initializer.
     *
     * @param ViewInterface $view
     * @return void
     */
    public function initializeView(ViewInterface $view): void
    {
        $view->assignMultiple(
            [
                'documentNode' => $this->documentNode,
                'node' => $this->node,
                'action' => $this->request->getControllerActionName(),
            ]
        );
    }

    protected function setupCustomNodeMappingForAjaxRequest(NodeInterface $node): void
    {
        $this->request->setArgument('__node', $node);
        $this->request->setArgument('__documentNode', $this->nodeService->getDocumentNode($node));

        $this->initializeAction();
        $this->initializeView($this->view);

        $this->applyCustomLanguageMappingFromDimensions($node);
    }

    /**
     * This may be called manually inside an action
     * that is working outside of the default node/context,
     * like an AJAX action.
     *
     * @param Node|null $node
     */
    protected function initializeNode(Node $node = null): void
    {
        /** @var Node|null $documentNode */
        $documentNode = $this->request->getInternalArgument('__documentNode');
        if ($documentNode instanceof Node) {
            $this->documentNode = $documentNode;
        }

        if ($node !== null) {
            $this->node = $node;
            $this->setLocale($node);
        } else {
            $node = $this->currentNode();
        }

        if ($node === null) {
            return;
        }

        $this->node = $node;
        $this->properties = array_replace_recursive(
            (array) $this->node->getProperties(),
            (array) $this->request->getInternalArgument('__properties')
        );
    }

    private function applyCustomLanguageMappingFromDimensions(NodeInterface $node): void
    {
        $dimensions = $node->getContext()->getDimensions();
        if (array_key_exists('language', $dimensions) && $dimensions['language'] !== []) {
            $configuration = $this->i18nService->getConfiguration();
            $currentLocale = new Locale($dimensions['language'][0]);
            $configuration->setCurrentLocale($currentLocale);
            $configuration->setFallbackRule(
                [
                    'strict' => false,
                    'order' => array_reverse($dimensions['language']),
                ]
            );
        }
    }

    /**
     * Forces the locale to the Node
     * Required for AJAX requests with translations
     *
     * @param NodeInterface $node
     */
    protected function setLocale(NodeInterface $node): void
    {
        $dimensions = $node->getContext()->getDimensions();
        if (($dimensions['language'] ?? []) !== []) {
            $currentLocale = new Locale($dimensions['language'][0]);
            $configuration = $this->i18nService->getConfiguration();
            $configuration->setCurrentLocale($currentLocale);
            $configuration->setFallbackRule(
                [
                    'strict' => false,
                    'order' => array_reverse($dimensions['language']),
                ]
            );
        }
    }

    private function currentNode(): ?Node
    {
        /** @var Node|null $node */
        $node = $this->request->getInternalArgument('__node');

        return ($node instanceof Node) ? $node : null;
    }
}
