<?php declare(strict_types=1);
namespace Newland\NeosFiltering\DataSource;

use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Neos\Service\DataSource\AbstractDataSource;
use Neos\Flow\I18n\Translator;
use Neos\Utility\SchemaValidator;
use Neos\Flow\Annotations as Flow;
use Newland\NeosFiltering\Service\FilterDictionaryService;
use Symfony\Component\Yaml\Yaml;
use Neos\Flow\Validation\Error;
use function Safe\file_get_contents;

/**
 * Simple direct data source that outputs options given to it.
 * The arguments given to the data source must at least contain `options` which is an
 * array of arrays each containing `value` and `label`.
 *
 * ```
 * dataSource: 'newland-neosfiltering-direct'
 * dataSourceArguments:
 *     options:
 *         - { label: 'Foo', value: 'foo' }
 *         - { label: 'Bar', value: 'bar' }
 *         - { label: 'Baz', value: 'baz' }
 * ```
 *
 * To use translated labels you can use the `i18n` options:
 *
 * ```
 * dataSource: 'newland-neosfiltering-direct'
 * dataSourceArguments:
 *     i18n:
 *         package: 'Newland.Toubiz.Poi.Neos'
 *         source: 'Views/Filter/Attributes'
 *     options:
 *          # Gets label from `foo` language label
 *         - value: 'foo'
 *
 *          # Custom language label id
 *         - value: 'bar'
 *           i18n: { id: 'title.bar' }
 *
 *          # Fully custom language resolution
 *         - value: 'baz'
 *           i18n:
 *               package: 'Newland.Toubiz.Poi.Neos'
 *               source: 'Views/Filter'
 *               id: 'attributes.title.baz'
 * ```
 */
class DirectDataSource extends AbstractDataSource
{
    protected static $identifier = 'newland-neosfiltering-direct';
    protected const SCHEMA = 'resource://Newland.NeosFiltering/Private/Schema/DirectDataSource.schema.yaml';


    /**
     * @var FilterDictionaryService
     * @Flow\Inject()
     */
    protected $filterDictionaryService;

    /**
     * @var SchemaValidator
     * @Flow\Inject()
     */
    protected $schemaValidator;

    public function getData(NodeInterface $node = null, array $arguments)
    {
        $language = null;
        if ($node !== null) {
            $language = $node->getDimensions()['language'][0] ?? null;
        }

        if (!array_key_exists('filterGroup', $arguments)) {
            throw new \InvalidArgumentException(
                "Invalid data source configuration\n"
            );
        }

        return $this->filterDictionaryService->getFilterGroup($arguments['filterGroup'], $language);
    }

    protected function throwIfInvalid(array $data): void
    {
        $result = $this->schemaValidator->validate($data, Yaml::parse(file_get_contents(static::SCHEMA)));
        if (!$result->hasErrors()) {
            return;
        }

        /** @var Error[][] $flattened */
        $flattened = $result->getFlattenedErrors();
        $errorMessages = [];
        foreach ($flattened as $property => $errors) {
            foreach ($errors as $error) {
                $errorMessages[] = sprintf('- %s: %s', $property, $error->__toString());
            }
        }

        throw new \InvalidArgumentException(
            sprintf(
                "Invalid data source configuration: \n%s",
                implode("\n", $errorMessages)
            )
        );
    }
}
