<?php

namespace App\Lib\RequestQueryBuilder;

use Illuminate\Http\Request;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;

class RequestQueryBuilder
{
    protected $defaultSortField = '-id';

    protected static int $pageSize = 15;

    public function __construct(
        protected Request $request,
        protected Builder $builder,
    ) {}

    public static function setDefaultPageSize(int $pageSize)
    {
        static::$pageSize = $pageSize;
    }

    public static function make(Request $request, Builder $builder)
    {
        return new static($request, $builder);
    }

    public function allowedFilters(Filters\Filter ...$allowedFilters)
    {
        $filters = $this->request->input('filter', []);

        $this->builder->where(
            function ($query) use ($filters, $allowedFilters) {
                /** @var Filters\Filter $allowedFilter */
                foreach ($allowedFilters as $allowedFilter) {
                    $allowedField = $allowedFilter->getField();

                    if (!\array_key_exists($allowedField, $filters)) {
                        continue;
                    }

                    $value = $filters[$allowedField];

                    if (empty($value)) {
                        continue;
                    }

                    $allowedFilter->apply($this->builder, $value);
                }
            }
        );

        return $this;
    }

    public function setDefaultSortField(string $field)
    {
        $this->defaultSortField = $field;

        return $this;
    }

    /**
     * @param string[] $allowedSorts
     */
    public function allowedSorts(array $allowedSorts)
    {
        $sort = $this->request->input('sort') ?? $this->defaultSortField;
        $sortFields = \explode(',', $sort);

        foreach ($sortFields as $sortField) {
            $direction = 'asc';

            if (\str_starts_with($sortField, '-')) {
                $direction = 'desc';
                $sortField = \substr($sortField, 1);
            }

            if (!\in_array($sortField, $allowedSorts)) {
                continue;
            }

            $this->builder->orderBy($sortField, $direction);
        }

        return $this;
    }

    public function paginate(
        ?int $perPage = null,
        array $columns = ['*'],
        string $pageName = 'page[number]',
        ?int $page = 1,
    ): LengthAwarePaginator
    {
        $perPage = $perPage ?? static::$pageSize;
        $size = $this->request->input('page.size', $perPage);
        $number = $this->request->input('page.number', $page);
        $pagination = $this->builder->paginate(
            perPage: $size,
            columns: $columns,
            pageName: $pageName,
            page: $number,
        );

        return $pagination->withQueryString();
    }

    public function builder()
    {
        return $this->builder;
    }

    public function query()
    {
        return $this->builder();
    }
}
