<?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 Newland\Toubiz\Api\Constants\Language;
use Newland\Toubiz\Api\ObjectAdapter\Concern\ArticleConstants;
use Newland\Toubiz\Api\Service\Toubiz\Legacy\DbService;
use Newland\Toubiz\Api\Service\Toubiz\Legacy\ObjectAdapter\DbService\PointOfInterestAdapter;
use Newland\Toubiz\Sync\Neos\Command\Helper\ApiServiceHelper;
use Newland\Toubiz\Sync\Neos\Command\Helper\ConfigurationHelper;
use Newland\Toubiz\Sync\Neos\Importer\ArticleImporter;
use Neos\Flow\Annotations as Flow;
use Newland\Toubiz\Sync\Neos\Logging\LoggerFactory;
use Psr\Log\LoggerInterface;

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

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

    /** @var int */
    private $processed = 0;

    /** @var int */
    private $total = 0;

    /** @var array */
    private $ids = [];

    /** @var string */
    protected static $serviceName = 'Toubiz/Legacy/Db';

    /** @var LoggerInterface */
    protected $logger;
    public function injectLogger(LoggerFactory $factory): void
    {
        $this->logger = $factory->getLogger();
    }

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

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

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

    public function synchronize(
        array $configuration,
        \Closure $errorHandlerWrapper,
        \Closure $onProgress
    ): SynchronizationResult {
        $this->logger->info('Starting POI import task', [ 'configuration' => $configuration ]);

        /** @var DbService $service */
        $service = $this->apiServiceHelper->initializeApiServiceWithCommonConfigurationOptions(
            static::$serviceName,
            $configuration
        );

        $this->total = 0;
        $this->processed = 0;
        $this->ids = [];
        $delete = [];
        $result = $service->fetchPointOfInterests(
            $errorHandlerWrapper(
                function (PointOfInterestAdapter $record, ?int $total) use ($onProgress, $configuration, &$delete) {
                    $this->total = (int) $total;
                    $onProgress(++$this->processed, $total);

                    $importer = new ArticleImporter();
                    $importer->setLanguage((string) $configuration['language']);
                    $importer->setClient((string) $configuration['client']);
                    if ($record->getLevelOfMaintenance() >= ($configuration['minLevelOfMaintenance'] ?? 0)) {
                        $imported = $importer->import($record);
                        if ($imported) {
                            $this->ids[] = $imported->getPersistenceObjectIdentifier();
                        }
                    } else {
                        $delete[] = $record->getExternalId();
                    }
                }
            )
        );

        $this->logger->info(sprintf(
            'Processed %d of total %d POIs.'
            . ' Of those %d were imported and %d were ignored as their level of maintenance is lower than %d.'
            . ' %d were ignored as they are hidden in toubiz.',
            $this->processed,
            $this->total,
            count($this->ids),
            count($delete),
            $configuration['minLevelOfMaintenance'] ?? 0,
            count((array) $result->getDeleted())
        ));

        return new SynchronizationResult(
            array_merge((array) $result->getDeleted(), $delete),
            $result->getAll(),
            $this->ids
        );
    }

    public function deleteOld(SynchronizationResult $result, array $configuration, \Closure $onProgress): void
    {
        $this->articleRepository->removeBasedOnSynchronizationResult(
            $result,
            ArticleConstants::TYPE_ATTRACTION,
            (string) $configuration['client'],
            (string) $configuration['language'],
            $onProgress
        );
    }
}
