<?php

namespace KupShop\KupShopBundle\Util\Price;

use KupShop\I18nBundle\Entity\Currency;

class PriceCalculator
{
    /**
     * Price + Price2.
     */
    public static function add(Price $price, Price $price2): Price
    {
        self::makeCompatible($price, $price2);

        return new Price($price->getValue()->add($price2->getValue()), $price->getCurrency(), $price->getVat());
    }

    /**
     * Price - Price2.
     */
    public static function sub(Price $price, Price $price2): Price
    {
        self::makeCompatible($price, $price2);

        return new Price($price->getValue()->sub($price2->getValue()), $price->getCurrency(), $price->getVat());
    }

    /**
     * Price < Price2.
     */
    public static function firstLower(Price $price, Price $price2): bool
    {
        self::makeCompatible($price, $price2, false);

        if ($price->getPriceWithVat()->lowerThan($price2->getPriceWithVat())) {
            return true;
        }

        return false;
    }

    /**
     * Price > Price2.
     */
    public static function firstGreater(Price $price, Price $price2): bool
    {
        self::makeCompatible($price, $price2, false);

        if ($price->getPriceWithVat()->comp($price2->getPriceWithVat()) == 1) {
            return true;
        }

        return false;
    }

    /**
     * Price * Constant.
     */
    public static function mul(Price $price, \Decimal $constant): Price
    {
        return new Price($price->getValue()->mul($constant), $price->getCurrency(), $price->getVat());
    }

    public static function addDiscount(Price $price, \Decimal $discount): Price
    {
        $discount_decimal = $price->getValue()->mul($discount)->div(\DecimalConstants::hundred());

        return new Price($price->getValue()->sub($discount_decimal), $price->getCurrency(), $price->getVat());
    }

    public static function makeCompatible(Price &$price, Price &$price2, bool $priceInstanceCheck = true)
    {
        if ($priceInstanceCheck && isDevelopment() && $price instanceof ProductPrice) {
            throw new \InvalidArgumentException(
                sprintf('Cannot use "%s" with "%s"! Convert it into price using "%s::toPrice".', self::class, ProductPrice::class, self::class)
            );
        }

        // if currency is not same
        if ($price->getCurrency()->getId() != $price2->getCurrency()->getId()) {
            $price2 = self::convert($price2, $price->getCurrency());
        }

        // if vat is not same
        if ($price->getVat() != $price2->getVat()) {
            self::makeSameVat($price, $price2);
        }
    }

    public static function toPrice(Price $price): Price
    {
        return new Price(
            $price->getPriceWithoutVat(false),
            $price->getCurrency(),
            $price->getVat()
        );
    }

    protected static function makeSameVat(Price &$price, Price &$price2)
    {
        $vat = $price->getVat();

        $price2WithVat = $price2->getValue()->addVat($price2->getVat());

        if ($price2 instanceof ProductPrice) {
            $price2 = new ProductPrice(
                $price2WithVat->removeVat($vat),
                $price2->getCurrency(),
                $vat,
                $price2->getDiscount()
            );
        } else {
            $price2 = new Price($price2WithVat->removeVat($vat), $price2->getCurrency(), $vat);
        }
    }

    public static function convert(Price $price, Currency $currency): Price
    {
        if ($price->getCurrency()->getId() === $currency->getId()) {
            return $price;
        }

        // to czk
        $converted = $price->getValue()->mul($price->getCurrency()->getRate());
        // div by given currency rate
        $converted = $converted->div($currency->getRate());

        if ($price instanceof ProductPrice) {
            return (new ProductPrice($converted, $currency, $price->getVat(), $price->getDiscount()))->setSource($price->getSource());
        } elseif ($price instanceof PriceLevelPrice) {
            $newPrice = new PriceLevelPrice(
                new ProductPrice($converted, $currency, $price->getVat(), $price->getDiscount())
            );
            $newPrice->setPricelevelDiscount($price->getPricelevelDiscount());

            return $newPrice->setSource($price->getSource());
        }

        return (new Price($converted, $currency, $price->getVat()))->setSource($price->getSource());
    }
}
