<?php

namespace Snaju\Inception\Http\Response\Pagination;

use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Tools\Pagination\Paginator;
use Snaju\Inception\Http\Request\WebRequest;
use Snaju\Inception\InceptionCore;

class DefaultPaginator
{

    protected static int $defaultPageNum = 1;

    protected static int $defaultPageLimit = 100;

    protected string $baseUrl = 'https://localhost:8080';

    protected string $modelUrlPath = '';

    protected QueryBuilder $query;

    protected int $page;

    protected int $recordsPerPage;

    protected int $totalRecords = 0;

    protected Paginator $paginator;

    protected bool $usesCursor = false;

    public static function getPageNumFromRequest(WebRequest $request): int
    {
        $params = $request->getQueryParams();
        $page = self::$defaultPageNum;

        if (isset($params['page'])) {
            switch (gettype($params['page'])) {
                case 'string':
                    $page = (int)$params['page'];
                    break;
                case 'array':
                    $pageObj = $params['page'];

                    if (isset($pageObj['num'])) {
                        $page = (int)$pageObj['num'];
                    }
                    break;
                default:
                    $page = self::$defaultPageNum;
            }
        }

        if ($page <= 0 || ! is_numeric($page)) {
            $page = self::$defaultPageNum;
        }

        return $page;
    }

    public static function getPageLimitFromRequest(WebRequest  $request): int
    {
        $params = $request->getQueryParams();
        $limit = self::$defaultPageLimit;

        if (isset($params['limit'])) {
            $limit = (int)$params['limit'];
        }

        if (isset($params['page'])) {
            switch (gettype($params['page'])) {
                case 'array':
                    $pageObj = $params['page'];

                    if (isset($pageObj['limit'])) {
                        $limit = (int)$pageObj['limit'];
                    }
                    break;
            }
        }

        if ($limit <= 0 || ! is_numeric($limit)) {
            $limit = self::$defaultPageLimit;
        }

        return $limit;
    }

    public function __construct(QueryBuilder $query, int $recordsPerPage = 100)
    {
        $request = InceptionCore::getRequest();
        $page = self::getPageNumFromRequest($request);
        $limit = self::getPageLimitFromRequest($request);


        // TODO: add `before` query param to grab records before a certain id
        if ($request->contains('after')) {
            $this->usesCursor = true;
            $after = $request->getQueryParams()['after'];
            $query = $query->andWhere('m.id >= :after')->setParameter('after', $after);
        }

        // If limit set by client is greater than limit, set max records
        // only applies for requests where before or after isn't used
        if (! $request->contains('before') && ! $request->contains('after')) {
            $recordsPerPage = $limit <= $recordsPerPage ? $limit : $recordsPerPage;
        }
        else if ($limit) {
            $recordsPerPage = $limit;
        }

        $query = $query->setFirstResult(($page - 1) * $recordsPerPage)
            ->setMaxResults($recordsPerPage);

        $request = InceptionCore::getRequest();

        if (! empty(env('APP_URL'))) {
            $this->baseUrl = (string)env('APP_URL');
        }
        else {
            // Get the HTTP WebRequest and set the base url from the fqdn
            $this->baseUrl = (string)$request->getUri()->getFQDN();
        }

        $this->modelUrlPath = $request->getUri()->getPath();
        $this->query = $query;
        $this->page = $page;
        $this->recordsPerPage = $recordsPerPage;
    }

    public function setPaginator()
    {
        $this->paginator = new Paginator($this->query);
        $this->totalRecords = count($this->paginator);
    }

    public function getPaginator(): Paginator
    {
        $this->setPaginator();
        return $this->paginator;
    }

    /**
     * @return string
     */
    public function getBaseUrl(): string
    {
        return $this->baseUrl;
    }

    /**
     * @param string $baseUrl
     */
    public function setBaseUrl(string $baseUrl): void
    {
        $this->baseUrl = $baseUrl;
    }

    /**
     * @return string
     */
    public function getModelUrlPath(): string
    {
        return $this->modelUrlPath;
    }

    /**
     * @param string $modelUrlPath
     */
    public function setModelUrlPath(string $modelUrlPath): void
    {
        $this->modelUrlPath = $modelUrlPath;
    }

    public function getBaseRecordUrl(): string
    {
        return $this->getBaseUrl() . '/' . preg_replace('/\//', '', $this->getModelUrlPath(), 1);
    }


    public function createResponse(bool $includeMetaData = true): array
    {
        $response = [];
//        $this->paginator = new Paginator($this->query);
        $this->paginator = $this->getPaginator();

        $records = $this->getRecords();

        if ($includeMetaData && !$this->usesCursor) {
            $response['meta'] = $this->createMetaData();
        }

        $response['data'] = $records;
        return $response;
    }

    public function getRecords(): array
    {
        return $this->paginator->getQuery()->getResult();
    }

    public function createMetaData(): array
    {
        $prevPage = $this->page === 1 ? null : $this->page - 1;
        $lastPage = ceil(($this->totalRecords + 0.0001) / $this->recordsPerPage);
        $nextPage = ($this->page + 1) > $lastPage ? null : $this->page + 1;

        if ($this->totalRecords === $this->recordsPerPage) {
            $nextPage = null;
            $lastPage = 1;
        }

        return [
            'total' => $this->totalRecords,
            'per_page' => $this->recordsPerPage,
            'current_page' => $this->page,
            'last_page' => $lastPage,
            'first_page_url' => $this->getBaseRecordUrl() . '?page=1',
            'last_page_url' => $this->getBaseRecordUrl() . '?page=' . $lastPage,
            'next_page_url' => $nextPage === null ? null : $this->getBaseRecordUrl() . '?page=' . $nextPage,
            'prev_page_url' => $prevPage === null ? null : $this->getBaseRecordUrl() . '?page=' . $prevPage,
        ];
    }

}