<?php

namespace KupShop\OrderingBundle\Entity\Purchase;

use KupShop\KupShopBundle\Util\Price\Price;
use Product;

class ProductPurchaseItem extends AbstractPurchaseItem implements PurchaseItemInterface
{
    public $id_product;
    public $id_variation;
    public $pieces;
    public $date;
    private $id_discount;
    public $discounts = [];
    public ?\Decimal $bonus_points = null;

    private $product;

    private ?Price $priceWithDiscounts = null;
    private ?Price $piecePriceWithDiscounts = null;

    public function __construct($id_product, $id_variation, $pieces, Price $price, ?array $note = null, $id_discount = null, $date = null)
    {
        $this->id_product = $id_product;
        $this->id_variation = $id_variation;
        $this->pieces = $pieces;
        $this->price = $price;
        $this->note = $note;
        $this->id_discount = $id_discount;
        $this->date = $date;
    }

    public function getIdProduct()
    {
        return $this->id_product;
    }

    public function getIdVariation()
    {
        return $this->id_variation;
    }

    public function addDiscountsNote(): self
    {
        if ($productDiscounts = $this->getDiscounts()) {
            $note = $this->getNote();
            $decimal_Pieces = toDecimal($this->getPieces());
            if (!empty($note['priceWithoutDiscounts'])) {
                // pridavame nove slevy (napr. akce "Uplatnit slevový kód" v adminu),
                // ale na polozce jiz drive byly aplikovany jine slevy
                // priceWithoutDiscounts - puvodni cena pred vsemi slevami
                $priceWithoutDiscounts_piece = toDecimal($note['priceWithoutDiscounts']);
            } else {
                $priceWithoutDiscounts_piece = $this->getPriceWithVat()->div($decimal_Pieces);
            }
            $priceWithDiscounts = $this->getPriceWithDiscounts()->getPriceWithVat(false)->value(2);
            $priceWithDiscounts_piece = $priceWithDiscounts->div($decimal_Pieces);
            $note['priceWithoutDiscounts'] = $priceWithoutDiscounts_piece;
            $note['totalDiscount'] = $priceWithoutDiscounts_piece->sub($priceWithDiscounts_piece);
            $note['totalDiscountPerc'] = \DecimalConstants::one()
                ->sub($priceWithDiscounts_piece->div($priceWithoutDiscounts_piece))
                ->mul(\DecimalConstants::hundred())->round(\Decimal::$default_scale);
            $note['discounts'] = array_merge($note['discounts'] ?? [], $productDiscounts);
            $this->note = $note;
        }

        return $this;
    }

    public function getPieces()
    {
        return $this->pieces;
    }

    public function setName($name): self
    {
        $this->name = $name;

        return $this;
    }

    public function hasCustomName(): bool
    {
        return isset($this->name);
    }

    public function getName()
    {
        if (isset($this->name)) {
            return $this->name;
        }

        $title = $this->getProduct()->title;

        if ($this->getProduct() instanceof \Variation) {
            $title .= ' ('.$this->getProduct()->variationTitle.')';
        }

        return $title;
    }

    public function getPriceWithVat(): \Decimal
    {
        // cenu cele polozky (muze byt nekolik kusu) nemame zaokrouhlovat,
        // protoze cena za kus uz ma byt spravne zaokrouhlena,
        // proto round = false, potom zaokrouhlime na haléře
        return $this->price->getPriceWithVat(false)->value(2);
    }

    public function getPriceWithoutVat(): \Decimal
    {
        return $this->getPriceWithVat()->removeVat($this->price->getVat());
    }

    public function getPiecePrice(): Price
    {
        if (toDecimal($this->getPieces())->isZero()) {
            return new Price(\DecimalConstants::zero(), $this->getPrice()->getCurrency(), $this->getPrice()->getVat());
        }

        return new Price(
            $this->getPrice()->getValue()->div(toDecimal($this->getPieces())),
            $this->getPrice()->getCurrency(),
            $this->getPrice()->getVat()
        );
    }

    public function getPiecePriceWithVat(): \Decimal
    {
        if (toDecimal($this->getPieces())->isZero()) {
            return \DecimalConstants::zero();
        }

        return $this->getPriceWithVat()->div(toDecimal($this->getPieces()));
    }

    public function getPiecePriceWithoutVat(): \Decimal
    {
        if (toDecimal($this->getPieces())->isZero()) {
            return \DecimalConstants::zero();
        }

        return $this->getPriceWithoutVat()->div(toDecimal($this->getPieces()));
    }

    public function getPriceWithDiscounts(): Price
    {
        if (!$this->getDiscounts()) {
            return $this->priceWithDiscounts = $this->price;
        }

        $price = $this->getPriceWithVat();

        foreach ($this->discounts as $discount) {
            $discountPrice = $discount['discountPrice']->addVat($this->price->getVat())->mul(toDecimal($this->pieces));
            $price = $price->sub($discountPrice, 2);
        }

        return $this->priceWithDiscounts = new Price($price->removeVat($this->price->getVat()), $this->price->getCurrency(), $this->price->getVat());
    }

    public function getPiecePriceWithDiscounts($round = true): Price
    {
        if (!$this->getDiscounts()) {
            return $this->piecePriceWithDiscounts = $this->getPiecePrice();
        }

        $piecePrice = $this->getPiecePrice();

        $price = $piecePrice->getPriceWithVat($round);

        foreach ($this->discounts as $discount) {
            $discountPrice = $discount['discountPrice']->addVat($piecePrice->getVat());
            $price = $price->sub($discountPrice, 2);
        }

        return $this->piecePriceWithDiscounts = new Price($price->removeVat($piecePrice->getVat()), $piecePrice->getCurrency(), $piecePrice->getVat());
    }

    public function getPriceWithDiscountsWithVat(): \Decimal
    {
        // cenu po sleve vzdy zaokrouhlit na haléře
        return $this->getPriceWithDiscounts()->getPriceWithVat(false)->value(2);
    }

    public function setProduct(\Product $product): self
    {
        $this->product = $product;

        return $this;
    }

    public function getProduct(): \Product
    {
        if (!$this->product) {
            if ($this->purchaseState) {
                $this->purchaseState->createProductCollection();
            }

            // pokud produkt nebyl nacteny pres product list, tak ho vytvorim normalne
            if (!$this->product) {
                $this->product = $this->id_variation ? new \Variation($this->id_product, $this->id_variation) : new \Product();
                $this->product->createFromDB($this->id_product);
            }
        }

        return $this->product;
    }

    public function getIdDiscount()
    {
        return $this->id_discount;
    }

    public function __sleep()
    {
        $vars = get_object_vars($this);

        unset($vars['product']);

        return array_keys($vars);
    }

    public function getDiscounts()
    {
        return $this->discounts;
    }

    public function addDiscount($discount)
    {
        $this->discounts[] = $discount;
    }

    public function getStep(): float
    {
        // V engine je možné získat jednotku dvěma způsoby
        // Modul UNITS je pro každou jednotku zvlášť
        // Modul STEP bere jednotku z customData produktu

        $step = 1;
        if (findModule(\Modules::PRODUCTS, \Modules::SUB_STEP)) {
            $step = $this->getProduct()->getData()['step'] ?? 1;
        }

        if (findModule(\Modules::PRODUCTS, \Modules::SUB_UNITS)) {
            $step = $this->getProduct()->unit['step'] ?? 1;
        }

        return max((float) $step, 1.0);
    }

    public function getDate(): ?string
    {
        return $this->date;
    }

    public function setDate(?string $date): void
    {
        $this->date = $date;
    }
}
