<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\PersonBundle\Repository\AccompanyingPeriod;

use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository;

class AccompanyingPeriodWorkEvaluationRepository implements ObjectRepository
{
    private readonly EntityRepository $repository;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->repository = $entityManager->getRepository(AccompanyingPeriodWorkEvaluation::class);
    }

    public function countNearMaxDateByUser(User $user): int
    {
        return $this->buildQueryNearMaxDateByUser($user)
            ->select('count(e)')->getQuery()->getSingleScalarResult();
    }

    public function find($id): ?AccompanyingPeriodWorkEvaluation
    {
        return $this->repository->find($id);
    }

    /**
     * @return array|AccompanyingPeriodWorkEvaluation[]
     */
    public function findAll(): array
    {
        return $this->repository->findAll();
    }

    /**
     * @param int $limit
     * @param int $offset
     *
     * @return array|AccompanyingPeriodWorkEvaluation[]
     */
    public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
    {
        return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
    }

    public function findNearMaxDateByUser(User $user, int $limit = 20, int $offset = 0): array
    {
        return $this->buildQueryNearMaxDateByUser($user)
            ->select('e')
            ->setFirstResult($offset)
            ->setMaxResults($limit)
            ->getQuery()
            ->getResult();
    }

    public function findOneBy(array $criteria): ?AccompanyingPeriodWorkEvaluation
    {
        return $this->repository->findOneBy($criteria);
    }

    public function getClassName(): string
    {
        return AccompanyingPeriodWorkEvaluation::class;
    }

    private function buildQueryNearMaxDateByUser(User $user): QueryBuilder
    {
        $qb = $this->repository->createQueryBuilder('e');

        $qb
            ->join('e.accompanyingPeriodWork', 'work')
            ->join('work.accompanyingPeriod', 'period')
            ->where(
                $qb->expr()->andX(
                    $qb->expr()->isNull('e.endDate'),
                    $qb->expr()->neq('period.step', ':closed'),
                    $qb->expr()->gte(':now', $qb->expr()->diff('e.maxDate', 'e.warningInterval')),
                    $qb->expr()->orX(
                        $qb->expr()->eq('period.user', ':user'),
                        $qb->expr()->exists(
                            'SELECT 1 FROM '.AccompanyingPeriodWork::class.' subw JOIN subw.referrersHistory subw_ref_history WHERE subw.id = work.id AND subw_ref_history.user = :user'
                        )
                    )
                )
            )
            ->setParameters([
                'user' => $user,
                'now' => new \DateTimeImmutable('now'),
                'closed' => AccompanyingPeriod::STEP_CLOSED,
            ]);

        return $qb;
    }
}
