<?php declare(strict_types=1);

namespace Newland\Toubiz\Map\Neos\Provider\DefaultProviders;

use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Neos\ContentRepository\Domain\Model\Node;
use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Flow\Configuration\Exception\InvalidConfigurationException;
use Newland\Toubiz\Api\ObjectAdapter\Concern\ArticleConstants;
use Newland\Toubiz\Map\Neos\Provider\MapDataProvider;
use Newland\Toubiz\Poi\Neos\Filter\ArticleFilterFactory;
use Newland\Toubiz\Sync\Neos\Domain\Model\Article;
use Neos\Flow\Annotations as Flow;
use Newland\Toubiz\Sync\Neos\Domain\Repository\ArticleRepository;

/**
 * @MapDataProvider()
 */
class FilteredArticles extends SubnodeBasedMarkerProvider
{
    use ArticleFormatting;
    protected $nodeType = 'Newland.Toubiz.Map.Neos:Map.Markers.FilteredArticles';
    private const HOUR = 60 * 60;

    /**
     * @var int
     * @Flow\InjectConfiguration(package="Newland.Toubiz.Map.Neos", path="filteredArticles.limit")
     */
    protected $limit;

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


    public function markersForNode(Node $node, int $offset, int $limit): array
    {
        if (!$node->hasProperty('articleType')) {
            return [];
        }

        if (!($node instanceof NodeInterface)) {
            return [];
        }

        /** @var Article[] $articles */
        $articles = $this->query($node)
            ->setFirstResult($offset)
            ->setMaxResults($limit)
            ->getQuery()
            ->setFetchMode(Article::class, 'categories', ClassMetadataInfo::FETCH_EAGER)
            ->execute();

        $alwaysDisplayPolygon = (bool) $node->getProperty('alwaysDisplayPolygon');
        $color = $node->getProperty('tourColor');
        $markers = [];
        foreach ($articles as $article) {
            $marker = $this->articleToMarker($article, $node, !$alwaysDisplayPolygon);
            if (!$marker) {
                continue;
            }

            if ($marker->tour) {
                $marker->tour['alwaysDisplayPolygon'] = $alwaysDisplayPolygon;
                $marker->tour['color'] = $color;
            }

            $markers[] = $marker;
        }

        return $markers;
    }

    public function numberOfMarkersForNode(Node $node): int
    {
        $query = $this->query($node);

        $countQuery = $this->articleRepository->createQueryBuilder('article')
            ->select('COUNT(article)')
            ->where($query->expr()->in(
                'article.Persistence_Object_Identifier',
                $query->select('entity.Persistence_Object_Identifier')->getDQL()
            ))->getQuery();

        $countQuery->enableResultCache(6 * self::HOUR);

        return min(
            (int) $countQuery->getSingleScalarResult(),
            $this->limit
        );
    }

    private function query(NodeInterface $node): QueryBuilder
    {
        $mainType = (int) $node->getProperty('articleType');
        if (!\in_array($mainType, ArticleConstants::ALL_TYPES, true)) {
            throw new InvalidConfigurationException(
                sprintf(
                    '"%s" is not a valid article type. Must be one of [%s]',
                    $node->getProperty('articleType'),
                    implode(', ', ArticleConstants::ALL_TYPES)
                )
            );
        }

        return (new ArticleFilterFactory($node))
            ->createFilterForArticleType($mainType, (array) $node->getProperties())
            ->enablePagination(false)
            ->getArticleQuery()
            ->andWhere('entity.longitude IS NOT NULL')
            ->andWhere('entity.latitude IS NOT NULL')
            ->setMaxResults($this->limit);
    }
}
