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

/*
 * This file is part of the "toubiz-map-neos" package.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 */

use Neos\ContentRepository\Domain\Factory\NodeFactory;
use Neos\ContentRepository\Domain\Model\Node;
use Neos\ContentRepository\Domain\Model\NodeData;
use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\ContentRepository\Domain\Model\Workspace;
use Neos\ContentRepository\Domain\Repository\NodeDataRepository;
use Neos\ContentRepository\Domain\Repository\WorkspaceRepository;
use Neos\Eel\FlowQuery\FlowQuery;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Cli\CommandController;
use Neos\Neos\Domain\Repository\SiteRepository;
use Neos\Neos\Domain\Service\ContentContextFactory;
use Newland\NeosCommon\LinkHandler\LinkingService;
use Newland\Toubiz\Map\Neos\Provider\Contract\ProviderContext;
use Newland\Toubiz\Map\Neos\Provider\MapDataProviderService;
use Newland\NeosCommon\Service\ControllerContextFactory;
use Newland\Toubiz\Map\Neos\Provider\Pagination\Paginator;
use Symfony\Component\Console\Helper\ProgressBar;

/**
 * Map command controller.
 *
 * Provides commands to manipulate map data.
 *
 * @Flow\Scope("singleton")
 */
class MapCommandController extends CommandController
{
    const NODE_TYPE = 'Newland.Toubiz.Map.Neos:Map';

    /**
     * @var MapDataProviderService
     * @Flow\Inject()
     */
    protected $mapDataProviderService;

    /**
     * @Flow\Inject()
     * @var NodeDataRepository
     */
    protected $nodeDataRepository;

    /**
     * @var WorkspaceRepository
     * @Flow\Inject()
     */
    protected $workspaceRepository;

    /**
     * @var SiteRepository
     * @Flow\Inject()
     */
    protected $siteRepository;

    /**
     * @var NodeFactory
     * @Flow\Inject()
     */
    protected $nodeFactory;

    /**
     * @var ContentContextFactory
     * @Flow\Inject()
     */
    protected $contentContextFactory;

    /**
     * @var LinkingService
     * @Flow\Inject()
     */
    protected $linkingService;

    /**
     * @var ControllerContextFactory
     * @Flow\Inject()
     */
    protected $controllerContextFactory;


    /**
     * Command to build an article cache to feed the map.
     *
     * As the map is based on a lot of marker data which requires a lot of
     * computing resources when fetching on-demand, static data is
     * built to assign to the view. This reduces server load.
     *
     * @return void
     */
    public function buildArticleCacheCommand()
    {
        $this->mapDataProviderService->clearCache();

        foreach ($this->siteNodes() as $siteNode) {
            /** @var Node $siteNode */

            $nodes = (new FlowQuery([ $siteNode ]))->find('[instanceof Newland.Toubiz.Map.Neos:Map]');

            foreach ($nodes as $node) {
                $context = new ProviderContext($node, null);
                $markers = $this->mapDataProviderService->getMarkers($context);

                $bar = new ProgressBar($this->output->getOutput());
                $bar->setFormat('%current% [%bar%] ' . $node->getContextPath());
                $bar->start($markers->numberOfPages());

                $numberOfPages = $markers->numberOfPages();
                for ($i = 1; $i <= $numberOfPages; $i++) {
                    $markers->get($i);
                    $bar->advance();
                }

                $this->mapDataProviderService->getFilterItems($context);
                $bar->finish();
                $this->outputLine();
            }
        }
    }

    private function siteNodes(): \Generator
    {
        /** @var Workspace[] $workspaces */
        $workspaces = $this->workspaceRepository->findAll();
        foreach ($workspaces as $workspace) {
            /** @var NodeData[] $nodeData */
            $nodeData = $this->nodeDataRepository->findByParentWithoutReduce('/sites', $workspace);
            foreach ($nodeData as $data) {
                yield $this->nodeFactory->createFromNodeData(
                    $data,
                    $this->contentContextFactory->create(
                        [
                            'workspaceName' => $data->getWorkspace()->getName(),
                            'invisibleContentShown' => false,
                            'inaccessibleContentShown' => false,
                            'removedContentShown' => false,
                            'dimensions' => $data->getDimensionValues(),
                        ]
                    )
                );
            }
        }
    }
}
