<?php
/*
 * Copyright (c) 2023. RTM Business
 */

namespace WordpressModels\ORM\EntityRepository;


use Doctrine\ORM\Cache;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\QueryBuilder;

/**
 * @template T
 * @extends EntityRepository<T>
 */
abstract class PaginationRepository extends EntityRepository
{

    private ?int $lastPaginationCount = null;

    /**
     * @return int|null
     */
    public function getLastPaginationCount(): ?int
    {
        return $this->lastPaginationCount;
    }

    public function getUniqueColumnValueCount(string $column)
    {
        $queryBuilder = $this->createQueryBuilder('e');
        $queryBuilder->select("COUNT(DISTINCT e.$column)");
        return $queryBuilder->getQuery()->getSingleScalarResult();
    }

    /**
     * @param int $limit
     * @param int $page
     * @param array $sort
     * @param array $filter
     * @return T[]
     * @throws NoResultException
     * @throws NonUniqueResultException
     */
    public function list(int $limit = -1, int $page = 0, array $sort = [], array $filter = []): array
    {
        $queryBuilder = $this->createPaginationBuilder($filter, $sort);

        $countQueryBuilder = (clone $queryBuilder)->select('COUNT(e)');
        $this->lastPaginationCount = $countQueryBuilder->getQuery()->getSingleScalarResult();

        $queryBuilder->setFirstResult($page * $limit);
        $queryBuilder->setMaxResults($limit);

        return $queryBuilder->getQuery()
            ->setCacheable(true)
            ->setCacheMode(Cache::MODE_GET)
            ->enableResultCache(10)
            ->getResult();
    }

    /**
     * @param array $sort
     * @param array $filter
     * @param \Doctrine\ORM\Mapping\ClassMetadata $classMetadata
     * @return QueryBuilder
     */
    protected function createPaginationBuilder(array $filter, array $sort): QueryBuilder
    {
        $classMetadata = $this->getEntityManager()->getClassMetadata($this->getEntityName());
        $queryBuilder = $this->createQueryBuilder('e');
        foreach ($sort as $field => $order) {
            $queryBuilder->addOrderBy("e.$field", $order);
        }
        $propExpr = $queryBuilder->expr()->andX();

        foreach ($filter as $field => $value) {

            $type = $classMetadata->getTypeOfField($field);

            if (!in_array($type, ['date', 'datetime'])) {
                $propExpr->add(is_array($value)
                    ? $queryBuilder->expr()->in("e.$field", ":$field")
                    : $queryBuilder->expr()->eq("e.$field", ":$field"));

                $queryBuilder->setParameter($field, $value);
            } else {
                if (is_array($value)) {

                    [$fromDate, $toDate] = $value;

                    if ($fromDate) {
                        $propExpr->add($queryBuilder->expr()->gte("e.$field", ":{$field}0"));
                        $queryBuilder->setParameter("{$field}0", $fromDate);
                    }
                    if ($toDate) {
                        $propExpr->add($queryBuilder->expr()->lte("e.$field", ":{$field}1"));
                        $queryBuilder->setParameter("{$field}1", $toDate);
                    }
                } else {
                    $queryBuilder->setParameter($field, $value);
                }
            }
        }
        if ($propExpr->count()) {
            $queryBuilder->where($propExpr);
        }
        return $queryBuilder;
    }

}
