<?php
namespace Newland\Toubiz\Poi\Neos\Listener;

use Doctrine\ORM\EntityManagerInterface;
use Neos\ContentRepository\Domain\Factory\NodeFactory;
use Neos\Flow\Cli\ConsoleOutput;
use Neos\Flow\Persistence\PersistenceManagerInterface;
use Neos\Neos\Domain\Repository\SiteRepository;
use Newland\NeosCommon\Domain\Repository\NodeRepository;
use Newland\Toubiz\Poi\Neos\Service\ArticleUrlService;
use Newland\Toubiz\Sync\Neos\Command\ArticlesCommandController;
use Newland\Toubiz\Sync\Neos\Domain\Repository\ArticleRepository;
use Newland\NeosCommon\Service\ControllerContextFactory;
use Neos\Flow\Annotations as Flow;
use Symfony\Component\Console\Helper\ProgressBar;
use Webit\DoctrineORM\QueryBuilder\Iterator\QueryBuilderIterator;

/**
 * @Flow\Scope("singleton")
 */
class BuildUrls
{
    public const NODE_TYPE = 'Neos.NodeTypes:Page';

    /**
     * @Flow\Inject()
     * @var NodeRepository
     */
    protected $nodeRepository;

    /**
     * @Flow\Inject()
     * @var EntityManagerInterface
     */
    protected $entityManager;

    /**
     * @Flow\Inject()
     * @var NodeFactory
     */
    protected $nodeFactory;

    /**
     * @Flow\Inject()
     * @var ControllerContextFactory
     */
    protected $contextFactory;

    /**
     * @Flow\Inject()
     * @var SiteRepository
     */
    protected $siteRepository;

    /**
     * @var ArticleUrlService
     * @Flow\Inject()
     */
    protected $articleUrlService;

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

    /**
     * @var ConsoleOutput
     * @api
     */
    protected $output;

    /**
     * @Flow\Inject()
     * @var PersistenceManagerInterface
     */
    protected $persistenceManager;

    public function run(string $type, ?array $ids): void
    {
        $this->output = new ConsoleOutput();
        if ($type === ArticlesCommandController::TYPE_FLUSH) {
            if ($ids === null) {
                $this->output->outputLine("\nGenerating all Article URLs");
            } else {
                $this->output->outputLine(sprintf("\nGenerating %d Article URLs", \count($ids)));
            }
            $this->generateArticleUrls($ids);
        }
    }

    protected function persistAndFlushEntityReferencesFromPersistenceManager(): void
    {
        $this->persistenceManager->persistAll();
        $this->persistenceManager->clearState();
    }

    private function generateArticleUrls(?array $ids): void
    {
        $batchSize = 100;
        $node = $this->nodeRepository->findOneByNodeType(static::NODE_TYPE, true);

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

        $context = $this->contextFactory->initializeFakeControllerContext($node);

        $progress = new ProgressBar($this->output->getOutput());
        $progress->setFormat('%current% / %max% [%bar%] Creating article urls');
        $progress->setMaxSteps($this->count($ids));

        foreach ($this->findArticles($ids) as $i => $article) {
            $this->articleUrlService->generateUrl($article, $context);
            $progress->setProgress($i);
            if (($i % $batchSize) === 0) {
                $this->persistAndFlushEntityReferencesFromPersistenceManager();
            }
        }

        $progress->finish();
        $this->output->outputLine();
    }

    private function findArticles(?array $ids): \Iterator
    {
        $iterableResult = new \ArrayIterator([]);
        $this->articleRepository->withoutLanguageHandling(function () use ($ids, &$iterableResult) {
            $query = $this->articleRepository->createQueryBuilder('article');
            if ($ids !== null) {
                $query->where($query->expr()->in('article.Persistence_Object_Identifier', $ids));
            }
            $iterableResult = new QueryBuilderIterator($query);
        });
        return $iterableResult;
    }

    private function count(?array $ids): int
    {
        if ($ids) {
            return \count($ids);
        }
        return $this->articleRepository->withoutLanguageHandling(function () {
            return $this->articleRepository->countAll();
        });
    }
}
