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

namespace CpCreation\VitiCore\Shop\Repository;

use CpCreation\VitiCore\Product\Model\ProductPriceCode;
use CpCreation\VitiCore\Product\Model\ProductPriceGroup;
use CpCreation\VitiCore\Product\Model\ProductType;
use CpCreation\VitiCore\Repository\BaseRepository;
use CpCreation\VitiCore\Shop\Model\DeliveryMethod;
use CpCreation\VitiCore\Shop\Model\ShippingPrice;
use CpCreation\VitiCore\Shop\Model\ShippingZone;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\Persistence\ManagerRegistry;

class ShippingPriceRepository extends BaseRepository
{
    /**
     * ShippingPriceRepository constructor.
     * @param ManagerRegistry $registry
     * @throws \ReflectionException
     */
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, ShippingPrice::class);
    }

    /**
     * @return mixed
     */
    public function findTypesOfPrice()
    {
        $qb = $this
            ->createQueryBuilder('p')
            ->innerJoin('p.types', 't')
            ->innerJoin('t.translations', 'tr')
            ->groupBy('tr.name')
            ->getQuery()
            ->getResult();

        return $qb;
    }

    /**
     * @return mixed
     */
    public function findOrder()
    {
        $qb = $this
            ->createQueryBuilder('p')
            ->innerJoin('p.types', 't')
            ->innerJoin('t.translations', 'tr')
            ->addOrderBy('tr.name')
            ->addOrderBy('p.qtyAt')
            ->getQuery()
            ->getResult();

        return $qb;
    }

    /**
     * @return mixed
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function findFreesShipping()
    {
        $qb = $this
            ->createQueryBuilder('p')
            ->where('p.packagePrice = 0')
            ->getQuery()
            ->setMaxResults(1)
            ->getOneOrNullResult();

        return $qb;
    }

    /**
     * @param ProductPriceGroup $group
     * @param ProductType       $type
     * @return mixed
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function findFreesShippingByGroup($group, ProductType $type)
    {
        $qb = $this
            ->createQueryBuilder('p')
            ->where('p.packagePrice = 0')
            ->innerJoin('p.groups', 'g', Join::WITH, 'g.id = :group')
            ->innerJoin('p.types', 't', Join::WITH, 't.id = :type')
            ->setParameter('group', $group)
            ->setParameter('type', $type->getId()->toString())
            ->getQuery()
            ->setMaxResults(1)
            ->getOneOrNullResult();

        return $qb;
    }

    /**
     * @param int          $qty
     * @param string       $type
     * @param ShippingZone $zone
     * @return mixed
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function findByQuantityAndByTypeAndByZone(int $qty, string $type, ShippingZone $zone)
    {
        $qb = $this
            ->createQueryBuilder('p')
            ->innerJoin('p.types', 't')
            ->innerJoin('t.translations', 'tr', Join::WITH, 'tr.slug = :type')
            ->innerJoin('p.zones', 'z', Join::WITH, 'z.id = :zone')
            ->andwhere('p.qtyOf < :qty')
            ->andwhere('p.qtyAt >= :qty')
            ->setParameter('qty', $qty)
            ->setParameter('type', $type)
            ->setParameter('zone', $zone->getId()->toString())
            ->addOrderBy('p.createdAt', 'DESC')
            ->getQuery()
            ->setMaxResults(1)
            ->getOneOrNullResult();

        return $qb;
    }

    /**
     * @param int                 $qty
     * @param string              $type
     * @param ShippingZone        $zone
     * @param                     $group
     * @param DeliveryMethod|null $deliveryMethod
     * @return int|mixed|string|null
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function findByQuantityAndByTypeAndByZoneAndByGroup(int $qty,
        string $type,
        ShippingZone $zone,
        $group,
        DeliveryMethod $deliveryMethod = null
    ) {


        $qb = $this
            ->createQueryBuilder('p')
            ->innerJoin('p.types', 't')
            ->innerJoin('t.translations', 'tr', Join::WITH, 'tr.slug = :type')
            ->innerJoin('p.zones', 'z', Join::WITH, 'z.id = :zone')
            ->innerJoin('p.groups', 'g', Join::WITH, 'g.id = :group')
            ->andwhere('p.qtyOf < :qty')
            ->andwhere('p.qtyAt >= :qty')
            ->setParameter('qty', $qty)
            ->setParameter('type', $type)
            ->setParameter('group', $group)
            ->setParameter('zone', $zone->getId()->toString())
            ->addOrderBy('p.createdAt', 'DESC');

        if ($deliveryMethod) {
            $qb->andwhere('p.deliveryMethod = :deliveryMethod')
               ->setParameter('deliveryMethod', $deliveryMethod->getId()->toString());
        } else {
            $qb->andwhere('p.deliveryMethod is NULL');
        }

        return $qb->getQuery()->setMaxResults(1)->getOneOrNullResult();
    }

    /**
     * @param int                 $qty
     * @param string              $type
     * @param ShippingZone        $zone
     * @param                     $group
     * @param DeliveryMethod|null $deliveryMethod
     * @param ProductPriceCode    $productPriceCode
     * @return int|mixed|string|null
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function findByQuantityAndByTypeAndByZoneAndByGroupAndByPriceCode(int $qty,
        string $type,
        ShippingZone $zone,
        $group,
        DeliveryMethod $deliveryMethod = null,
        ProductPriceCode $productPriceCode = null
    ) {
        // Si un productPriceCode est fourni, cherche d'abord avec ce code
        if ($productPriceCode !== null) {
            $qb = $this
                ->createQueryBuilder('p')
                ->innerJoin('p.types', 't')
                ->innerJoin('t.translations', 'tr', Join::WITH, 'tr.slug = :type')
                ->innerJoin('p.zones', 'z', Join::WITH, 'z.id = :zone')
                ->innerJoin('p.groups', 'g', Join::WITH, 'g.id = :group')
                ->innerJoin('p.productPriceCodes', 'pc', Join::WITH, 'pc.id = :productPriceCode')
                ->andwhere('p.qtyOf < :qty')
                ->andwhere('p.qtyAt >= :qty')
                ->setParameter('qty', $qty)
                ->setParameter('type', $type)
                ->setParameter('group', $group)
                ->setParameter('zone', $zone->getId()->toString())
                ->setParameter('productPriceCode', $productPriceCode->getId()->toString());

            if ($deliveryMethod) {
                $qb->andwhere('p.deliveryMethod = :deliveryMethod')
                   ->setParameter('deliveryMethod', $deliveryMethod->getId()->toString());
            } else {
                $qb->andwhere('p.deliveryMethod is NULL');
            }

            $result = $qb->getQuery()->setMaxResults(1)->getOneOrNullResult();

            if ($result) {
                return $result;
            }
        }

        // Sinon, cherche explicitement les ShippingPrice sans aucun productPriceCode
        // En récupérant d'abord tous les ShippingPrice qui ont des productPriceCodes
        $subQb = $this->createQueryBuilder('p2')
            ->select('p2.id')
            ->innerJoin('p2.productPriceCodes', 'pc2');

        $qb = $this
            ->createQueryBuilder('p')
            ->innerJoin('p.types', 't')
            ->innerJoin('t.translations', 'tr', Join::WITH, 'tr.slug = :type')
            ->innerJoin('p.zones', 'z', Join::WITH, 'z.id = :zone')
            ->innerJoin('p.groups', 'g', Join::WITH, 'g.id = :group')
            ->andwhere('p.qtyOf < :qty')
            ->andwhere('p.qtyAt >= :qty')
            ->andWhere('p.id NOT IN ('.$subQb->getDQL().')')
            ->setParameter('qty', $qty)
            ->setParameter('type', $type)
            ->setParameter('group', $group)
            ->setParameter('zone', $zone->getId()->toString());

        if ($deliveryMethod) {
            $qb->andwhere('p.deliveryMethod = :deliveryMethod')
               ->setParameter('deliveryMethod', $deliveryMethod->getId()->toString());
        } else {
            $qb->andwhere('p.deliveryMethod is NULL');
        }

        return $qb->getQuery()->setMaxResults(1)->getOneOrNullResult();
    }
}
