<?php

/*
 * This file is part of the VitiCore 2.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Product\Repository;

use CpCreation\VitiCore\Product\Model\Product;
use CpCreation\VitiCore\Product\Model\ProductCategory;
use CpCreation\VitiCore\Product\Model\ProductPriceGroup;
use CpCreation\VitiCore\Repository\BaseRepository;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\Persistence\ManagerRegistry;

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

    /**
     * @return Product[]
     */
    public function findAllNotDeleted(): array
    {
        return $this->createQueryBuilder('product')
            ->addSelect('va')
            ->innerJoin('product.variants', 'va', Join::WITH, 'va.deletedAt IS NULL')
            ->orderBy('product.position', 'ASC')
            ->getQuery()
            ->getResult();
    }

    /**
     * @return Product[]
     */
    public function findAllPublish($group = null, $category = null): array
    {
        $qb = $this->createQueryBuilder('product')
            ->addSelect('tr, im, im2, ca, va, ty, tytr, pr')
            ->innerJoin('product.translations', 'tr', Join::WITH, 'tr.translatable = product.id')
            ->leftJoin('product.category', 'ca', Join::WITH, 'ca.id = product.category')
            ->innerJoin('product.image', 'im', Join::WITH, 'im.id = product.image')
            ->leftJoin('product.secondImage', 'im2', Join::WITH, 'im2.id = product.secondImage')
            ->innerJoin('product.variants', 'va', Join::WITH, '(va.unPublished = FALSE and va.deletedAt IS NULL)')
            ->innerJoin('va.prices', 'pr')
            ->innerJoin('va.type', 'ty')
            ->innerJoin('ty.translations', 'tytr')
            ->andWhere('product.disabled = FALSE')
            ->andWhere('product.deletedAt IS NULL')
            ->orderBy('product.position', 'ASC');

        if (null !== $category) {
            $qb
                ->andWhere('product.category = :category')
                ->setParameter('category', $category);
        }

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

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

    /**
     * @return Product[]
     */
    public function findOnlyCuvee($category = null): array
    {
        if (null !== $category) {
            return $this->createQueryBuilder('product')
                ->addSelect('tr, im')
                ->innerJoin('product.translations', 'tr', Join::WITH, 'tr.translatable = product.id')
                ->innerJoin('product.image', 'im', Join::WITH, 'im.id = product.image')
                ->andWhere('product.category = :category')
                ->andWhere('product.disabled = FALSE')
                ->andWhere('product.deletedAt IS NULL')
                ->andWhere('product.cuvee = TRUE')
                ->orderBy('product.position', 'ASC')
                ->setParameter('category', $category)
                ->getQuery()
                ->getResult();
        }

        return $this->createQueryBuilder('product')
            ->addSelect('tr, im')
            ->innerJoin('product.translations', 'tr')
            ->innerJoin('product.image', 'im')
            ->innerJoin('product.category', 'cat')
            ->andWhere('product.disabled = FALSE')
            ->andWhere('product.deletedAt IS NULL')
            ->andWhere('cat.pageCuvee = TRUE')
            ->andWhere('product.cuvee = TRUE')
            ->addOrderBy('cat.position', 'ASC')
            ->addOrderBy('product.position', 'ASC')
            ->getQuery()
            ->getResult();
    }

    /**
     * @return Product[]
     */
    public function findOnlyShop($group = null, $category = null): array
    {
        $qb = $this->createQueryBuilder('product')
            ->addSelect('tr, im')
            ->innerJoin('product.translations', 'tr', Join::WITH, 'tr.translatable = product.id')
            ->innerJoin('product.image', 'im', Join::WITH, 'im.id = product.image')
            ->innerJoin('product.variants', 'va', Join::WITH, '(va.unPublished = FALSE and va.deletedAt IS NULL)')
            ->innerJoin('va.type', 'type', Join::WITH, 'type.id = va.type')
            ->innerJoin('product.category', 'cat')
            ->andWhere('cat.pageShop = TRUE')
            ->andWhere('product.disabled = FALSE')
            ->andWhere('product.deletedAt IS NULL')
            ->andWhere('product.shop = TRUE')
            ->orderBy('product.positionShop', 'ASC');

        if (null !== $category) {
            $qb
                ->andWhere('product.category = :category')
                ->setParameter('category', $category);
        }

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

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

    /**
     * @param array{
     *     category?: string|null,
     *     variant?: string|null,
     *     group?: string|null,
     *     cuvee?: bool|null,
     *     shop?: bool|null
     * } $filters
     *
     * @return Product[]
     */
    public function findByAdminFilters(array $filters = []): array
    {
        $qb = $this->createQueryBuilder('product')
            ->select('DISTINCT product, im, ca, gr, va, ty')
            ->leftJoin('product.image', 'im')
            ->leftJoin('product.category', 'ca')
            ->leftJoin('product.groups', 'gr')
            ->leftJoin('product.variants', 'va', Join::WITH, 'va.deletedAt IS NULL')
            ->leftJoin('va.type', 'ty')
            ->andWhere('product.deletedAt IS NULL')
            ->orderBy('product.position', 'ASC');

        if (!empty($filters['category'])) {
            $qb
                ->andWhere('ca.id = :categoryId')
                ->setParameter('categoryId', $filters['category']);
        }

        if (!empty($filters['variant'])) {
            $qb
                ->andWhere('ty.id = :variantId')
                ->setParameter('variantId', $filters['variant']);
        }

        if (!empty($filters['group'])) {
            $qb
                ->andWhere('gr.id = :groupId')
                ->setParameter('groupId', $filters['group']);
        }

        if (\array_key_exists('cuvee', $filters) && null !== $filters['cuvee']) {
            $qb
                ->andWhere('product.cuvee = :cuvee')
                ->setParameter('cuvee', $filters['cuvee']);
        }

        if (\array_key_exists('shop', $filters) && null !== $filters['shop']) {
            $qb
                ->andWhere('product.shop = :shop')
                ->setParameter('shop', $filters['shop']);
        }

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

    /**
     * @return Product[]
     */
    public function findByPositionShopOrderAdmin(): array
    {
        return $this->createQueryBuilder('r')
            ->andWhere('r.deletedAt IS NULL')
            ->orderBy('r.positionShop', 'ASC')
            ->getQuery()
            ->getResult();
    }

    /**
     * @return Product[]
     */
    public function findByPositionShopOrder(): array
    {
        return $this->createQueryBuilder('r')
            ->andWhere('r.deletedAt IS NULL')
            ->andWhere('r.disabled = FALSE')
            ->orderBy('r.positionShop', 'ASC')
            ->getQuery()
            ->getResult();
    }

    /**
     * @throws NonUniqueResultException
     */
    public function findNextCuvee(int $position): ?Product
    {
        return $this->createQueryBuilder('product')
            ->innerJoin('product.category', 'cat')
            ->andWhere('product.disabled = FALSE')
            ->andWhere('product.deletedAt IS NULL')
            ->andWhere('cat.pageCuvee = TRUE')
            ->andWhere('product.cuvee = TRUE')
            ->andWhere('product.position > :position')
            ->addOrderBy('cat.position', 'ASC')
            ->addOrderBy('product.position', 'ASC')
            ->setMaxResults(1)
            ->setParameter('position', $position)
            ->getQuery()
            ->getOneOrNullResult();
    }

    /**
     * @throws NonUniqueResultException
     */
    public function findPreviousCuvee(int $position): ?Product
    {
        return $this->createQueryBuilder('product')
            ->innerJoin('product.category', 'cat')
            ->andWhere('product.disabled = FALSE')
            ->andWhere('product.deletedAt IS NULL')
            ->andWhere('cat.pageCuvee = TRUE')
            ->andWhere('product.cuvee = TRUE')
            ->andWhere('product.position < :position')
            ->addOrderBy('product.position', 'DESC')
            ->setMaxResults(1)
            ->setParameter('position', $position)
            ->getQuery()
            ->getOneOrNullResult();
    }
}
