<?php

namespace Shop\Repositories\Basket;

use Core\Basket\Basket;
use Repo\CollectionInterface;
use Repo\EntityInterface;
use Repo\PaginationInterface;
use Shop\Domain\Basket\BasketCollection;
use Shop\Domain\Basket\BasketEntity;
use Shop\Domain\Basket\BasketItem;
use Shop\Domain\Basket\Contracts\BasketCriteriaInterface;
use Shop\Domain\Basket\Contracts\BasketEntityInterface;
use Shop\Domain\Basket\Contracts\BasketRepositoryInterface;
use Shop\Domain\Customer\Contracts\CustomerEntityInterface;
use Shop\Models\Position\Position;
use Shop\Repositories\Customer\CustomerRepository;
use Shop\Repositories\PricePosition\PricePositionCriteria;
use Shop\Repositories\PricePosition\PricePositionRepository;

class BasketRepository implements BasketRepositoryInterface
{
    protected $basket;
    protected $pricePositionRepository;
    protected $customerRepository;

    /**
     * BasketRepository constructor.
     * @param Basket $basket
     */
    public function __construct(Basket $basket,
                                PricePositionRepository $pricePositionRepository,
                                CustomerRepository $customerRepository)
    {
        $this->basket = $basket;
        $this->pricePositionRepository = $pricePositionRepository;
        $this->customerRepository = $customerRepository;
    }

    /**
     * @return EntityInterface|BasketEntity
     */
    public static function createEntity(): EntityInterface
    {
        return (new BasketEntity())->setName('default');
    }

    public function buildEntityFromArray(array $row, EntityInterface $entity = null): EntityInterface
    {
        return self::createEntity();
    }

    /**
     * @param EntityInterface|BasketEntityInterface $entity
     */
    public function save(\Repo\EntityInterface $entity): void
    {
        $this->basket->clear();

        foreach ($entity->getItems() as $item) {
            $this->addBasketItem($item);
        }
        //$entity = $this->getBasket();
    }

    public function delete(\Repo\EntityInterface $entity): void
    {
        // TODO: Implement delete() method.
    }

    public function findById(int $id): ?EntityInterface
    {
        return $this->getBasket();
    }

    /**
     * @param PaginationInterface $criteria
     * @return CollectionInterface
     * @throws \Propel\Runtime\Exception\PropelException
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function findByCriteria(PaginationInterface $criteria): CollectionInterface
    {
        $collection = new BasketCollection();

        $collection->push($this->getBasket($criteria->getFilterByCustomer()));

        return $collection;
    }

    public function count(?PaginationInterface $criteria): int
    {
        return $this->findByCriteria($criteria)->count();
    }


    /**
     * @param CustomerEntityInterface $customer
     * @return BasketEntityInterface
     * @throws \Propel\Runtime\Exception\PropelException
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    private function getBasket(CustomerEntityInterface $customer): BasketEntityInterface
    {

        $count = $this->basket->getCount();
        $summ = $this->basket->getSumm();
        $items = [];

        foreach ($this->basket->getItems() as $k => $item) {

            $items[] = $this->buildItem($k, $item, $customer);
        }

        $basket = self::createEntity()
            ->setItems($items)
            ->setCustomer($customer);

        $basket->setId(time());

        return $basket;
    }

    /**
     * @param BasketItem $item
     */
    private function addBasketItem(BasketItem $item): void
    {
        $article = $item->getArticle();
        $key = $this->basket->createKey($article, $item->getPrice());
        $exit = $this->basket->getItemByKey($key);
        if ($item->getAmount() === 0 && $exit) {
            $this->basket->deleteByKey($key);
        } elseif ($exit) {
            $this->basket->updateItem(
                $key,
                $article,
                $item->getPrice(),
                $item->getAmount(),
                $this->createData($item)
            );

        } else {
            $key = $this->basket->addItem(
                $article,
                $item->getPrice(),
                $item->getAmount(),
                $this->createData($item)
            );
            $item->setId($key);
        }
    }


    /**
     * @param \Shop\DTO\BasketItemRequest $item
     * @return array
     */
    private function createData(BasketItem $item): array
    {
        return [
            "name" => $item->getName(),
            'destination' => $item->getDestination(),
            "desc" => $item->getDesc(),
            "ref" => $item->getRef(),
            "brand" => $item->getBrand(),
            "providerId" => $item->getProviderId(),
            "menagerComment" => $item->getMenagerComment()
        ];
    }

    /**
     * @param string $k
     * @param array $item
     * @param CustomerEntityInterface $customerEntity
     * @return BasketItem
     * @throws \Propel\Runtime\Exception\PropelException
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    private function buildItem(string $k, array $item, CustomerEntityInterface $customerEntity): BasketItem
    {

        $ref = $item['data']['ref'];

        //если в к позиции по какой то причине отсутсвует поставщик поробуем найти его
        if(!$providerId = $item["data"]["providerId"] ?? null){

            $positions = $this->pricePositionRepository->findByCriteria(
              PricePositionCriteria::create()->setFilterByRefId($ref)
            );
            /**
             * @var Position $position
             */
            if(!$position = $positions->current()){
                $providerId = 0;// хоть что то добавим
            }
            else{
                $providerId = $position->getPricelistSheet()->getPricelistFile()->getProviderId();
            }
        }


        $item = (new BasketItem())
            ->setId($k)
            ->setPrice($item["price"])
            ->setAmount($item["amount"])
            ->setName($item["data"]["name"])
            ->setArticle($item["article"])
            ->setRef($ref)
            ->setBrand($item["data"]["brand"])
            ->setDesc($item["data"]["desc"])
            ->setProviderId($providerId)
            ->setDestination($item["data"]["destination"])
            ->setMenagerComment(isset($item["data"]["menagerComment"]) ? $item["data"]["menagerComment"] : null);


        $positions = $this->pricePositionRepository->findByCriteria(
            PricePositionCriteria::create()->setFilterByRefId($item->getRef())
        );

        if (!$position = $positions->current()) {
            $item->setState(BasketItem::STATE_OUT_STOCK);
            return $item;
        }

        /**
         * только так получаем позиции с корректным ценообразованием
         */
        $allPositions = $position->getProduct()->getCurrentPositions($customerEntity);


        $currentPrice = null;

        foreach ($allPositions as $findPosition) {


            if ($findPosition->getRefId() !== $item->getRef()) {
                continue;
            }

            $currentPrice = $findPosition->getPriceFinal();
            break;
        }

        if (!$currentPrice) {
            $item->setState(BasketItem::STATE_OUT_STOCK);
            return $item;
        }

        if ($item->getPrice() > $currentPrice) {
            $item->setState(BasketItem::STATE_DOWN_PRICE);
            $item->setPrice($currentPrice);
        } elseif ($item->getPrice() < $currentPrice) {
            $item->setState(BasketItem::STATE_UP_PRICE);
            $item->setPrice($currentPrice);
        }

        return $item;

    }

}