<?php 
 
declare(strict_types=1); 
 
namespace Slivki\Repository\City; 
 
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; 
use Doctrine\ORM\NonUniqueResultException; 
use Doctrine\Persistence\ManagerRegistry; 
use Slivki\Entity\City; 
use Slivki\Exception\CityNotFoundException; 
 
final class CityRepository extends ServiceEntityRepository implements CityRepositoryInterface 
{ 
    public function __construct(ManagerRegistry $registry) 
    { 
        parent::__construct($registry, City::class); 
    } 
 
    /** 
     * @return City[] 
     */ 
    public function getActiveCities(): array 
    { 
        $qb = $this->createQueryBuilder('city'); 
        $expr = $qb->expr(); 
 
        return $qb 
            ->andWhere($expr->eq('city.active', ':active')) 
            ->setParameter('active', true) 
            ->addOrderBy($expr->asc('city.position')) 
            ->addOrderBy($expr->asc('city.name')) 
            ->getQuery() 
            ->getResult(); 
    } 
 
    public function findById(int $id): ?City 
    { 
        $qb = $this->createQueryBuilder('c'); 
        $expr = $qb->expr(); 
 
        return $qb 
            ->andWhere($expr->eq('c.ID', ':id')) 
            ->setParameters([ 
                'id' => $id, 
            ]) 
            ->getQuery() 
            ->getOneOrNullResult(); 
    } 
 
    public function getById(int $id): City 
    { 
        $city = $this->findById($id); 
 
        if (!$city instanceof City) { 
            throw new CityNotFoundException(); 
        } 
 
        return $city; 
    } 
 
    public function getByName(string $name): City 
    { 
        $qb = $this->createQueryBuilder('city'); 
        $expr = $qb->expr(); 
 
        $city = $qb 
            ->andWhere($expr->eq('city.name', ':name')) 
            ->setParameter('name', $name) 
            ->getQuery() 
            ->getOneOrNullResult(); 
 
        if (!$city instanceof City) { 
            throw new CityNotFoundException(); 
        } 
 
        return $city; 
    } 
 
    public function findActiveNearestCity(string $latitude, string $longitude): ?City 
    { 
        $qb = $this->createQueryBuilder('city'); 
        $expr = $qb->expr(); 
 
        return $qb 
            ->andWhere($expr->eq('city.active', ':active')) 
            ->addOrderBy($expr->min( 
                \sprintf('ST_Distance(city.point, GeomFromEWKT(\'SRID=4326;POINT(%s)\'))', \implode(' ', [$latitude, $longitude]))) 
            ) 
            ->addGroupBy('city.ID') 
            ->setParameters([ 
                'active' => true, 
            ]) 
            ->setMaxResults(1) 
            ->getQuery() 
            ->getOneOrNullResult(); 
    } 
 
    public function findAllSortByName(): array 
    { 
        $qb = $this->createQueryBuilder('c'); 
        $expr = $qb->expr(); 
         
        return $qb 
            ->andWhere($expr->eq('c.active', ':active')) 
            ->setParameter('active', true) 
            ->addOrderBy($expr->asc('c.name')) 
            ->getQuery() 
            ->getResult(); 
    } 
 
    public function findActiveByDomain(string $domain): ?City 
    { 
        $queryBuilder = $this->createQueryBuilder('city'); 
        $expr = $queryBuilder->expr(); 
 
        return $queryBuilder 
            ->andWhere($expr->eq('city.active', ':active')) 
            ->andWhere($expr->eq('city.domain', ':domain')) 
            ->setParameters( 
                [ 
                    'active' => true, 
                    'domain' => $domain, 
                ] 
            ) 
            ->setMaxResults(1) 
            ->getQuery() 
            ->getOneOrNullResult(); 
    } 
}