<?php declare(strict_types=1);

namespace Newland\Toubiz\Sync\Neos\Command\MigrationPair;

use Doctrine\ORM\Query;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Validation\ValidatorResolver;
use Newland\Toubiz\Sync\Neos\Domain\Repository\ArticleRepository;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\OutputInterface;
use Neos\Flow\Persistence\PersistenceManagerInterface;

abstract class MigrationPairFinder
{

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

    public function injectArticleRepository(ArticleRepository $articleRepository): void
    {
        $articleRepository->setLanguage(null);
        $this->articleRepository = $articleRepository;
    }

    /**
     * @var ValidatorResolver
     * @Flow\Inject()
     */
    protected $validatorResolver;

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

    /** @var OutputInterface */
    protected $output;

    /** @var string */
    protected $fromSourceSystem;

    /** @var string */
    protected $toSourceSystem;

    public function __construct(OutputInterface $output, string $fromSourceSystem, string $toSourceSystem)
    {
        $this->output = $output;
        $this->fromSourceSystem = $fromSourceSystem;
        $this->toSourceSystem = $toSourceSystem;
    }

    abstract public function findMigrationPairs(string $language, array $idsToIgnore): \Generator;

    protected function idByExternalId(
        string $sourceSystem,
        string $externalIdType,
        string $externalId,
        string $language
    ): ?string {
        $query = $this->articleRepository
            ->createQueryBuilder('article')
            ->join('article.externalIds', 'externalId')
            ->select('article.Persistence_Object_Identifier')
            ->setMaxResults(1);

        $query->where($query->expr()->andX(
            $query->expr()->eq('externalId.type', ':type'),
            $query->expr()->eq('externalId.id', ':externalId'),
            $query->expr()->eq('article.sourceSystem', ':system'),
            $query->expr()->eq('article.language', ':language')
        ));

        return $query->getQuery()->execute([
            'system' => $sourceSystem,
            'type' => $externalIdType,
            'externalId' => $externalId,
            'language' => $language,
        ], Query::HYDRATE_ARRAY)[0]['Persistence_Object_Identifier'] ?? null;
    }

    protected function loopWithBar(array $data, string $message = ''): \Generator
    {
        $progress = new ProgressBar($this->output, count($data));
        $progress->setFormat(' %current:5s%/%max:5s% [%bar%] %percent:3s%% | ' . $message);
        $progress->start();

        $i = 0;
        foreach ($data as $key => $item) {
            yield $key => $item;
            $progress->advance();

            if (++$i % 100 === 0) {
                $this->persistenceManager->persistAll();
                $this->persistenceManager->clearState();
                $this->validatorResolver->reset();
            }
        }
        $progress->finish();
        $this->output->writeln('');
    }
}
