<?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->query = $this->loadService(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 [
            'with' => ['taxRate', 'productPointSale'],
            'join_with' => [],
            'orderby' => 'order ASC',
            'attribute_id_producer' => 'product.id_producer'
        ];
    }

    public function findOneProductById(int $id): ?Product
    {
        return Product::searchOne(['id' => $id]);
    }

    public function findOneProductByName(string $name): ?Product
    {
        return Product::searchOne(['name' => $name]);
    }

    public function findProducts(): array
    {
        return Product::searchAll([], [
            'orderby' => 'product.order ASC'
        ]);
    }

    /**
     * Retourne le nombre de produits du producteur courant.
     */
    public static function countProducts(): int
    {
        return Product::searchCount();
    }

    /**
     * Retourne les produits d'une production donnée.
     */
    // searchByDistribution
    // getByDistribution
    public function findProductsByDistribution(Distribution $distribution)
    {
        $productArray = Product::find()
            ->leftJoin('product_distribution', 'product.id = product_distribution.id_product')
            ->where([
                'id_producer' => GlobalParam::getCurrentProducerId(),
                'product_distribution.id_distribution' => $distribution->id
            ])
            ->orderBy('product_distribution.active DESC, product.order ASC')
            ->all();

        $productArrayById = [];
        foreach($productArray as $product) {
            $productArrayById[$product->id] = $product;
        }

        return $productArrayById;
    }

    // queryByProductCategory
    public function queryProductsByProductCategory(ProductCategory $productCategory)
    {
        return Product::find()
            ->andWhere([
                'id_producer' => $this->getProducerContext()->id,
                'active' => true,
            ])
            ->andWhere(
                'product.id_product_category = :id_product_category'
            )
            ->params(
                [':id_product_category' => $productCategory->id]
            )
            ->orderBy(
                'order ASC'
            );
    }

    public function countProductsWithoutCategory(Producer $producer): int
    {
        return Product::searchCount([
            'id_producer' => $producer->id,
            'product.active' => 1,
            'product.id_product_category' => null
        ]);
    }

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

        // 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
                ]), 3),
                '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' => $this->productSolver->getPrice($product),
                'price_with_tax' => $this->productSolver->getPriceWithTax($product),
            ];
        }

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

        return $priceArray;
    }
}