<?php

namespace Lc\SovBundle\Repository;

use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\QueryBuilder;
use Knp\Component\Pager\PaginatorInterface;

abstract class AbstractRepositoryQuery
{
    protected ServiceEntityRepository $repository;
    protected QueryBuilder $query;
    protected PaginatorInterface $paginator;
    protected string $id;

    public function __construct(ServiceEntityRepository $repository, string $id, PaginatorInterface $paginator = null)
    {
        $this->repository = $repository;
        $this->query = $repository->createQueryBuilder($id);
        $this->paginator = $paginator;
        $this->id = $id;
    }

    public function __call(string $name, $params): self
    {
        foreach ($params as $key => $value) {
            $this->populateDqlId($params[$key]);
        }

        call_user_func_array([$this->query, $name], $params);

        return $this;
    }

    public function create()
    {
        $class = get_called_class();

        return new $class($this->repository, $this->paginator);
    }

    public function call(callable $fn): self
    {
        $fn($this->query, $this);

        return $this;
    }

    public function count(): string
    {
        return $this->query->getQuery()
            ->getSingleScalarResult();
    }

    public function findOne()
    {
        return $this->query->getQuery()
            ->setMaxResults(1)
            ->getOneOrNullResult();
    }

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

    public function limit(int $maxResults): self
    {
        return $this->query->setMaxResults($maxResults);
    }

    public function paginate(int $page = 1, int $limit = 20)
    {
        return $this->paginator->paginate($this->query->getQuery(), $page, $limit);
    }

    public function getRepository(): ServiceEntityRepository
    {
        return $this->repository;
    }

    protected function populateDqlId(&$data)
    {
        if (is_string($data)) {
            $words = explode(' ', $data);

            foreach ($words as $k => $v) {
                if (isset($v[0]) && '.' === $v[0]) {
                    $words[$k] = $this->id . $v;
                }
            }

            $data = implode(' ', $words);
        } elseif (is_array($data)) {
            foreach ($data as $k => $v) {
                $this->populateDqlId($data[$k]);
            }
        }

        return $data;
    }

    public function orderBy(string $field, string $sort = 'ASC'): self
    {
        if (substr($field, 0, 1) === '.') {
            return $this->addOrderBy($field, $sort);
        } else {
            return $this->addOrderBy('.' . $field, $sort);
        }
    }

}

