<?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\RegionAdapterInterface;
use Newland\Toubiz\Api\Service\ServiceFactory;
use Newland\Toubiz\Sync\Neos\Domain\Repository\RegionRepository;
use Newland\Toubiz\Sync\Neos\Domain\Repository\KeyValueRepository;
use Newland\Toubiz\Sync\Neos\Importer\RegionImporter;
use Newland\Toubiz\Api\Service\Sisag;

/**
 * Snow report command controller.
 *
 * Provides commands to manipulate snow report data.
 *
 * @Flow\Scope("singleton")
 */
class SnowReportCommandController extends AbstractCommandController
{
    public const DAILY_TIP = 'snowReport_dailyTip';
    protected const TYPE_SISAG = 'sisag';

    /**
     * @Flow\InjectConfiguration
     * @var array
     */
    protected $configuration;

    /**
     * @var RegionRepository
     * @Flow\Inject()
     */
    protected $regionRepository;

    /**
     * @var KeyValueRepository
     * @Flow\Inject()
     */
    protected $keyValueRepository;

    /**
     * Synchronize command.
     *
     * Updates local snow report database from API data source.
     *
     * @throws \Exception
     */
    public function synchronizeCommand(bool $quiet = false): void
    {
        if (!$quiet) {
            $this->showProgressOnCommandLine();
        }

        // There is currently only one API providing snow reports.
        $configuration = $this->configurationHelper->getConfigurationForService('Sisag/Api');
        if (!$configuration) {
            throw new \Exception('Sisag/Api service not configured!');
        }

        foreach ($configuration['clients'] as $clientConfiguration) {
            $this->synchronizeFromSisag($clientConfiguration);
        }

        $this->emitFinish();
    }

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

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

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

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


    private function synchronizeFromSisag(array $configuration): void
    {
        $this->emitStart(static::TYPE_SISAG);

        /** @var Sisag\ApiService $service */
        $service = ServiceFactory::get('Sisag/Api', $configuration['baseUri'] ?? null);
        $service->setClientName($configuration['client']);

        $regions = $service->fetchRegions();
        foreach ($regions as $i => $region) {
            $this->emitProgress(static::TYPE_SISAG, $i);

            $this->wrapImportClosure(function (RegionAdapterInterface $regionAdapter) {
                (new RegionImporter())->import($regionAdapter);
            })($region);
        }

        $dailyTip = $service->fetchAdditionalInformation();
        if ($dailyTip !== null) {
            $this->keyValueRepository->store(static::DAILY_TIP, $dailyTip);
        }

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