<?php

namespace Newland\Toubiz\Poi\Neos\DataSource;

/*
 * This file is part of the "toubiz-poi-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\Model\NodeInterface;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\I18n\Translator;
use Neos\Neos\Service\DataSource\AbstractDataSource;
use Newland\NeosCommon\Translation\LanguageAwareDataSource;
use Newland\Toubiz\Poi\Neos\Domain\Repository\TopicRepository;

/**
 * Categories data source service.
 *
 * @Flow\Scope("singleton")
 */
class TopicsDataSource extends AbstractDataSource
{
    use LanguageAwareDataSource;

    /**
     * Data source identifier.
     *
     * @var string
     */
    protected static $identifier = 'newland-toubiz-poi-neos-topics';

    /**
     * @var Translator
     * @Flow\Inject()
     */
    protected $translator;

    /**
     * @var TopicRepository
     * @Flow\Inject()
     */
    protected $topicRepository;

    /**
     * Get requested data from source.
     *
     * The return value must be JSON serializable data structure.
     *
     * @api
     * @param NodeInterface $node The node that is currently edited (optional)
     * @param array $arguments Additional arguments (key / value)
     * @return mixed JSON serializable data
     * @throws \Neos\ContentRepository\Exception\NodeException
     */
    public function getData(NodeInterface $node = null, array $arguments = [])
    {
        $this->topicRepository->setLanguage($this->getLanguage($node));

        $articleType = $this->getArticleType($node);
        $typesToSelect = $this->getTypesToSelect($arguments, $articleType);

        $topics = $this->fetchTopics($typesToSelect, $articleType);

        $data = [];
        $ids = [];
        foreach ($topics as $topic) {
            $ids[$topic->getCombinedIdentifier()] = true;
            $data[] = [
                'label' => $topic->getTitle(),
                'value' => $topic->getCombinedIdentifier(),
                'group' => $this->groupLabel($topic->getType()),
            ];
        }

        if ($node !== null) {
            $data = $this->addUnknownPreselectedTopics($data, $node, $ids);
        }

        return $data;
    }

    private function fetchTopics(array $typesToSelect, ?string $articleType): array
    {
        $topics = [];
        foreach ($typesToSelect as $type) {
            $topics = array_merge(
                $topics,
                $this->topicRepository->findAllForType($type, [ 'articleType' => $articleType ])
            );
        }
        return $topics;
    }

    private function addUnknownPreselectedTopics(array $data, NodeInterface $node, array $ids): array
    {
        foreach ($node->getProperty('preselectedTopics') ?? [] as $preselected) {
            if (!array_key_exists($preselected, $ids)) {
                $data[] = [
                    'value' => $preselected,
                    'label' => $this->translate('attribute.unknown') . ' ' . $preselected,
                    'group' => $this->translate('type.unknown'),
                ];
            }
        }

        return $data;
    }

    private function groupLabel(string $type): string
    {
        switch ($type) {
            case TopicRepository::TYPE_ATTRIBUTE:
                return $this->translate('type.attributes') ?? $type;
            case TopicRepository::TYPE_CATEGORY:
                return $this->translate('type.categories') ?? $type;
            case TopicRepository::TYPE_TAG:
                return $this->translate('type.tags') ?? $type;
            default:
                return $type;
        }
    }

    /**
     * @param string $id
     * @return string|null
     */
    private function translate(string $id)
    {
        return $this->translator->translateById(
            $id,
            [],
            null,
            null,
            'Models/Topic',
            'Newland.Toubiz.Poi.Neos'
        );
    }

    private function getArticleType(?NodeInterface $node): ?string
    {
        if ($node && $node->hasProperty('articleType')) {
            return $node->getProperty('articleType');
        }
        return null;
    }

    private function getTypesToSelect(array $arguments, ?string $articleType): array
    {
        /*
         * Added for legacy support of older teaser lists
         * Previous teaser list would not scope by articleType,
         * so the decision was not to return properties before the legacy node is removed
        */
        if ($articleType === null) {
            return [ TopicRepository::TYPE_CATEGORY ];
        }

        return $arguments['types'] ?? $this->topicRepository->getTypes();
    }
}
