<?php

/**
 * Created by PhpStorm.
 * User: ondra
 * Date: 23.11.17
 * Time: 8:01.
 */

namespace KupShop\QuantityDiscountBundle\Util;

use KupShop\I18nBundle\Util\PriceConverter;
use KupShop\KupShopBundle\Context\CurrencyContext;
use KupShop\KupShopBundle\Util\Contexts;
use KupShop\KupShopBundle\Util\Price\Price;
use KupShop\KupShopBundle\Util\Price\ProductPrice;
use KupShop\QuantityDiscountBundle\Context\QuantityDiscountContext;
use KupShop\QuantityDiscountBundle\Query\QuantityDiscount;
use Query\Operator;
use Query\QueryBuilder;

class QuantityDiscountPrice
{
    public function __construct(
        private QuantityDiscountContext $quantityDiscountContext,
        protected ?PriceConverter $priceConverter = null,
    ) {
    }

    public function getPrice($idProduct, $pieces, $idVariation = null): Price
    {
        if ($productPrice = $this->getProductPrice($idProduct, $idVariation)) {
            return $this->applyDiscount($productPrice, $idProduct, $pieces, $idVariation);
        }

        return new Price(toDecimal(0), Contexts::get(CurrencyContext::class)->getDefault(), getVat());
    }

    public function applyDiscount(ProductPrice $productPrice, $idProduct, $pieces, $idVariation): Price
    {
        $discountedValue = $this->calculateQuantityDiscountForProductPrice(
            $productPrice,
            $this->getDiscount($idProduct, toDecimal($pieces), $idVariation)
        );

        return new Price($discountedValue, $productPrice->getCurrency(), $productPrice->getVat());
    }

    public function calculateQuantityDiscountPriceForProductPrice(ProductPrice $productPrice, array $discount): Price
    {
        return new Price(
            $this->calculateQuantityDiscountForProductPrice($productPrice, $discount),
            $productPrice->getCurrency(),
            $productPrice->getVat()
        );
    }

    private function calculateQuantityDiscountForProductPrice(ProductPrice $productPrice, ?array $discount): \Decimal
    {
        $value = $productPrice->getPriceWithVat(false)
            ->removeVat($productPrice->getVat());

        if (!$discount) {
            return $value;
        }

        if ($discount['discount_type'] == QuantityDiscountUtil::DISCOUNT_TYPE_PERC) {
            $value = $value->addDiscount($discount['discount']);
        } else {
            $discountValue = $this->priceConverter?->convert($discount['discount_type'], $productPrice->getCurrency(), $discount['discount']) ?: toDecimal($discount['discount']);
            $value = \Decimal::max($value->sub($discountValue), \DecimalConstants::zero());
        }

        return $value;
    }

    private function getProductPrice($idProduct, $idVariation = null): ?ProductPrice
    {
        if ($product = $this->getProduct($idProduct, $idVariation)) {
            return $product->getProductPrice();
        }

        return null;
    }

    public function getDiscount(
        int $idProduct,
        \Decimal $pieces,
        ?int $variationId = null,
    ): ?array {
        $active = $this->quantityDiscountContext->getActive();
        if (!$active) {
            return null;
        }

        $specs = [
            Operator::equals(['id_product' => $idProduct]),
            QuantityDiscount::byGroup($active),
        ];

        if ($variationId) {
            if ($quantityDiscount = $this->getDiscountQueryBuilder($specs,
                $pieces)->andWhere(Operator::equals(['id_variation' => $variationId]))->execute()->fetchAssociative()) {
                return $quantityDiscount;
            }
        }

        $quantityDiscount = $this->getDiscountQueryBuilder($specs,
            $pieces, )->andWhere('id_variation IS NULL')->execute()->fetchAssociative();

        return $quantityDiscount ?: null;
    }

    private function getDiscountQueryBuilder($specs, $pieces): QueryBuilder
    {
        return sqlQueryBuilder()
            ->select('discount, discount_type')
            ->from('products_quantity_discounts')
            ->where('pieces <= :pieces')
            ->andWhere(Operator::andX($specs))
            ->orderBy('pieces', 'DESC')
            ->setParameter('pieces', $pieces)
            ->setMaxResults(1);
    }

    private function getProduct($idProduct, $idVariation = null): ?\Product
    {
        if ($idVariation !== null) {
            $product = new \Variation($idProduct, $idVariation);
        } else {
            $product = new \Product($idProduct);
        }

        if (!$product->createFromDB()) {
            return null;
        }

        return $product;
    }
}
