<?php declare(strict_types=1);
namespace Newland\Toubiz\Sync\Neos\Command\Task;

/*
 * This file is part of the "toubiz-sync-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\Query;
use Newland\Toubiz\Api\Constants\Language;
use Newland\Toubiz\Api\Service\Outdooractive\ApiService;
use Newland\Toubiz\Api\Service\Outdooractive\ObjectAdapter\TourAdapter;
use Newland\Toubiz\Sync\Neos\Command\Helper\ApiServiceHelper;
use Newland\Toubiz\Sync\Neos\Command\Helper\ConfigurationHelper;
use Newland\Toubiz\Sync\Neos\Domain\Repository\ArticleRepository;
use Newland\Toubiz\Sync\Neos\Importer\ArticleImporter;
use Neos\Flow\Annotations as Flow;

class SynchronizeToursFromOutdoorActive implements SynchronizationTask, DeletesOld, ArticleSynchronizationTask
{
    use HandlesClientsForArticles;

    /**
     * @var ApiServiceHelper
     * @Flow\Inject()
     */
    protected $apiServiceHelper;

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

    protected static $serviceName = 'Outdooractive/Api';

    public function name(): string
    {
        return 'OutdoorActive';
    }

    public function configurations(ConfigurationHelper $configurationHelper): \Generator
    {
        $configuration = $configurationHelper->getConfigurationForService(static::$serviceName);
        if (!$configuration) {
            return;
        }

        foreach ($configuration['clients'] as $clientKey => $clientConfig) {
            foreach ($clientConfig['languages'] ?? $configuration['languages'] ?? [ Language::DE ] as $language) {
                $clientConfig['language'] = $language;
                $clientConfig['client'] = (string) ($clientConfig['client'] ?? $clientKey);
                yield $clientConfig;
            }
        }
    }

    public function synchronize(
        array $configuration,
        \Closure $errorHandlerWrapper,
        \Closure $onProgress
    ): SynchronizationResult {
        /** @var ApiService $apiService */
        $apiService = $this->apiServiceHelper->initializeApiServiceWithCommonConfigurationOptions(
            static::$serviceName,
            $configuration
        );

        $processed = 0;
        $ids = [];
        $result = $apiService->fetchTours(
            $errorHandlerWrapper(
                function (TourAdapter $tour, ?int $total) use (&$processed, &$ids, $onProgress, $configuration) {
                    $onProgress(++$processed, $total);
                    $importer = new ArticleImporter();
                    $importer->setLanguage($configuration['language']);
                    $importer->setClient($configuration['client']);
                    $ids[] = $importer->import($tour)->getPersistenceObjectIdentifier();
                }
            )
        );

        return SynchronizationResult::fromServiceResult($result, $ids);
    }

    public function deleteOld(SynchronizationResult $result, array $configuration, \Closure $onProgress): void
    {
        $query = $this->articleRepository->createQueryBuilder('article');
        $where = [ $query->expr()->notIn('article.originalId', $result->getOriginalIdsToRetain()) ];

        $language = $configuration['language'] ?? null;
        if ($language) {
            $where[] = $query->expr()->eq('article.language', ':language');
            $query->setParameter('language', $language);
        }

        $client = $configuration['client'] ?? null;
        if ($client) {
            $where[] = $query->expr()->eq('article.client', ':client');
            $query->setParameter('client', $client);
        }

        $query->andWhere(...$where);
        $count = (int) (clone $query)
            ->select('COUNT(article) as count')
            ->getQuery()
            ->execute(null, Query::HYDRATE_ARRAY)[0]['count'];

        $i = 0;
        $onProgress(0, $count);
        foreach ($query->getQuery()->execute() as $articles) {
            foreach ($articles as $article) {
                $this->articleRepository->remove($article);
                $onProgress(++$i, $count);
            }
        }
    }
}
