<?php

namespace Ignite\Core\Models\Grid\Scopes;

use Ignite\Core\Models\Grid\AbstractTable;
use Illuminate\Http\Request;
use Nwidart\Modules\Facades\Module;

class FilterFactory
{
    /**
     * @var array
     */
    protected $filters = [];

    /**
     * Collect the filters from the configuration for each module.
     *
     * @return array
     */
    public function collect()
    {
        if (empty($this->filters)) {
            $this->filters = $this->merge(config('core.report.filters', []), $this->collectFromModules());
        }

        return $this->filters;
    }

    /**
     * Apply the configured filters in the request to the given table.
     *
     * @param AbstractTable $table
     * @param Request $request
     *
     * @return AbstractTable
     */
    public function apply(AbstractTable $table, $requestFilters)
    {
        $filters = $this->collect();

        $tableClass = get_class($table);

        $activeFilters = $filters[$tableClass] ?? [];

        $activeFilter = array_shift($activeFilters);

        if (empty($activeFilter)) {
            return $table;
        }

        foreach ($activeFilter as $filter) {
            if (isset($filter['scope']) && isset($filter['field'])) {
                $table->addScope(app(
                    $filter['scope'],
                    $requestFilters[$filter['field']] ?? $this->getDefaultParams($filter)
                ));
            }
        }

        return $table;
    }

    /**
     * The default parameters for the filter when none have been set.
     *
     * @return array
     */
    protected function getDefaultParams($settings)
    {
        $default = [];
        $request = $settings['request'] ?? [];

        foreach ($request as $param) {
            $default[$param] = null;
        }

        return array_merge($default, [
            'field' => $settings['field']
        ]);
    }

    /**
     * Collect filters from the registered modules.
     *
     * @return array
     */
    protected function collectFromModules()
    {
        $filters = [];
        $configs = collect(Module::getOrdered())->except('Core');

        foreach ($configs as $module) {
            $filters = $this->merge($filters, config("{$module->getLowerName()}.report.filters", []));
        }

        return $filters;
    }

    /**
     * Merge two filter arrays together.
     *
     * @param array $source
     * @param array $target
     *
     * @return array
     */
    protected function merge(array $source, array $target)
    {
        if (empty($source) && empty($target)) {
            return [];
        }

        if (! empty($source) && empty($target)) {
            return $source;
        }

        if (empty($source) && ! empty($target)) {
            return $target;
        }

        $filters = [];

        foreach ($source as $class => $filter) {
            $filters[$class][] = $filter;
        }

        foreach ($target as $class => $filter) {
            $filters[$class][] = $filter;
        }

        return $filters;
    }
}
