<?php declare(strict_types=1);

namespace Newland\Toubiz\Sync\Neos\Controller\Backend;

use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Flow\Annotations as Flow;
use Neos\Neos\Controller\Module\AbstractModuleController;
use Neos\Neos\Domain\Repository\SiteRepository;
use Newland\Contracts\Neos\Filter\FilterRoot;
use Newland\NeosCommon\Domain\Repository\NodeRepository;
use Newland\Toubiz\Api\Constants\Language;
use Newland\Toubiz\Sync\Neos\Configuration\ConfigurationProvider;
use Newland\Toubiz\Sync\Neos\Configuration\MultiProvider;
use Newland\Toubiz\Sync\Neos\Domain\Repository\RecordConfigurationRepository;
use Newland\Toubiz\Sync\Neos\Translation\CurrentLanguageService;

class ConfigurationController extends AbstractModuleController
{
    protected const PER_PAGE = 50;

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

    /**
     * @var NodeRepository
     * @Flow\Inject()
     */
    protected $nodeRepository;

    /**
     * @var RecordConfigurationRepository
     * @Flow\Inject()
     */
    protected $recordConfiguration;

    /**
     * @var array
     * @Flow\InjectConfiguration(path="configurationProviders")
     */
    protected $configurationProviderSettings;

    /** @var ConfigurationProvider */
    protected $configurationProvider;

    /**
     * @var CurrentLanguageService
     * @Flow\Inject()
     */
    protected $currentLanguageService;

    public function initializeAction(): void
    {
        parent::initializeAction();

        $classNames = array_keys(array_filter($this->configurationProviderSettings));
        $instances = array_map([ $this->objectManager, 'get' ], $classNames);
        $this->configurationProvider = new MultiProvider($instances);
    }

    public function indexAction(string $type = null, int $page = 1, string $language = Language::DE): void
    {
        $this->currentLanguageService->updateLanguage($language);

        $sites = $this->getSites();
        $node = $this->getCurrentNode($sites);

        $type = $type ?? $this->configurationProvider->types()[0];
        $this->configurationProvider->setType($type);
        $filter = $this->initializeFilter($node, $page, $type);
        $count = $this->configurationProvider->count($filter);

        $items = [];
        foreach ($this->configurationProvider->fetch($filter) as $entity) {
            $items[] = [
                'entity' => $entity,
                'entityType' => \get_class($entity),
                'actions' => $this->configurationProvider->actions($entity),
                'tableCells' => $this->configurationProvider->tableItems($entity),
            ];
        }

        $this->view->assignMultiple(
            [
                'types' => $this->configurationProvider->types(),
                'type' => $type,
                'page' => $page,
                'pageBounds' => [
                    ($page - 1) * static::PER_PAGE + 1,
                    min($count, $page * static::PER_PAGE),
                ],
                'filter' => $filter,
                'items' => $items,
                'total' => $count,
                'lastPage' => ceil($count / static::PER_PAGE),
                'language' => $language,
                'languages' => Language::values(),
            ]
        );
    }

    public function runAction(string $type, string $actionKey, string $itemType, string $itemId): void
    {
        $this->configurationProvider->setType($type);
        $item = $this->persistenceManager->getObjectByIdentifier($itemId, $itemType);
        $action = $this->configurationProvider->actions($item)[$actionKey];

        $this->recordConfiguration->updateConfiguration($item, [ $action, 'invoke' ], $action->getPersistChanges());

        $this->redirectToRequest($this->request->getReferringRequest());
    }

    private function initializeFilter(NodeInterface $node, int $page, string $type): FilterRoot
    {
        $filter = $this->configurationProvider->filter($node);
        $filter->setState($this->request->getMainRequest()->getArguments());
        $filter->addTemplateRootPaths([ 'resource://Newland.Toubiz.Sync.Neos/Private/Templates/Backend/Filter' ]);

        if (method_exists($filter, 'setPageSize')) {
            $filter->setPageSize(static::PER_PAGE);
        }

        $filter->setState([ 'page' => $page ]);
        $filter->addHiddenFields([ 'moduleArguments[type]' => $type ]);
        return $filter;
    }

    private function getSites(): array
    {
        $sites = $this->siteRepository->findAll();
        $result = [];
        foreach ($sites as $site) {
            $node = $this->nodeRepository->findOneByNodePath(
                sprintf('/sites/%s', $site->getNodeName()),
                true
            );
            if ($node) {
                $result[] = [ 'site' => $site, 'node' => $node ];
            }
        }
        return $result;
    }

    private function getCurrentNode(array $sites): NodeInterface
    {
        $node = null;
        if ($this->request->hasArgument('node')) {
            $node = $this->nodeRepository->findOneByNodePath($this->request->getArgument('node'));
        }
        return $node ?? $sites[0]['node'];
    }
}
