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

namespace CpCreation\VitiCore\Product\Repository;

use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Model\CartItem;
use CpCreation\VitiCore\Cart\Model\Promo;
use CpCreation\VitiCore\Product\Model\Product;
use CpCreation\VitiCore\Product\Model\ProductDegressive;
use CpCreation\VitiCore\Product\Model\ProductPriceGroup;
use CpCreation\VitiCore\Repository\BaseRepository;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\Persistence\ManagerRegistry;

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

    /**
     * @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;
    }

    public function findAllByProductAndByGroup(Product $product, $group = null)
    {

        $qb = $this->createQueryBuilder('d');
        $qb
           ->innerJoin('d.products', 'products', Join::WITH, 'products.id = :product')
           ->innerJoin('d.types', 'types')
           ->setParameter('product', $product->getId())
           ->orderBy('types.volume', 'ASC')
           ->addOrderBy('d.qtyOf', 'DESC');

        if ($group) {
            $qb
                ->innerJoin('d.groups', 'gr', Join::WITH, 'gr.id = :group')
                ->setParameter('group', $group);
        }

        $query = $qb->getQuery();

        return $query->getResult();
    }

    public function findByQuantityAndByItemAndByGroup(int $qty, CartItem $item, $group = null)
    {

        $qb = $this->createQueryBuilder('d');
        $qb->andwhere('d.qtyOf < :qty')
           ->innerJoin('d.products', 'products', Join::WITH, 'products.id = :product')
           ->innerJoin('d.types', 'types', Join::WITH, 'types.id = :type')
           ->setMaxResults(1)
           ->setParameter('product', $item->getVariant()->getProduct()->getId())
           ->setParameter('qty', $qty)
           ->setParameter('type', $item->getVariant()->getType()->getId())
           ->orderBy('d.qtyOf', 'DESC');

        if ($group) {
            $qb
                ->innerJoin('d.groups', 'gr', Join::WITH, 'gr.id = :group')
                ->setParameter('group', $group);
        }

        $query = $qb->getQuery();

        return $query->getOneOrNullResult();
    }

    /**
     * @param int               $qty
     * @param string            $type
     * @param Cart              $cart
     * @param ProductPriceGroup $group
     * @return mixed
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function findByQuantityAndByTypeAndByGroup(int $qty, string $type, Cart $cart, $group = null)
    {

        $arrayItem = [];
        /** @var CartItem $item */
        foreach ($cart->getItems() as $item) {
            $arrayItem[] = $item->getVariant()->getProduct()->getId()->toString();
        }

        $qb = $this->createQueryBuilder('p');
        $qb->andwhere('p.qtyOf < :qty')
           ->innerJoin('p.types', 't', Join::WITH, 't.id = :type')
           ->setParameter('qty', $qty)
           ->setParameter('type', $type)
           ->orderBy('p.qtyOf', 'DESC');

        $qb->leftjoin('p.products', 'pr');
        $qb->andWhere(
            $qb->expr()->orX(
                $qb->expr()->in('pr.id', "'".implode("','", $arrayItem)."'"),
                $qb->expr()->isNull('pr.id')
            )
        );

        if ($group) {
            $qb
                ->innerJoin('p.groups', 'gr', Join::WITH, 'gr.id = :group')
                ->setParameter('group', $group);
        }

        $query = $qb->getQuery();
        if ($query->getResult()) {
            /** @var ProductDegressive $result */
            foreach ($query->getResult() as $result) {
                $qb2 = $this->createQueryBuilder('p');
                $qb2->andWhere('p.id = :result')
                    ->andWhere('p.qtyOf < :quantity');
                $qb2->orderBy('p.qtyOf', 'DESC');
                $qb2->setParameter('result', $result->getId()->toString())
                    ->setParameter('quantity', $cart->getQuantityByProducts($result->getProducts()));
                $query2 = $qb2->getQuery();
                if ($query2->getResult()) {
                    return $query2->getOneOrNullResult();
                }
            }
        }

        return $query->getOneOrNullResult();
    }

    /**
     * @param int               $qty
     * @param string            $type
     * @param Cart              $cart
     * @param ProductPriceGroup $group
     * @return mixed
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function findByQuantityAndByTypeAndByGroupAndByEquivalency(int $qty, $type, Cart $cart, $group = null)
    {

        $arrayItem = [];
        /** @var CartItem $item */
        foreach ($cart->getItems() as $item) {
            $arrayItem[] = $item->getVariant()->getProduct()->getId()->toString();
        }

        $qb = $this->createQueryBuilder('p');
        $qb->andwhere('p.equivalency = TRUE')
           ->andwhere('p.qtyOf < :qty')
           ->innerJoin('p.types', 't', Join::WITH, 't.id = :type')
           ->setParameter('qty', $qty)
           ->setParameter('type', $type)
           ->orderBy('p.qtyOf', 'DESC');

        $qb->leftjoin('p.products', 'pr');
        $qb->andWhere(
            $qb->expr()->orX(
                $qb->expr()->in('pr.id', "'".implode("','", $arrayItem)."'"),
                $qb->expr()->isNull('pr.id')
            )
        );

        if ($group) {
            $qb
                ->innerJoin('p.groups', 'gr', Join::WITH, 'gr.id = :group')
                ->setParameter('group', $group);
        }

        $query = $qb->getQuery();

        if ($query->getResult()) {
            /** @var ProductDegressive $result */
            foreach ($query->getResult() as $result) {
                $qb2 = $this->createQueryBuilder('p');
                $qb2->andWhere('p.id = :result')
                    ->andWhere('p.qtyOf < :quantity');
                $qb2->orderBy('p.qtyOf', 'DESC');
                $qb2->setParameter('result', $result->getId()->toString())
                    ->setParameter('quantity', $cart->getQuantityByProducts($result->getProducts()));
                $query2 = $qb2->getQuery();

                if ($query2->getResult()) {
                    return $query2->getOneOrNullResult();
                }
            }

            return $query->getOneOrNullResult();
        }
    }


    public function findOneByTypesAndQuantity(Collection $types, $quantity, $group = null)
    {
        $qb   = $this->createQueryBuilder('d');
        $andX = $qb->expr()->andX();

        foreach ($types as $key => $type) {
            $qb->setParameter('type'.$key, $type);
            $qb->innerJoin('d.types', 't'.$key, Join::WITH, 't'.$key.' = :type'.$key);
            $andX->add('t'.$key.' = :type'.$key);
        }
        if ($group) {
            $qb
                ->innerJoin('d.groups', 'gr', Join::WITH, 'gr.id = :group')
                ->setParameter('group', $group);
        }
        $qb->addOrderBy('d.qtyOf', 'DESC');
        $qb->andWhere(' d.qtyOf <= :quantity');
        $qb->setParameter('quantity', $quantity);


        $query = $qb->getQuery()->setMaxResults(1);

        return $query->getOneOrNullResult();
    }

    public function findOneByTypesAndProductsAndQuantity(Collection $types,
        Collection $products,
        $quantity,
        $group = null
    ) {
        $qb   = $this->createQueryBuilder('d');
        $andX = $qb->expr()->andX();

        foreach ($types as $key => $type) {
            $qb->setParameter('type'.$key, $type);
            $qb->innerJoin('d.types', 't'.$key, Join::WITH, 't'.$key.' = :type'.$key);
            $andX->add('t'.$key.' = :type'.$key);
        }

        foreach ($products as $key => $product) {
            $qb->setParameter('product'.$key, $product);
            $qb->innerJoin('d.products', 'p'.$key, Join::WITH, 'p'.$key.' = :product'.$key);
            $andX->add('p'.$key.' = :product'.$key);
        }
        if ($group) {
            $qb
                ->innerJoin('d.groups', 'gr', Join::WITH, 'gr.id = :group')
                ->setParameter('group', $group);
        }
        $qb->addOrderBy('d.qtyOf', 'DESC');
        $qb->andWhere(' d.qtyOf <= :quantity');
        $qb->setParameter('quantity', $quantity);


        $query = $qb->getQuery()->setMaxResults(1);

        return $query->getOneOrNullResult();
    }
}
