<?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 Newland\Toubiz\Api\ObjectAdapter\OfferAdapterInterface;
use Newland\Toubiz\Api\Service\ServiceFactory;
use Neos\Flow\Annotations as Flow;
use Newland\Toubiz\Api\Service\Tportal\ApiService;
use Newland\Toubiz\Api\Service\Tportal\ObjectAdapter\OfferAdapter;
use Newland\Toubiz\Sync\Neos\Command\Helper\ApiServiceHelper;
use Newland\Toubiz\Sync\Neos\Domain\Model\Offer;
use Newland\Toubiz\Sync\Neos\Domain\Repository\OfferRepository;
use Newland\Toubiz\Sync\Neos\Importer\OfferImporter;

/**
 * @Flow\Scope("singleton")
 */
class OfferCommandController extends AbstractCommandController
{
    protected const TYPE_TPORTAL = 'tportal';

    /**
     * @var OfferRepository
     * @Flow\Inject()
     */
    protected $offerRepository;

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

    /**
     * Synchronize command.
     *
     * Updates local Offer database from API data source.
     *
     * @param bool $quiet
     * @param bool $finishHooks
     * @return void
     */
    public function synchronizeCommand($quiet = false, bool $finishHooks = true): void
    {
        if (!$quiet) {
            $this->showProgressOnCommandLine();
        }

        $configuration = $this->configurationHelper->getConfigurationForService('Tportal/Api');

        if ($configuration === null) {
            return;
        }

        foreach ($configuration['clients'] as $clientConfiguration) {
            foreach ($clientConfiguration['languages'] ?? $configuration['languages'] ?? [ 'de' ] as $language) {
                $this->synchronizeOffersFromTportal($clientConfiguration, $language);
            }
        }

        if ($finishHooks) {
            $this->objectPathMappingService->flushMappings(Offer::class);
            $this->emitFinish();
        }
    }

    /**
     * Removes offers from the system according to the given clause.
     * If no WHERE clause is given then all offers will be deleted.
     *
     * # Remove all offers
     * $ php flow offer:remove
     *
     * # Remove offers according to WHERE clause
     * $ php flow offer:remove --where='offer.name LIKE "Segway%"'
     *
     * # Remove single offer
     * $ php flow offer:remove \
     *      --where="offer.Persistence_Object_Identifier='a4625eb4-6e84-4834-969d-c9f1d447408b'"
     *
     *
     * @param string|null $where DQL WHERE clause selecting the offers to delete.
     * @param string|null $language Language to operate in. Operates on all languages if none specified.
     */
    public function removeCommand(string $where = null, string $language = null): void
    {
        if ($language === null) {
            $this->offerRepository->withoutLanguageHandling(
                function () use ($where) {
                    $this->remove($where);
                }
            );
        } else {
            $this->offerRepository->setLanguage($language);
            $this->remove($where);
        }
    }

    private function remove(string $where = null)
    {
        $query = $this->offerRepository->createQueryBuilder('offer');
        if ($where) {
            $query->where($where);
            $this->outputLine('Deleting offers WHERE ' . $where);
        } else {
            $this->outputLine('Deleting all offers');
        }

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

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

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


    private function synchronizeOffersFromTportal(array $configuration, string $language): void
    {
        $client = $configuration['client'];
        $configuration['language'] = $language;
        $this->emitStart(static::TYPE_TPORTAL, compact('language', 'client'));

        /** @var \Newland\Toubiz\Api\Service\Tportal\ApiService $service */
        $service = $this->apiServiceHelper->initializeApiServiceWithCommonConfigurationOptions(
            'Tportal/Api',
            $configuration
        );

        $importer = new OfferImporter();
        $processed = 0;
        $service->fetchOffers(
            $this->wrapImportClosure(
                function (OfferAdapter $record) use ($importer, &$processed, $language, $configuration, $client) {
                    $record->setDetailUriTemplate($configuration['detailUri'] ?? null);
                    $this->emitProgress(static::TYPE_TPORTAL, ++$processed, compact('language', 'client'));
                    $importer->import($record);
                }
            )
        );

        $this->emitEnd(static::TYPE_TPORTAL, compact('language', 'client'));
    }
}
