<?php

namespace common\logic\Product\Product\Repository;

use common\helpers\GlobalParam;
use common\logic\AbstractRepository;
use common\logic\Distribution\Distribution\Model\Distribution;
use common\logic\PointSale\PointSale\Model\PointSale;
use common\logic\Producer\Producer\Model\Producer;
use common\logic\Product\Product\Model\Product;
use common\logic\Product\Product\Service\ProductSolver;
use common\logic\Product\ProductCategory\Model\ProductCategory;
use common\logic\Product\ProductPrice\Service\ProductPriceSolver;
use common\logic\User\User\Model\User;
use common\logic\User\UserProducer\Repository\UserProducerRepository;

class ProductRepository extends AbstractRepository
{
    protected ProductRepositoryQuery $query;
    protected ProductSolver $productSolver;
    protected ProductPriceSolver $productPriceSolver;
    protected UserProducerRepository $userProducerRepository;

    public function loadDependencies(): void
    {
        $this->loadQuery(ProductRepositoryQuery::class);
        $this->productSolver = $this->loadService(ProductSolver::class);
        $this->productPriceSolver = $this->loadService(ProductPriceSolver::class);
        $this->userProducerRepository = $this->loadService(UserProducerRepository::class);
    }

    public function getDefaultOptionsSearch(): array
    {
        return [
            self::WITH => ['taxRate', 'productPointSale'],
            self::JOIN_WITH => [],
            self::ORDER_BY => 'product.order ASC',
            self::ATTRIBUTE_ID_PRODUCER => 'product.id_producer'
        ];
    }

    public function findOneProductById(int $id): ?Product
    {
        return $this->createDefaultQuery()
            ->filterById($id)
            ->findOne();
    }

    public function findOneProductByName(string $name): ?Product
    {
        return $this->createDefaultQuery()
            ->filterByName($name)
            ->findOne();
    }

    public function findProducts(bool $filterStatus = true): array
    {
        return $this->createDefaultQuery($filterStatus)->find();
    }

    /**
     * Retourne le nombre de produits du producteur courant.
     */
    public function countProducts(): int
    {
        return $this->createDefaultQuery()->count();
    }

    public function queryProductsByDistribution(Distribution $distribution, bool $filterStatus = true)
    {
        return $this->createDefaultQuery($filterStatus)
            ->joinWith([
                'productDistribution' => function ($query) use ($distribution) {
                    $query->andOnCondition(
                        'product_distribution.id_distribution = ' . $distribution->id
                    );
                }
            ])
            ->orderBy('product_distribution.active DESC, product.order ASC');
    }

    /**
     * Retourne les produits d'une production donnée.
     */
    public function findProductsByDistribution(Distribution $distribution, bool $filterStatus = true)
    {
        $productArray = $this->queryProductsByDistribution($distribution, $filterStatus)->find();
        return $this->buildProductsArrayById($productArray);
    }

    public function findProductsByDistributionAsArray(Distribution $distribution)
    {
        return $this->queryProductsByDistribution($distribution)->asArray()->find();
    }

    public function buildProductsArrayById(array $productsArray): array
    {
        $productArrayById = [];
        foreach($productsArray as $product) {
            $productArrayById[$product->id] = $product;
        }

        return $productArrayById;
    }

    public function queryProductsByProductCategory(ProductCategory $productCategory)
    {
        return $this->createDefaultQuery()
            ->filterIsStatusOnline()
            ->filterByProductCategory($productCategory);
    }

    public function countProductsWithoutCategory(Producer $producer): int
    {
        return $this->createDefaultQuery()
            ->filterIsStatusOnline()
            ->filterByProductCategory(null)
            ->count();
    }

    public function getPriceArray(Product $product, User $user = null, PointSale $pointSale = null): array
    {
        $priceArray = [];
        $userProducer = null;
        if ($user) {
            $userProducer = $this->userProducerRepository->findOneUserProducer($user);
        }

        // specific prices
        $specificPriceArray = $this->productPriceSolver->filterByPriorityMatch(
            $product->productPrice,
            $user,
            $pointSale
        );

        foreach ($specificPriceArray as $specificPrice) {
            $priceArray[] = [
                'from_quantity' => $specificPrice->from_quantity ? $specificPrice->from_quantity : 0,
                'price' => number_format($this->productSolver->getPrice($product, [
                    'user' => $user,
                    'user_producer' => $userProducer,
                    'point_sale' => $pointSale,
                    'quantity' => $specificPrice->from_quantity
                ]), 5),
                'price_with_tax' => number_format($this->productSolver->getPriceWithTax($product, [
                    'user' => $user,
                    'user_producer' => $userProducer,
                    'point_sale' => $pointSale,
                    'quantity' => $specificPrice->from_quantity
                ]), 2),
            ];
        }

        if (!$this->productSolver->hasPriceWithQuantityZero($priceArray)) {
            // base price
            $priceArray[] = [
                'from_quantity' => 0,
                'price' => number_format($this->productSolver->getPrice($product), 5),
                'price_with_tax' => number_format($this->productSolver->getPriceWithTax($product), 2),
            ];
        }

        usort($priceArray, function ($a, $b) {
            if ($a['price'] < $b['price']) {
                return 1;
            } elseif ($a['price'] > $b['price']) {
                return -1;
            } else {
                return 0;
            }
        });

        return $priceArray;
    }
}