<?php
/**
 * @author Colinet Julien
 */

namespace CpCreation\VitiCore\Shop\Repository;

use CpCreation\VitiCore\Repository\BaseRepository;
use CpCreation\VitiCore\Shop\Model\ShippingCountry;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Intl\Countries;

class ShippingCountryRepository extends BaseRepository
{
    /**
     * @var array<string, ?ShippingCountry>
     */
    private array $groupCountryCache = [];

    /**
     * @var array<string, array<string, ShippingCountry>>
     */
    private array $assignedCountriesByGroupCache = [];

    /**
     * @throws \ReflectionException
     */
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, ShippingCountry::class);
    }

    public function findCountriesNotDisabledQueryBuilder(): QueryBuilder
    {
        return $this->createQueryBuilder('country')
            ->andWhere('country.disabled = FALSE')
            ->orderBy('country.code', 'ASC');
    }

    public function findCountriesNotDisabledNotAssignedQueryBuilder(): QueryBuilder
    {
        return $this->createQueryBuilder('country')
            ->andWhere('country.disabled = FALSE')
            ->orderBy('country.code', 'ASC');
    }

    /**
     * @return list<ShippingCountry>
     */
    public function findCountriesNotDisabled(): array
    {
        return $this->createQueryBuilder('country')
            ->andWhere('country.disabled = FALSE')
            ->orderBy('country.code', 'ASC')
            ->getQuery()
            ->getResult();
    }

    /**
     * @return array<string, ShippingCountry>
     */
    public function findCountriesOrder(): array
    {
        $countries = [];
        $results = $this->createQueryBuilder('country')
            ->getQuery()
            ->getResult();

        /** @var ShippingCountry $country */
        foreach ($results as $country) {
            $countries[Countries::getName($country->getCode()) . '-' . $country->getId()] = $country;
        }

        ksort($countries);

        return $countries;
    }

    /**
     * @return array<string, ShippingCountry>
     */
    public function findCountriesAssigned(): array
    {
        $countries = [];
        $results = $this->createQueryBuilder('country')
            ->innerJoin('country.zones', 'zones')
            ->innerJoin('zones.countries', 'countries', Join::WITH, 'countries.id = country.id')
            ->andWhere('country.disabled = FALSE')
            ->getQuery()
            ->getResult();

        /** @var ShippingCountry $country */
        foreach ($results as $country) {
            $countries[Countries::getName($country->getCode()) . '-' . $country->getId()] = $country;
        }

        ksort($countries);

        return $countries;
    }

    /**
     * @return array<string, ShippingCountry>
     */
    public function findCountriesAssignedByGroup(mixed $group): array
    {
        $cacheKey = (string) $group;

        if (isset($this->assignedCountriesByGroupCache[$cacheKey])) {
            return $this->assignedCountriesByGroupCache[$cacheKey];
        }

        $countries = [];
        $results = $this->createQueryBuilder('country')
            ->innerJoin('country.zones', 'zones')
            ->innerJoin('zones.countries', 'c', Join::WITH, 'c.id = country.id')
            ->innerJoin('country.groups', 'g', Join::WITH, 'g.id = :group')
            ->andWhere('country.disabled = FALSE')
            ->setParameter('group', $group)
            ->getQuery()
            ->getResult();

        /** @var ShippingCountry $country */
        foreach ($results as $country) {
            $countries[Countries::getName($country->getCode()) . '-' . $country->getId()] = $country;
        }

        ksort($countries);

        return $this->assignedCountriesByGroupCache[$cacheKey] = $countries;
    }

    /**
     * @throws NonUniqueResultException
     */
    public function findOneByGroup(string $code, mixed $group): ?ShippingCountry
    {
        $cacheKey = $code.'|'.(string) $group;

        if (\array_key_exists($cacheKey, $this->groupCountryCache)) {
            return $this->groupCountryCache[$cacheKey];
        }

        return $this->groupCountryCache[$cacheKey] = $this->createQueryBuilder('country')
            ->andWhere('country.code = :code')
            ->andWhere('country.disabled = FALSE')
            ->innerJoin('country.groups', 'g', Join::WITH, 'g.id = :group')
            ->setMaxResults(1)
            ->setParameter('code', $code)
            ->setParameter('group', $group)
            ->getQuery()
            ->getOneOrNullResult();
    }
}
