<?php
namespace Newland\Toubiz\Sync\Neos\Command;

/*
 * 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\AbstractQuery;
use Neos\Flow\Annotations as Flow;
use Newland\Toubiz\Api\ObjectAdapter\EventAdapterInterface;
use Newland\Toubiz\Api\Service\ServiceFactory;
use Newland\Toubiz\Api\Service\Toubiz\Legacy\EventApiService;
use Newland\Toubiz\Sync\Neos\Domain\Model\Event;
use Newland\Toubiz\Sync\Neos\Domain\Repository\EventRepository;
use Newland\Toubiz\Sync\Neos\Importer\EventImporter;

/**
 * Events command controller.
 *
 * Provides commands to manipulate events data.
 *
 * @Flow\Scope("singleton")
 */
class EventsCommandController extends AbstractCommandController
{
    const TYPE_LEGACY_TOUBIZ = 'LegacyToubiz';

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

    /**
     * Synchronize command.
     *
     * Updates local events database from API data source.
     *
     * @todo delete persisted events not being in the API response
     * @param bool $quiet
     * @return void
     * @throws \Exception
     */
    public function synchronizeCommand($quiet = false)
    {
        if (!$quiet) {
            $this->showProgressOnCommandLine();
        }

        // There is currently only one API providing events.
        $configuration = $this->getConfigurationForService('Toubiz/Legacy/EventApi');
        if (!$configuration) {
            throw new \Exception('Toubiz/Legacy/EventApi service not configured!');
        }
        $this->synchronizeEventsFromLegacyToubiz($configuration);
        (new EventImporter())->postImport();

        $this->objectPathMappingService->flushMappings(Event::class);
    }


    /**
     * Removes events from the system according to the given clause.
     * If no WHERE clause is given then all events will be deleted.
     *
     * # Remove all events
     * $ php flow events:remove
     *
     * # Remove events according to WHERE clause
     * $ php flow events:remove --where='event.sourcename="REGIO Konstanz-Bodensee-Hegau e.V."'
     *
     * # Remove single event
     * $ php flow events:remove \
     *      --where="event.Persistence_Object_Identifier='a4625eb4-6e84-4834-969d-c9f1d447408b'"
     *
     *
     * @param string|null $where DQL WHERE clause selecting the event to delete.
     * @throws \Neos\Flow\Persistence\Exception\IllegalObjectTypeException
     */
    public function removeCommand(string $where = null)
    {
        $query = $this->eventRepository->createQueryBuilder('event');
        if ($where) {
            $query->where($where);
            $this->outputLine('Deleting events WHERE ' . $where);
        } else {
            $this->outputLine('Deleting all events');
        }

        $count = (clone $query)->select('COUNT(event) AS count')
            ->getQuery()
            ->execute([], AbstractQuery::HYDRATE_ARRAY)[0]['count'] ?? 0;
        $this->askForConfirmationAndAbortIfNoneGiven(sprintf('Do you really want to remove %d events?', $count));

        $this->output->progressStart($count);
        foreach ($query->getQuery()->execute() as $event) {
            $this->eventRepository->remove($event);
            $this->output->progressAdvance();
        }

        $this->output->progressFinish();
    }


    private function synchronizeEventsFromLegacyToubiz(array $configuration)
    {
        $this->emitStart(static::TYPE_LEGACY_TOUBIZ);

        /** @var EventApiService $toubizService */
        $toubizService = ServiceFactory::get('Toubiz/Legacy/EventApi', $configuration['baseUri'] ?? null);
        $toubizService->setClientName($configuration['client']);
        $toubizService->setApiKey($configuration['apiKey']);

        if (array_key_exists('organizer', $configuration)) {
            $toubizService->setOrganizerId($configuration['organizer']);
        }

        $processed = 0;
        $toubizService->fetchActiveEvents($this->handleImportExceptions(
            function (EventAdapterInterface $adapter) use (&$processed) {
                $this->emitProgress(static::TYPE_LEGACY_TOUBIZ, ++$processed);
                (new EventImporter())->import($adapter);
            }
        ));

        $this->emitEnd(static::TYPE_LEGACY_TOUBIZ);
    }
}
