<?php declare(strict_types=1);

namespace App\Services\Search;

use App\Models\Role;
use Illuminate\Container\Container;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Pagination\CursorPaginator;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Collection;
use App\Contracts\Search as SearchContract;

final readonly class Search implements SearchContract
{
    public function __construct(
        private Relation|Builder $query
    ) { }

    public function all(): Collection
    {
        return $this->query->get();
    }

    public function get(int $limit): Collection
    {
        return $this->query->limit($limit)->get();
    }

    public function pagination(int $limit, int $page = 1): LengthAwarePaginator
    {
        if ($page > 100) {
            return $this->paginationPerfomance($limit, $page);
        }
        return $this->query->paginate($limit, page: $page)->withQueryString();
    }

    public function cursorPaginate(int $limit): CursorPaginator
    {
        return $this->query->cursorPaginate($limit);
    }

    private function paginationPerfomance(int $limit, int $page = 1): LengthAwarePaginator
    {
        $total = $this->query->clone()->count();
        $options = [
            'path' => Paginator::resolveCurrentPath(),
            'pageName' => 'page',
        ];
        
        $result = collect();
        if ($total > 0) {
            $result = $this->subQuery($limit, $page);
        }

        $pagination = Container::getInstance()->makeWith(LengthAwarePaginator::class, [
            'items' => $result,
            'total' => $total,
            'perPage' => $limit,
            'currentPage' => $page,
            'options' => $options
        ]);

        return $pagination->withQueryString();
    }

    private function subQuery(int $limit, int $page): Collection
    {
        $table = $this->query->getModel()->getTable();
        return $this->query->getModel()::query()
            ->select($table.'.*')
            ->with($this->query->getEagerLoads())
            ->from(
                clone $this->query->select('id')->forPage($page, $limit),
                'q'
            )->join($table.' as '.$table, $table.'.id', '=', 'q.id')
            ->get();
    }
}
