<?php

namespace App\Http\Controllers;

use App\Helpers\DateHelper;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class Controller extends BaseController
{
    use AuthorizesRequests, ValidatesRequests;

    protected array $data = [
        'filters'   => [],
        'ranges'    => [],
        'dates'     => [],
    ];

    /**
     * @param Model $model
     * @param string ...$columnNames
     * @return void
     */
    protected function createDateFilters(Model $model, string ...$columnNames): void
    {
        foreach ($columnNames as $columnName) {
            $this->data['dates'][$columnName] = [
                'title' => $this->data['header'][$columnName],
                'min'   => DateHelper::getDateForDB($model::query()->min($columnName) ?? ''),
                'max'   => DateHelper::getDateForDB($model::query()->max($columnName) ?? ''),
            ];
        }
    }

    /**
     * @param Model $model
     * @param array $columnNames
     * @return void
     */
    protected function createRangeFilters(Model $model, string ...$columnNames): void
    {
        foreach ($columnNames as $columnName) {
            if(str_ends_with($columnName, '_price')) {
                $this->data['ranges'][$columnName] = [
                    'title' => $this->data['header'][$columnName . '_txt'],
                    'min'   => $model::query()->min($columnName) / 100,
                    'max'   => $model::query()->max($columnName) / 100,
                ];
            } else {
                $this->data['ranges'][$columnName] =  [
                    'title' => $this->data['header'][$columnName ],
                    'min'   => $model::query()->min($columnName),
                    'max'   => $model::query()->max($columnName),
                ];
            }
        }
    }

    /**
     * @param Model $model
     * @param array $columns
     * @return void
     */
    protected function createFilters(Model $model, string ...$columns): void
    {
        foreach ($columns as $column) {

            $uniqueValues = $model::query()->distinct()->get($column)->pluck($column)->toArray();
            foreach ($uniqueValues as $k => $v) {
                if(!$v) {
                    unset($uniqueValues[$k]);
                }
            }
            $result = [];
            foreach ($uniqueValues as $val){
                if(str_ends_with($column, '_id')) {
                    $relation = Str::camel(str_replace('_id', '', $column));
                    $result[$val] = $model::query()->where($column, '=', $val)->first()->$relation->name;
                } else {
                    $result[$val] = $val;
                }
            }
            $this->data['filters'][$column] = [
                'title'     => $this->data['header'][$column],
                'values'    => $result
            ];
        }
    }


    /**
     * @param Model $model
     * @param Request $request
     * @return void
     */
    protected function setSortAndOrderBy(Model $model, Request $request): void
    {
        // ------- setup sort and order --------------------------------------------------------------------------------
        $this->data['sortBy'] = (!empty($request->sortBy))
            ? Str::replace('_txt', '', $request->sortBy) // remove '_txt' fields modifier
            : $model::DEFAULT_SORT_BY ?? 'created_at';

        // check for sortBy is valid field
        $p = new $model();
        if(!in_array($this->data['sortBy'], array_merge(['id', 'created_at'], $p->getFillable()))) {
            $this->data['sortBy'] = $model::DEFAULT_SORT_BY ?? 'created_at';
        }

        // set order
        $this->data['orderBy'] = (!empty($request->order)) ? 'desc' : 'asc';
    }

    /**
     * @param Builder $query
     * @param Request $request
     * @return void
     */
    protected function acceptFilters(Builder $query, Request $request): void
    {
        // accept filters
        if(!empty($request->filters) && is_array($request->filters)) {
            foreach ($request->filters as $filterName => $filterValue) {
                if(!$filterValue) continue;

                if(Str::contains($filterName, 'price')) {
                    $filterValue = $filterValue * 100;
                }

                if(Str::endsWith($filterName, '_from')) {
                    if(is_string($filterValue) && DateHelper::isDate($filterValue)) {
                        $filterValue .= ' 00:00:00';
                    }
                    $query->where(Str::replace('_from', '', $filterName), '>=', $filterValue);
                } elseif(Str::endsWith($filterName, '_to')) {
                    if(is_string($filterValue) && DateHelper::isDate($filterValue)) {
                        $filterValue .= ' 23:59:59';
                    }
                    $query->where(Str::replace('_to', '', $filterName), '<=', $filterValue);
                } else {
                    $query->where($filterName, '=', $filterValue);
                }
            }
        }
    }

    /**
     * @param Builder $query
     * @param Request $request
     * @return void
     */
    protected function acceptSearch(Builder $query, Request $request): void
    {
        // accept search
        if(!empty($request->s)) {
            $s = $request->s;
            $searchFields = $this->data['searchFields'];
            $query->where(function ($query) use ($searchFields, $s) {
                foreach ($searchFields as $searchField) {
                    if(Str::contains($searchField, '-')) {
                        list($relation, $column) = explode('-', $searchField);
                        $query->orWhereHas($relation, function ($query) use ($s, $column) {
                           $query->where($column, 'LIKE', "%{$s}%");
                        });
                    } else {
                        $query->orWhere($searchField, 'LIKE', '%' . $s . '%');
                    }
                }
            });
        }
    }

}
