<?php declare(strict_types=1);
namespace Newland\Toubiz\Poi\Neos\Filter\Items;

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

use Doctrine\ORM\QueryBuilder;
use Neos\Flow\Configuration\Exception\InvalidConfigurationException;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Neos\Domain\Model\Site;
use Neos\Neos\Domain\Service\ContentContext;
use Neos\Neos\Service\LinkingService;
use Newland\NeosFiltering\Items\PaginationInput;
use Newland\NeosFiltering\Items\Root;
use Newland\Toubiz\Poi\Neos\Filter\ArticleClientFilterService;
use Newland\Toubiz\Poi\Neos\Service\LinkListService;
use Newland\Toubiz\Poi\Neos\Service\SplitscreenService;
use Newland\Toubiz\Sync\Neos\Domain\Repository\ArticleRepository;
use Neos\Flow\Annotations as Flow;

class ArticleRoot extends Root
{

    /**
     * @var ArticleRepository
     * @Flow\Inject()
     */
    protected $articleRepository;

    /**
     * @var ArticleClientFilterService
     * @Flow\Inject()
     */
    protected $clientFilterService;

    /**
     * @var int
     */
    protected $articleType;

    /**
     * @var LinkListService
     * @Flow\Inject()
     */
    protected $linkListService;

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

    /**
     * @var SplitscreenService
     * @Flow\Inject()
     */
    protected $splitscreenService;

    public function setArticleType(int $articleType): void
    {
        $this->articleType = $articleType;
        $this->enableSplitScreen($this->splitscreenService->isEnabled($articleType));
    }

    public function initializeForFilteredLists(ActionRequest $request): self
    {
        if ($this->pluginNode !== null) {
            $this->addFormAttributesForAjax(sprintf(
                '/toubiz-poi/filtered-list?node=%s&format=ajax',
                $this->pluginNode->getContextPath()
            ));
        }
        $this->fillStateFromRootRequestArguments($request)
            ->setAction(
                [
                    '@action' => 'show',
                    '@controller' => 'FilteredLists',
                    '@package' => 'Newland.Toubiz.Poi.Neos',
                ],
                $request
            );

        return $this;
    }

    public function getArticleQuery(array $additionalWhere = [], bool $withHidden = false): QueryBuilder
    {
        $queryGetter = function () use ($additionalWhere, $withHidden) {
            $query = $this->articleRepository->createQueryBuilder('entity', null, $withHidden);
            $query = $this->applyBaseConditions($query, $additionalWhere);
            return $this->applyToQuery($query);
        };

        if ($this->pluginNode) {
            $language = $this->pluginNode->getDimensions()['language'][0] ?? null;
            return $this->articleRepository->withLanguage($language, $queryGetter);
        }

        return $queryGetter();
    }

    public function countArticles(array $additionalWhere = [], bool $withHidden = false): int
    {
        $query = $this->articleRepository->createQueryBuilder('entity', null, $withHidden);
        $query = $this->applyBaseConditions($query, $additionalWhere);
        return $this->getResultCount($query);
    }

    public function getListUri(ControllerContext $context, array $overrides): string
    {
        $site = $this->getSite();
        $siteName = $site ? $site->getNodeName() : 'default';

        $targetUri = $this->linkListService
            ->getLinkTargetNodeByClientAndArticleType($this->articleType, $siteName);

        if ($targetUri === '') {
            return $targetUri;
        }

        if (strpos($targetUri, 'node://') === 0) {
            $node = $this->linkingService->convertUriToObject($targetUri, $this->pluginNode);
            if ($node === null) {
                throw new InvalidConfigurationException(sprintf('No node matching "%s" found', $targetUri));
            }
            $arguments = $this->buildQueryStringFromOverrides($overrides);

            return $this->linkingService->createNodeUri(
                $context,
                $node,
                $this->pluginNode,
                null,
                false,
                $arguments
            );
        }

        return $this->removePlaceholders($targetUri);
    }

    public function setPageSize(int $pageSize): ArticleRoot
    {
        foreach ($this->flattenChildren($this) as $child) {
            if ($child instanceof PaginationInput) {
                $config = $child->getConfiguration();
                $config['pageSize'] = $pageSize;
                $child->setConfiguration($config);
            }
        }
        return $this;
    }


    protected function removePlaceholders(string $url)
    {
        $uri = \Safe\preg_replace('/\{.*\}(\/)?/', '', $url);

        if (is_array($uri)) {
            $uri[0] = $uri[0] ?? '';
            $uri = $uri[0];
        }
        return $uri;
    }

    private function applyBaseConditions(QueryBuilder $query, array $additionalWhere = []): QueryBuilder
    {
        if ($this->articleType !== null) {
            $query->andWhere(sprintf('entity.mainType = %d', $this->articleType));
            $site = $this->getSite();
            if ($site !== null) {
                $this->clientFilterService->addClientWhereClause($query, $this->articleType, $site, 'entity');
            }
        }
        foreach ($additionalWhere as $where) {
            $query->andWhere($where);
        }
        return $query;
    }

    private function getSite(): ?Site
    {
        if ($this->pluginNode === null) {
            return null;
        }

        $context = $this->pluginNode->getContext();
        if (!($context instanceof ContentContext)) {
            return null;
        }

        return $context->getCurrentSite();
    }
}
