<?php

namespace Shop\Infrastructure\Repositories\Stock;

use Diamond\Repositories\PropelAbstractRepository;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\ActiveQuery\ModelCriteria;
use Propel\Runtime\Collection\ObjectCollection;
use Propel\Runtime\Map\TableMap;
use Repo\CollectionInterface;
use Repo\CrudRepositoryBuilderInterface;
use Repo\EntityInterface;
use Repo\PaginationInterface;
use Shop\Domain\Stock\Contracts\StockCriteriaInterface;
use Shop\Domain\Stock\StockCollection;
use Shop\Infrastructure\Models\Address\Address;
use Shop\Infrastructure\Models\Shop\Shop;
use Shop\Infrastructure\Models\Shop\ShopQuery;

class StockRepository extends PropelAbstractRepository implements CrudRepositoryBuilderInterface
{
    public static function createEntity(): EntityInterface
    {
        return new Shop();
    }

    protected function createQuery(): ModelCriteria
    {
        return ShopQuery::create();
    }

    /**
     * @param array $row
     * @param EntityInterface|null $entity
     * @return EntityInterface
     */
    public function buildEntityFromArray(array $row, EntityInterface $entity = null): EntityInterface
    {
        if(isset($row['parentId']) && $row['parentId']===''){
            $row['parentId'] = null;
        }

        if (!$entity) {
            $entity = static::createEntity();
        }

        $entity->fromArray($row, TableMap::TYPE_CAMELNAME);

        if(!$address = $entity->getAddress()){
            $address = new Address();
        }
        $address->fromArray($row, TableMap::TYPE_CAMELNAME);
        $entity->setAddress($address);

        return $entity;

    }


    /**
     * @param PaginationInterface|StockCriteriaInterface $criteria
     * @param ModelCriteria|ShopQuery $dbCriteria
     */
    protected function modifyCriteria(PaginationInterface $criteria, ModelCriteria $dbCriteria): void
    {

        $dbCriteria
            ->leftJoinWithAddress()

            ->_if($criteria->getFilterByParentId()!==null)
            ->filterByParentId($criteria->getFilterByParentId())
            ->_endif()

            ->_if($criteria->getFilterByActive()!==null)
            ->filterByActive($criteria->getFilterByActive()===true?'Y':'N')
            ->_endif()

            ->_if($criteria->getFilterByCityId()!==null)
            ->filterByCityId($criteria->getFilterByCityId())
            ->_endif()

            ->_if($criteria->getFilterByRegionId()!==null)
            ->useAddressQuery()
            ->useCityQuery()
            ->filterByRegionId($criteria->getFilterByRegionId())
            ->endUse()
            ->endUse()
            ->_endif()

            ->_if($criteria->getSortByTitle()!==null)
            ->orderByTitle($criteria->getSortByTitle())
            ->_endif()
            ->_if($criteria->getFilterByTypes() !== null)
            ->filterByType($criteria->getFilterByTypes())
            ->_endif()
            ->_if($criteria->getFilterByType() !== null)
            ->filterByType($criteria->getFilterByType())
            ->_endif()
            ->_if($criteria->getFilterByContractorId() !== null)
            ->filterByContractorId($criteria->getFilterByContractorId())
            ->_endif()

            ->_if($criteria->getSearchByTitle()!==null)
            ->filterByTitle($criteria->getSearchByTitle() . '%',Criteria::LIKE)
            ->_endif()
        ;

    }

    /**
     * @param PaginationInterface $criteria
     * @return CollectionInterface|StockCollection
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function findByCriteria(PaginationInterface $criteria): CollectionInterface
    {
        $query = $this->createQuery();
        $this->modifyCriteria($criteria, $query);
        $result = $query->paginate($criteria->getPage(), $criteria->getLimit());

        $collection = self::createCollection();
        $rows = $result->getResults();

        if ($criteria->asTreeView() === true) {
            $this->treeAdaptive($rows, $collection);
        } else {
            foreach ($rows as $row) {
                $collection->push($row);
            }
        }

        return $collection;
    }

    protected function createCollection(): CollectionInterface
    {
        return new StockCollection();
    }

    /**
     * поиск как дилеров так и ГО
     * @deprecated
     * @todo перевести на Criteria
     * @param array $shopKeys
     * @return StockCollection
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function findMainAndDealers($shopKeys = []): StockCollection
    {
        $res =  $this->createQuery()
            ->_if(count($shopKeys))
            ->filterByPrimaryKeys($shopKeys)
            ->_endif()
            ->useAddressQuery()
            ->useCityQuery()
            ->orderByName()
            ->endUse()
            ->endUse()
            ->filterByActive('Y')
            ->findByType(["dealer", "main"]);

        $collection = new StockCollection();
        foreach ($res as $model){
            $collection->push($model);
        }
        return $collection;
    }


    /**
     * @param ObjectCollection $rows
     * @param StockCollection $collection
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    private function treeAdaptive(ObjectCollection $rows, StockCollection $collection): void
    {

        $new = [];

        /**
         * @var Shop $row
         * @var Shop $child
         */
        foreach ($rows as $row) {


            if ($row->getParentId() > 0) {
                $new[$row->getParentId()]['childs'][$row->getId()] = $row;
            }
            else{
                $new[$row->getId()]['model'] = $row;
            }
        }



        usort($new,function($el, $el2){

            if(isset($el['childs'])){
                return -1;
            }

            if(isset($el['model']) && isset($el2['model']) && $el['model']->getTitle() < $el2['model']->getTitle()){
                return -1;
            }

            return 1;
        });

        foreach ($new as $data) {

            $row = $data['model']??null;

            if (!isset($data['childs']) && $row) {
                $collection->push($row);
                continue;
            }
            if($row){
                $row->setCountChildrens(count($data['childs']));
                $collection->push($row);
            }


            usort($data['childs'],function($el, $el2){
                /**
                 * @var Menu $el
                 * @var Menu $el2
                 */
                return $el->getTitle() > $el2->getTitle() ? 1: -1;
            });

            foreach ($data['childs'] as $child) {
                $collection->push($child);
            }
        }

        //dd($collection);
    }

}