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

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
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\Events\Neos\Service\EventUrlService;
use Newland\NeosCommon\Service\ControllerContextFactory;
use Neos\Flow\Annotations as Flow;
use Newland\Toubiz\Sync\Neos\Command\EventsCommandController;
use Newland\Toubiz\Sync\Neos\Domain\Repository\EventRepository;
use Symfony\Component\Console\Helper\ProgressBar;
use Webit\DoctrineORM\QueryBuilder\Iterator\QueryBuilderIterator;

/**
 * @Flow\Scope("singleton")
 */
class BuildUrls
{
    /**
     * @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 EventUrlService
     * @Flow\Inject()
     */
    protected $eventUrlService;

    /**
     * @var EventRepository
     * @Flow\Inject()
     */
    protected $eventRepository;

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

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


    const NODE_TYPE = 'Neos.NodeTypes:Page';

    public function run(string $type, ?array $ids): void
    {
        $this->output = new ConsoleOutput();
        if ($type === EventsCommandController::TYPE_FLUSH) {
            $this->generateEventUrls($ids);
        }
    }

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

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

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

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

        $context = $this->contextFactory->initializeFakeControllerContext($node);
        foreach ($this->findEvents($ids) as $i => $event) {
            $this->eventUrlService->generateUrl($event, $context);
            $progress->setProgress($i);
            if (($i % $batchSize) === 0) {
                $this->persistAndFlushEntityReferencesFromPersistenceManager();
            }
        }

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

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

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