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

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

use Neos\Neos\Service\DataSource\DataSourceInterface;
use Newland\Contracts\Neos\Filter\DataSourcedFilterItem;
use Newland\Contracts\Neos\Filter\FilterItem;
use Newland\NeosFiltering\Service\DataSourceHelper;
use function Safe\preg_replace;

/**
 * Default implementation for the `DataSourcedFilterItem` interface.
 *
 * Note: This interface only provides a common implementation - you must explicitly implement
 *       the `DataSourcedFilterItem` interface if you want to mark your filter item as data sourced.
 *
 * Additional to the regular data source value (`label` & `value`) a item from a data source may
 * also contain `queryString` to specify a human readable string.
 *
 * Note: When using this trait together with `FilterItemCommon` you will have to explicitly specify
 *      that you want to use `HasDataSource::setState` instead of the one in `FilterItemCommon`:
 * ```
 * use FilterItemCommon, HasDataSource {
 *      use FilterItemCommon::setState instead of FilterItemCommon;
 * }
 * ```
 *
 * @see DataSourcedFilterItem
 */
trait HasDataSource
{

    /** @var DataSourceInterface */
    protected $dataSource;

    /** @var array */
    protected $dataSourceArguments = [];

    /** @var bool */
    protected $filterForAllIfNoneSelected = false;

    public function filterForAllIfNoneSelected(bool $filterForAllIfNoneSelected): void
    {
        $this->filterForAllIfNoneSelected = $filterForAllIfNoneSelected;
    }

    public function setDataSource(DataSourceInterface $dataSource): void
    {
        $this->dataSource = $dataSource;
    }

    public function getDataSource(): ?DataSourceInterface
    {
        return $this->dataSource;
    }

    public function setDataSourceArguments(array $arguments): void
    {
        $this->dataSourceArguments = $arguments;
    }

    public function getDataSourceArguments(): array
    {
        return $this->dataSourceArguments;
    }

    public function setState($state): void
    {
        $this->state = DataSourceHelper::extractValidDataSourceOptionsBasedOnStateStrings(
            $this->getDataFromDataSource(),
            (array) $state
        );
    }

    public function getDataFromDataSource(): array
    {
        $node = null;
        if ($this instanceof FilterItem) {
            $node = $this->getRoot()->getPluginNode();
        }

        /** @var array[] $raw */
        $raw = $this->dataSource->getData($node, $this->dataSourceArguments ?? []);
        $data = [];

        foreach ($raw as $item) {
            $niceValue = preg_replace('/\W/', '-', $item['value']);
            $niceValue = \is_array($niceValue) ? implode('-', $niceValue) : $niceValue;
            $id = sprintf('%s-%s', $this->getId(), $niceValue);
            $item['id'] = $id;
            $data[$id] = $item;
        }

        return array_values($data);
    }

    protected function getStateValues(): array
    {
        return array_column((array) $this->state, 'value');
    }

    protected function getStateValuesForQuery(): array
    {
        return array_column($this->getDataSourceItemsForQuery(), 'value');
    }

    protected function getDataSourceItemsForQuery(): array
    {
        // $this->state are already data source items
        if (count((array) $this->state) > 0) {
            return (array) $this->state;
        }

        if ($this->filterForAllIfNoneSelected) {
            return $this->getDataFromDataSource();
        }

        return [];
    }

    public function getHiddenFieldsIfObstructed(): array
    {
        if (!\is_array($this->state)) {
            return [];
        }

        $fields = [];
        foreach ($this->state as $index => $state) {
            $name = sprintf('%s[%d]', $this->queryString, $index);
            $fields[$name] = $state['value'];
        }
        return $fields;
    }
}
