<?php

namespace CouponsPlus\App\Coupon\Tree\Factories;

use CouponsPlus\App\Conditions\Filter;
use CouponsPlus\App\Conditions\SubsetFilter;
use CouponsPlus\App\Coupon\Tree\TestableGroup;
use CouponsPlus\App\Conditions\IntersectionFilter;
use CouponsPlus\App\Conditions\CompositeANDCondition;
use CouponsPlus\App\Conditions\CompositeFilter;
use CouponsPlus\App\Coupon\Abilities\DashboardCard;
use CouponsPlus\App\Coupon\Context;
use CouponsPlus\App\Coupon\Tree\Abilities\TestableData;
use CouponsPlus\App\Coupon\Tree\SequentialTestableGroup;
use CouponsPlus\App\Coupon\Tree\IntersectionTestableGroup;
use CouponsPlus\App\Conditions\Abilities\TestableComposite;
use CouponsPlus\App\Coupon\Tree\Abilities\TestableGroupData;
use CouponsPlus\App\Offers\OffersSet;
use CouponsPlus\Original\Collections\Collection;
use CouponsPlus\App\Coupon\Tree\IgnorableCardsTestableGroup;
use CouponsPlus\App\Conditions\BuiltIn\Filters\NumberOfItems;
use CouponsPlus\App\Coupon\Tree\BranchSubsetSumTestableGroup;

class ContextFromTestableGroupDataFactory
{
    private $ConditionFilterOrOfferFromDataFactory;

    public function __construct(ConditionFilterOrOfferFromDataFactory $ConditionFilterOrOfferFromDataFactory)
    {
        $this->ConditionFilterOrOfferFromDataFactory = $ConditionFilterOrOfferFromDataFactory;
    } 

    public function create(TestableGroupData $testableGroupData) : TestableComposite
    {
        (boolean) $childrenAreConditions = $this->testablesAreConditionComponents($testableGroupData->getChildren());
        (object) $TestableComposite = $childrenAreConditions? CompositeANDCondition::class : CompositeFilter::class;

        $testables = $this->ConditionFilterOrOfferFromDataFactory->createFromTestableDataCollection(
            $testableGroupData->getChildren()
        );

        if ($childrenAreConditions) {
            return new CompositeANDCondition(
                $testables->asArray()
            );
        }

        return new TestableGroup(
            new SequentialTestableGroup,
            [
                /**
                 * First the intersection filters (pretty much all filters fall in this category)
                 */
                new TestableGroup(
                    new IgnorableCardsTestableGroup(
                        new IntersectionTestableGroup
                    ),
                    $this->getIntersectionFilters($testables)
                ),
                /**
                 * Then the subset filters (NumberOfItems)
                 */
                new BranchSubsetSumTestableGroup(
                    new TestableGroup(
                        new IgnorableCardsTestableGroup(
                            new SequentialTestableGroup
                        ),
                        $this->getSubsetFilters($testables)
                    )
                )
            ]
        );

        return new Context(
            new $TestableComposite(
            /**
             * so instead of the CompositeFilter (from $TestableComposite)),
             * here we'll refactr this sht to use an itemssetfiter composite:
             * new ItemsSetFilterComposite([
             *      new IntersectionFilter($this->filters->filter(function(Filter $filter) {
                      return $filter->isIntersected();
                    })),
             *      new SubsetFilter($this->filters->filter(function(Filter $filter) {
                       return $filter->isSubsetSum();
                    })),
             * ])
             *
             * of better yet, a KeepsSameSorterItemsSetFiltererComposite:
             * new KeepsSameSorterItemsSetFiltererComposite([
             *      new IntersectionFilter,
             *      new SubsetFilter,
             * ])
             *
             */
                $testableGroupData->getChildren()->map(
                    function(TestableData $testableData) use ($childrenAreConditions) : DashboardCard {
                        return $this->ConditionFilterOrOfferFromDataFactory->create(
                            $type = $childrenAreConditions? 'condition' : 'filter',
                            $testableData
                        );
                    } 
                )->asArray()
            ), 
            new OffersSet([])
        );
    }

    protected function testablesAreConditionComponents(Collection $testableDatas) : bool
    {
        // we can't just pick the first one because if the collection is empty we'd have an error
        // and a context may only have conditions or filters so all the children should have the same type
        return (boolean) $testableDatas->find(function(TestableData $testableData) : bool {
            return $testableData->getTestableType()->is('condition');
        });
    }

    private function getIntersectionFilters(Collection $testables): Collection
    {
        return $testables->filter(function(Filter $testable) {
            return $testable->isIntersected();
        });
    }

    private function getSubsetFilters(Collection $testables): Collection
    {
        $subsets = $testables->filter(function(Filter $testable) {
            return $testable->isSubset();
        });

        return $subsets;
    }
}