<?php

declare(strict_types=1);

namespace Mobile\Device\Repository;

use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ManagerRegistry;
use Mobile\Device\Entity\Block;
use Mobile\Device\Entity\UnitParcel;
use Mobile\Measure\Entity\Variable\StateCode;
use Mobile\Project\Entity\DataEntryProject;

/**
 * Class UnitParcelRepository.
 *
 * @method UnitParcel|null find($id, $lockMode = null, $lockVersion = null)
 * @method UnitParcel|null findOneBy(array $criteria, array $orderBy = null)
 * @method UnitParcel[] findAll()
 * @method UnitParcel[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class UnitParcelRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, UnitParcel::class);
    }

    /**
     * @param DataEntryProject $project
     * @return array
     * @throws NoResultException
     * @throws NonUniqueResultException
     */
    public function findHasDeadStateCodeByProject(DataEntryProject $project): array
    {
        $qb = $this->getByProjectQuery($project);

        $qb->innerJoin('upl.stateCode', 'sco', Join::WITH, 'upl.stateCode = sco.id');
        $qb->andWhere('sco.type = :deadType');
        $qb->setParameter('deadType', StateCode::DEAD_STATE_CODE);

        return $qb->getQuery()->getResult();
    }

    /**
     * @param DataEntryProject $project
     * @param array $names
     * @return array
     * @throws NoResultException
     * @throws NonUniqueResultException
     */
    public function findByNameByProject(DataEntryProject $project, array $names): array
    {
        $qb = $this->getByProjectQuery($project);

        $qb->andWhere($qb->expr()->in('upl.name', ':names'));

        $qb->setParameter('names', $names);

        return $qb->getQuery()->getResult();
    }

    /**
     * @param DataEntryProject $project
     * @return QueryBuilder
     * @throws NoResultException
     * @throws NonUniqueResultException
     */
    private function getByProjectQuery(DataEntryProject $project): QueryBuilder
    {
        // Check with sub block.
        $sqb = $this->createQueryBuilder('up2');
        $sqb->select($sqb->expr()->count('up2.id'))
            ->innerJoin('up2.subBlock', 'sb2', Join::WITH, 'up2.subBlock = sb2.id')
            ->innerJoin('sb2.block', 'bl2', Join::WITH, 'sb2.block = bl2.id')
            ->innerJoin('bl2.device', 'ex2', Join::WITH, 'bl2.device = ex2.id')
            ->innerJoin('ex2.platform', 'pl2', Join::WITH, 'ex2.platform = pl2.id')
            ->innerJoin('pl2.project', 'pr2', Join::WITH, 'pl2.project = pr2.id');

        $sqb->where($sqb->expr()->andX(
            $sqb->expr()->eq('pr2.id', ':projectId')
        ));

        $sqb->setParameter('projectId', $project->getId());

        $subBlockExists = 0 < $sqb->getQuery()->getSingleScalarResult();

        $qb = $this->createQueryBuilder('upl');

        if ($subBlockExists) {
            $qb->leftJoin('upl.subBlock', 'sbl', Join::WITH, 'upl.subBlock = sbl.id')
                ->innerJoin(Block::class, 'blo', Join::WITH, 'sbl.block = blo.id OR upl.block = blo.id');
        } else {
            $qb->innerJoin('upl.block', 'blo', Join::WITH, 'upl.block = blo.id');
        }

        $qb->innerJoin('blo.device', 'exp', Join::WITH, 'blo.device = exp.id')
            ->innerJoin('exp.platform', 'pla', Join::WITH, 'exp.platform = pla.id')
            ->innerJoin('pla.project', 'pro', Join::WITH, 'pla.project = pro.id');

        $qb->where($qb->expr()->eq('pro.id', ':projectId'));

        $qb->setParameter('projectId', $project->getId());

        return $qb;
    }
}
