<?php
namespace Newland\NeosCommon\LinkHandler\Handler;

use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Eel\FlowQuery\FlowQuery;
use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\Property\PropertyMapper;
use Neos\Neos\Domain\Service\ContentContext;
use Neos\Flow\Annotations as Flow;
use Newland\NeosCommon\LinkHandler\Domain\Model\Record;
use Newland\NeosCommon\LinkHandler\LinkingService;
use Newland\NeosCommon\Service\NodeService;
use Newland\NeosCommon\LinkHandler\Domain\Repository\RecordRepository;

/**
 * Links to arbitrary / configurable objects that are being displayed on a detail page.
 * The following configuration options must be given:
 *
 * - `eelPluginSelector`: Selector of the detail plugin
 * - `objectType`: Class of the object that is being linked to
 * - `arguments`: Array containing the arguments passed to the detail plugin - including the
 *      namespace prefix. The special property `parameterName` contains the name of the parameter
 *      that will receive the id of the record.
 * - `searchField`: The database field that is being searched for.
 * - `filter`: Fields being filtered for
 *
 *
 * A full configuration may look like this:
 * ```
 * eelPluginSelector: '[instanceof Newland.Toubiz.Poi.Neos:List]'
 * arguments:
 *      '--newland_toubiz_poi_neos-list':
 *          '@package': 'newland.toubiz.poi.neos'
 *          '@controller': 'pointofinterests'
 *          '@action': 'show'
 *          parameterName: 'article'
 * objectType: Newland\Toubiz\Sync\Neos\Domain\Model\Article
 * searchField: name
 * filter:
 *      mainType: 0
 * ```
 *
 * @Flow\Scope("singleton")
 * @deprecated
 */
class ModelWithDetailPluginHandler extends AbstractLinkHandler
{
    /**
     * @var RecordRepository
     * @Flow\Inject()
     */
    protected $recordRepository;

    /**
     * @var (NodeInterface|null)[]
     */
    protected $nodeCache = [];

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

    /**
     * @var PropertyMapper
     * @Flow\Inject()
     */
    protected $propertyMapper;

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

    /**
     * @param string $identifier
     * @return Record|null
     */
    public function findByIdentifier(string $identifier): ?Record
    {
        return $this->recordRepository->findByRecordIdentifier($identifier, $this->getQueryParameters());
    }

    /**
     * @param string $searchTerm
     * @return Record[]
     */
    public function findBySearchTerm(string $searchTerm): array
    {
        return $this->recordRepository->findBySearchField($searchTerm, $this->getQueryParameters());
    }

    public function generateRecordLink(
        Record $record,
        ControllerContext $controllerContext,
        NodeInterface $contextNode,
        bool $absolute
    ): ?string {
        $detailPage = $this->detailPage($contextNode);
        if (!$detailPage) {
            return null;
        }

        $arguments = $this->getDetailPageArguments($record);
        return $this->linkingService->createNodeUri(
            $controllerContext,
            $detailPage,
            null,
            null,
            $absolute,
            $arguments
        );
    }

    protected function nodeForRecord(NodeInterface $site): ?NodeInterface
    {
        $key = $site->getPath() . ':' . $this->recordType;
        if (!array_key_exists($key, $this->nodeCache)) {
            $selector = $this->configuration['eelPluginSelector'];
            $nodes = (new FlowQuery([ $site ]))
                ->find($selector)
                ->parents('[instanceof Neos.NodeTypes:Page]')
                ->get();

            $this->nodeCache[$key] = $this->nodeService->getFirstAvailableNode($nodes);
        }

        return $this->nodeCache[$key];
    }

    private function detailPage(NodeInterface $contextNode): ?NodeInterface
    {
        $context = $contextNode->getContext();

        if ($context instanceof ContentContext) {
            return $this->nodeForRecord($context->getCurrentSiteNode());
        }

        return null;
    }

    private function getDetailPageArguments(Record $record): array
    {
        $arguments = $this->configuration['arguments'];

        $namespace = array_keys($arguments)[0];
        $parameterName = $arguments[$namespace]['parameterName'];
        $arguments[$namespace][$parameterName] = $this->propertyMapper->convert(
            $record->getId(),
            $this->configuration['objectType']
        );
        unset($arguments[$namespace]['parameterName']);

        return $arguments;
    }

    private function getQueryParameters(): array
    {
        $filter = $this->configuration['filter'] ?? [];
        if ($this->language !== null) {
            $filter['language'] = $this->language;
        }

        return [
            'alias' => $this->recordType,
            'objectType' => $this->configuration['objectType'],
            'searchField' => $this->configuration['searchField'],
            'filter' => $filter,
        ];
    }
}
