<?php

namespace CouponsPlus\App\Coupon\Tree\Data\Transformers;

use CouponsPlus\Original\Collections\Collection;
use CouponsPlus\App\Conditions\FiltersRegistrator;
use CouponsPlus\App\Conditions\ConditionsRegistrator;
use CouponsPlus\App\Coupon\Tree\Finders\BottomToTopNodeFinder;
use CouponsPlus\App\Coupon\Tree\Finders\FirstPassingNodeFinder;
use CouponsPlus\App\Coupon\Tree\Data\Transformers\Abilities\DataTransformer;
use CouponsPlus\App\Coupon\Tree\Finders\FilterReturningTheMostItemsNodeFinder;
use CouponsPlus\App\Coupon\Tree\Data\Transformers\Abilities\TreeNodeAlgorithmResolver;

class TieredBranchTransformer implements DataTransformer
{
    private DataTransformer $treeNodeTransformer;
    public function __construct(DataTransformer $treeNodeTransformer)
    {
        $this->treeNodeTransformer = $treeNodeTransformer;
    }
    /**
     * @param object $data
     */
    public function transform($data) : object
    {
        /**
         * This will:
         * 1. change the type from 'tiered' to a type known to the Offers Engine. EG: type: 'tiered' -> type: 'filter-returning-most-items'
         * 3. merge the testable from data.baseTestable to each child treenodes.testable (currently tiered branches have their chidlren as a testable partial. we need to add the full testabe to each child)
         * 4. work on each child treenode recursively so that transofmrations occur if they are also tiered branches
         */
        $branchChildTransformer = new TransformerComposite(
            [
                new MergeBaseTestableIntoTestablePartialTransformer($data),
                $this->treeNodeTransformer // this might be recursive if the child is also a tiered branch

            ],
            new AllTransformersStrategy()
        );

        return (object) array_merge(
            (array) $data,
            [
                'type' => $this->figureOutAlgorithm($data),
                'children' => Collection::create($data->children)->map(
                    function (object $childData) use ($branchChildTransformer): object {
                        return $branchChildTransformer->transform($childData);
                    }
                )->asArray()
            ]
        );
    }

    /*
     * Here we'll take the data.baseTestable and try to figure out the algorithm from it.
    */
    private function figureOutAlgorithm(object $treeNodeData) : string
    {
        $tierType = $treeNodeData->data->tierType; // 'numeric', etc.
        $testableType = $treeNodeData->data->baseTestable->testableType; // 'filter' | 'condition'
        $componentType = $treeNodeData->data->baseTestable->type; // 'NumberOfItems', 'InCategories', etc.
        $ComponentRegistrator = $testableType === 'filter' ? FiltersRegistrator::class : ConditionsRegistrator::class;
        $component = $ComponentRegistrator::get()->getByType($componentType);

        if ($resolvedType = $this->tryToResoleCustomType($component, $treeNodeData)) {
            return $resolvedType;
        }

        if ($tierType === 'numeric') {
            if ($testableType === 'filter') {
                return FilterReturningTheMostItemsNodeFinder::TYPE;
            }

            if ($testableType === 'condition') {
                return BottomToTopNodeFinder::TYPE;
            }
        }

        // default
        return FirstPassingNodeFinder::TYPE;
    }

    private function tryToResoleCustomType(string $filter, object $treeNodeData) : ?string
    {
        if (is_a($filter, TreeNodeAlgorithmResolver::class, true) && $resolvedType = $filter::resolveTreeNodeAlgorithm($treeNodeData)) {
            return $resolvedType;
        }
    }
}