<?php declare(strict_types=1);

namespace Newland\PageFrameProvider\NodeResolution;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Configuration\ConfigurationManager;
use Neos\Flow\Configuration\Exception\InvalidConfigurationException;
use Neos\Flow\ObjectManagement\ObjectManager;
use Neos\ContentRepository\Domain\Model\NodeInterface;

/**
 * Not a real resolution strategy - but only a wrapper that uses another resolver
 * behind the scenes. This resolver can be configured in `Newland.PageFrameProvider.nodeResolution.class`.
 *
 * All additional properties in `Newland.PageFrameProvider.nodeResolution` will be passed to the
 * resolver as configuration.
 *
 * @Flow\Scope("singleton")
 */
class ConfigurationBasedNodeResolver implements RootNodeResolver
{

    /**
     * @var array
     * @Flow\InjectConfiguration(path="nodeResolution")
     */
    protected $resolutionConfiguration;

    /**
     * @var ConfigurationManager
     * @Flow\Inject()
     */
    protected $configurationManager;

    /**
     * @var ObjectManager
     * @Flow\Inject()
     */
    protected $objectManager;

    /**
     * @var RootNodeResolver
     */
    protected $resolver;

    public function resolveRootNode(?NodeInterface $referenceNode): ?NodeInterface
    {
        return $this->resolver()->resolveRootNode($referenceNode);
    }

    public function isNodePathAllowed(string $path): bool
    {
        return $this->resolver()->isNodePathAllowed($path);
    }

    public function setConfiguration(array $configuration): void
    {
        $this->resolver()->setConfiguration(
            (array) array_replace_recursive($this->resolutionConfiguration, $configuration)
        );
    }

    private function resolver(): RootNodeResolver
    {
        if ($this->resolver === null) {
            $class = $this->resolutionConfiguration['class'] ?? null;
            if ($class === null) {
                throw new InvalidConfigurationException(
                    'No resolver class specified in Newland.PageFrameProvider.nodeResolution.class'
                );
            }
            if (!class_exists($class)) {
                throw new InvalidConfigurationException(sprintf(
                    'Unknown resolver class %s specified in Newland.PageFrameProvider.nodeResolution.class',
                    $class
                ));
            }

            $instance = $this->objectManager->get($class);
            if (!($instance instanceof RootNodeResolver)) {
                throw new InvalidConfigurationException(sprintf(
                    'Resolver %s specified in Newland.PageFrameProvider.nodeResolution.class must implement %s',
                    $class,
                    RootNodeResolver::class
                ));
            }

            $instance->setConfiguration($this->resolutionConfiguration);
            $this->resolver = $instance;
        }

        return $this->resolver;
    }
}
