<?php declare(strict_types=1);

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

use Newland\Toubiz\Api\Constants\Language;
use Newland\Toubiz\Api\Service\Toubiz\ApiV1\EventService;
use Newland\Toubiz\Api\Service\Toubiz\ApiV1\ObjectAdapter\EventAdapter;
use Newland\Toubiz\Sync\Neos\Command\Helper\ApiServiceHelper;
use Newland\Toubiz\Sync\Neos\Command\Helper\ConfigurationHelper;
use Newland\Toubiz\Sync\Neos\Domain\Repository\EventRepository;
use Newland\Toubiz\Sync\Neos\Importer\EventImporter;
use Newland\Toubiz\Sync\Neos\Logging\LoggerFactory;
use Psr\Log\LoggerInterface;
use Neos\Flow\Annotations as Flow;

class SyncEventsFromToubizApiV1 implements SynchronizationTask, DeletesOld
{
    protected const SERVICE_NAME = 'Toubiz/ApiV1/Event';

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

    /** @var string[] */
    protected $ids = [];

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

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

    /** @var LoggerInterface */
    protected $logger;

    public function injectLogger(LoggerFactory $loggerFactory): void
    {
        $this->logger = $loggerFactory->getLogger();
    }

    public function name(): string
    {
        return static::SERVICE_NAME;
    }

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

        foreach ($configuration['clients'] ?? [] as $clientKey => $clientConfig) {
            if (!array_key_exists('request', $clientConfig)) {
                $clientConfig['request'] = [
                    'filter' => [
                        'clientIncludingManaged' => $clientConfig['client'],
                    ],
                    'unlicensed' => true,
                ];
                $this->logger->info(
                    sprintf(
                        'Newland.Toubiz.Sync.Neos.services.%s.%s: `request` not specified using default request %s',
                        static::SERVICE_NAME,
                        $clientKey,
                        json_encode($clientConfig['request'])
                    )
                );
            }
            foreach ($clientConfig['languages'] ?? Language::values() as $language) {
                $clientConfig['language'] = $language;
                yield $configurationHelper->mergeWithDefaults($clientConfig);
            }
        }
    }

    public function synchronize(
        array $configuration,
        \Closure $errorHandlerWrapper,
        \Closure $onProgress
    ): SynchronizationResult {
        /** @var EventService $service */
        $service = $this->apiServiceHelper->initializeApiServiceWithCommonConfigurationOptions(
            static::SERVICE_NAME,
            $configuration
        );
        $service->setDetailUriTemplates($configuration['detailUriTemplates'] ?? []);

        $this->processed = 0;
        $this->ids = [];

        $importer = new EventImporter();
        $importer->setLanguage((string) $configuration['language']);
        $importer->setClient((string) $configuration['client']);

        $result = $service->fetchEvents(
            $errorHandlerWrapper(
                function (EventAdapter $record, ?int $total) use ($onProgress, $importer, $configuration) {
                    $onProgress(++$this->processed, $total);
                    $record->setLanguage((string) $configuration['language']);

                    $imported = $importer->import($record);
                    if ($imported) {
                        $this->ids[] = (string) $imported->getPersistenceObjectIdentifier();
                    }
                }
            )
        );

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

    public function synchronizeSingle(
        string $id,
        array $configuration,
        \Closure $errorHandlerWrapper
    ): SynchronizationResult {
        /** @var EventService $service */
        $service = $this->apiServiceHelper->initializeApiServiceWithCommonConfigurationOptions(
            static::SERVICE_NAME,
            $configuration
        );
        $service->setDetailUriTemplates($configuration['detailUriTemplates'] ?? []);

        $this->processed = 0;
        $this->ids = [];

        $importer = new EventImporter();
        $importer->setLanguage((string) $configuration['language']);
        $importer->setClient((string) $configuration['client']);

        $result = $service->fetchEvent(
            $id,
            $errorHandlerWrapper(
                function (EventAdapter $record) use ($importer, $configuration) {
                    $record->setLanguage((string) $configuration['language']);

                    $imported = $importer->import($record);
                    if ($imported) {
                        $this->ids[] = (string) $imported->getPersistenceObjectIdentifier();
                    }
                }
            ),
            $errorHandlerWrapper(
                function (string $id) use ($configuration) {
                    $this->eventRepository->removeBasedOnSynchronizationResult(
                        new SynchronizationResult([ $id ], [], []),
                        (string) $configuration['client'],
                        (string) $configuration['language'],
                        function () {
                        },
                    );
                },
            ),
        );

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

    public function deleteOld(SynchronizationResult $result, array $configuration, \Closure $onProgress): void
    {
        if ($configuration['delta'] ?? null) {
            $this->logger->warning(
                'Old events can only be deleted when running the sync task without specifying a delta (fullsync)'
            );
            return;
        }

        $this->eventRepository->removeBasedOnSynchronizationResult(
            $result,
            (string) $configuration['client'],
            (string) $configuration['language'],
            $onProgress
        );
    }
}
