<?php

namespace CouponsPlus\App\Coupon\Tree\Finders;

use CouponsPlus\App\Conditions\ItemsSet;
use CouponsPlus\Original\Collections\Collection;
use CouponsPlus\App\Coupon\Abilities\OffersSetFinder;
use CouponsPlus\App\Coupon\CartComponentsSet;
use CouponsPlus\App\Coupon\Tree\Finders\Sets\OffersDataSet;
use CouponsPlus\App\Coupon\Tree\Abilities\FindableOffersFromNode;
use CouponsPlus\App\Coupon\Tree\Abilities\Grove;
use CouponsPlus\App\Coupon\Tree\Abilities\NodeData;
use CouponsPlus\App\Offers\OffersSetCollection;

/**
 * My beautiful offers engine :)
 */
class GroveOffersDataFinder implements OffersSetFinder
{
    /**
     * @var true
     */
    protected static bool $isShutOff = false;
    protected $grove;
    protected $nodeOffersFinder;

    public static function shutOff(): void
    {
        static::$isShutOff = true;
    }

    public static function resume(): void
    {
        static::$isShutOff = false;
    }

    public static function isShutOff(): bool
    {
        return static::$isShutOff;
    }

    public function __construct(
        Grove $grove,
        /**
         * This one's a decorator
         * it has an exception catcher that will return an empty CartComponentsSet when encountering an exception
         */
        FindableOffersFromNode $nodeOffersFinder
    ) {
        $this->grove = $grove;
        $this->nodeOffersFinder = $nodeOffersFinder;
    }

    /**
     * Returns a OffersSetCollection with a collection of *OffersDataSet** object
     *
     * This returns only the offers data, not the actual offer objects.
     */
    public function findOffers(ItemsSet $itemsSet): CartComponentsSet
    {
        // Only run if the extension is loaded AND enabled via the -d flag
        (object) $offersSetCollection = new OffersSetCollection([]);
        //(object) $offerItemsForTheFoundOffers = new Collection([]);
        OffersSetCollection::resetRegisteredItemsToUse();
        /** @var NodeData $tree */
        foreach ($this->grove->getTrees()->asArray() as $treeRootNode) {
            (int) $recursiveIterationIndex = 0;
            (string) $currentOriginId = $treeRootNode->getId();
            // here run a while loop if the offer is recursive. and run it until we have no more items to disocunt.
            do {
                $ItemsSetExcludingUsedItems = $itemsSet->withoutCollectionOf(
                    $offersSetCollection->getAllItemsUsed(
                        // if this is recursive, do noe exclude ids from being excluded because this
                        // exclusion by id is designed for other trees, not the same tree in recursive iterations
                        $recursiveIterationIndex === 0? $currentOriginId : null
                    ),
                    'sum' // sum so that we do NOT union same items. so 1x product a and 2x product a will become 3x product a, because items used and discounted are different.
                );

                /** @var OffersDataSet $offersSetCollectionForCurrentTree */
                (object) $offersSetCollectionForCurrentTree = $this->nodeOffersFinder->findOffers(
                    $ItemsSetExcludingUsedItems,
                    $treeRootNode
                );

                if ($offersSetCollectionForCurrentTree->getItems()->haveAny()) {
                    $offersSetCollectionForCurrentTree->setOriginId($currentOriginId);
                    $offersSetCollection->getItems()->push($offersSetCollectionForCurrentTree);
                    // this so that other offers may use this internally to exclude items already used by other offers
                    OffersSetCollection::registerItemsToUse(
                        $offersSetCollectionForCurrentTree->getOfferItems(),
                        $offersSetCollectionForCurrentTree->getOffer()->getID(),
                        $recursiveIterationIndex //todo: when we do virtualization, multiply this by the index of the virtual branch
                    );
                    $recursiveIterationIndex++;
                    // here we'll add the cart items from the offfer sets to the $itemstoignore passed to the next tree

                    // ok so here we know the current tree has offers, so:
                    // 1 let's request the itemstouse for the branch and set a vraible to it
                    // 2 if the offer is recursive, then set a recursive flag to true
                }
            } while ($offersSetCollectionForCurrentTree->getItems()->haveAny() && $offersSetCollectionForCurrentTree->isRecursive());
        }

        return $offersSetCollection;
    }        
}