<?php


namespace Shop\Application\Basket;


use Core\Basket\Basket;
use Shop\Application\Basket\AddItemInCurrentBasket\AddItemInCurrentBasketCommand;
use Shop\Application\Basket\Dto\Discount;
use Shop\Application\Basket\Exceptions\BasketNotFoundException;
use Shop\Application\Office\OfficeService;
use Shop\Presentation\Config\Main;

use Shop\Domain\Basket\Contracts\BasketEntityInterface;
use Shop\Domain\Customer\Contracts\CustomerEntityInterface;
use Shop\Infrastructure\Repositories\Basket\BasketCriteria;
use Shop\Infrastructure\Repositories\Basket\BasketRepository;
use Shop\Infrastructure\Repositories\Discount\DiscountRepository;
use Shop\Application\Basket\AddItemInCurrentBasket\BasketItemRequest;
use Shop\Application\Basket\AddItemInCurrentBasket\BasketItemResult;
use spaceonfire\CommandBus\CommandBus;

/**
 * Class BasketService
 * @package Shop\Application\Basket
 */
class BasketService
{
    protected $basketRepository;
    protected $officeService;
    protected $basketLib;
    protected $mainConfig;
    protected $discountRepository;
    protected $commandBus;

    /**
     * BasketService constructor.
     * @param BasketRepository $basketRepository
     * @param OfficeService $officeService
     * @param DiscountRepository $discountRepository
     * @param CommandBus $commandBus
     * @param Basket $basket
     * @param Main $mainConfig
     */
    public function __construct(BasketRepository $basketRepository,
                                OfficeService $officeService,
                                DiscountRepository $discountRepository,
                                CommandBus $commandBus,
                                Basket $basket,
                                Main $mainConfig)
    {
        $this->basketRepository = $basketRepository;
        $this->officeService = $officeService;
        $this->basketLib = $basket;
        $this->mainConfig = $mainConfig;
        $this->commandBus = $commandBus;
        $this->discountRepository = $discountRepository;
    }

    /**
     * @return BasketEntityInterface
     * @throws \Propel\Runtime\Exception\PropelException
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function getCurrentBasket(): BasketEntityInterface
    {
        $customer = $this->officeService->getCurrentCustomer();

        $baskets = $this->basketRepository->findByCriteria(
            BasketCriteria::create()->setFilterByCustomer($customer)
        );

        foreach ($baskets as $basket) {
            if ($basket->getName() === 'default') {
                return new \Shop\Application\Basket\Decorators\Basket(
                    $basket,
                    $this->basketLib
                    );
            }
        }

        throw new BasketNotFoundException('current basket not found');
    }


    /**
     * @return int
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function getScoresInCurrentBasket(): int
    {
        $scores = 0;

        foreach ($this->getCurrentBasket()->getItems() as $item) {
            if (strpos($item->getMenagerComment(), "score") !== false) {
                continue;
            }
            $scores = $scores + $item->getPrice() * $item->getAmount();
        }
        return abs($scores);
    }


    /**
     * @return int
     * @throws \Propel\Runtime\Exception\PropelException
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function calculateScoreInCurrentBasket(): int
    {
        $sum = $this->getCurrentBasket()->getSum();

        if ($this->mainConfig->getOrderScoreSumm() > 0) {
            $amount = floor($sum / $this->mainConfig->getOrderScoreSumm());
            return (int)$amount * $this->mainConfig->getOrderScoreVolume();
        }

        return 0;
    }

    /**
     * @param BasketEntityInterface $basketEntity
     */
    public function saveBasket(BasketEntityInterface $basketEntity): void
    {
        $this->basketRepository->save($basketEntity);
    }


    /**
     * Подсчет след.скидки на основе корзины тек пользователя
     * @param BasketEntityInterface $basket
     * @return Discount
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function calculateSumForNextDiscount(BasketEntityInterface $basket): Discount
    {

        $customer = $this->officeService->getCurrentCustomer();

        return $this->calculateSumForNextDiscountForCustomer($basket, $customer);
    }


    /**
     * @param BasketEntityInterface $basket
     * @param CustomerEntityInterface $customer
     * @return Discount
     */
    public function calculateSumForNextDiscountForCustomer(BasketEntityInterface $basket, CustomerEntityInterface $customer): Discount
    {

        if (is_array($this->mainConfig->getOrderDiscountIgnoreCustomerGroups()) &&
            in_array($customer->getGroupId(), $this->mainConfig->getOrderDiscountIgnoreCustomerGroups(), false)) {
            return new Discount();
        }

        if (!$discount = $this->discountRepository->findNextDiscount($basket->getSum())) {
            return new Discount();
        }

        $Sum = $discount->getPriceFrom() - $basket->getSum();

        return (new Discount())->setSum((float)$Sum)->setPercent($discount->getPercent());
    }


    /**
     * @param BasketEntityInterface $basket
     * @return Discount
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function calculateSumDiscount(BasketEntityInterface $basket): Discount
    {
        return $this->calculateDiscountBySum($basket->getSum());
    }

    /**
     * @param float $SumBasket
     * @return Discount
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function calculateDiscountBySum(float $SumBasket): Discount
    {
        $customer = $this->officeService->getCurrentCustomer();

        return $this->calculateDiscountBySumForCustomer($SumBasket, $customer);
    }


    /**
     * @param float $SumBasket
     * @param CustomerEntityInterface $customer
     * @return Discount
     */
    public function calculateDiscountBySumForCustomer(float $SumBasket, CustomerEntityInterface $customer): Discount
    {

        if (is_array($this->mainConfig->getOrderDiscountIgnoreCustomerGroups())
            && in_array($customer->getGroupId(), $this->mainConfig->getOrderDiscountIgnoreCustomerGroups(), false)) {
            return new Discount();
        }

        $discountDto = new Discount;

        if (!$discount = $this->discountRepository->findDiscount($SumBasket)) {
            return $discountDto;
        }

        $Sum = $SumBasket / 100 * $discount->getPercent();

        return $discountDto->setSum((float)$Sum)->setPercent($discount->getPercent());
    }




    /**
     * @param BasketItemRequest $itemRequest
     * @return string
     */
    public function addItem(BasketItemRequest $itemRequest): string
    {
        $basketItemResult = new BasketItemResult();

        $this->commandBus->handle(new AddItemInCurrentBasketCommand($itemRequest, $basketItemResult));
        return $basketItemResult->getSourceId();
    }

    /**
     * @param string $item
     */
    public function deleteItem(string $item): void{
        $this->basketLib->deleteByKey($item);
    }


}