<?php

namespace CouponsPlus\App\Coupon\Tree\TestableHooks;

use CouponsPlus\Original\Collections\Collection;
use CouponsPlus\App\Coupon\Tree\Abilities\Branch;
use CouponsPlus\App\Coupon\Tree\Testable\ProtectedGlobalState;
use CouponsPlus\App\Coupon\Tree\Sets\Abilities\ItemsSetSorterInterface;

/**
 * Why this? because we need to set a state once in a deeply nested tree.
 *
 * Unfortunately, passing the dependency one by one to all components in the tree is just not feasible.
 *
 * So we have designed this api akin to React.useContext() to set data once for all descendants in
 * order to prevent "prop drilling" or in this case, "dependency drilling". It's the exact same concept.
 *
 * Why not set this while constructing the tree? because this is *dynamic* so one can change this at their discretion
 * at runtime.
 *
 * Ideally I'd love to inject these data manually, but that would require a whole lot of redesigning & refactoring,
 * so I have to be pragmatic.
 */

/**
 * ItemsSet Sorter
 *
 * Currently supported by:
 *  ItemsSet
 * 
 * @template T
 * @param ItemsSetSorterInterface $itemsSetSorter
 * @param callable(): T $callback
 * @return T
 */
function usingSorter(ItemsSetSorterInterface $itemsSetSorter, callable $callback)
{
    $key = ProtectedGlobalState::getInstance()->addData('items_set_sorter', $itemsSetSorter);

    $result = $callback();

    ProtectedGlobalState::getInstance()->removeData($key);
    
    return $result;
}

function useItemsSetSorter() : ?ItemsSetSorterInterface
{
    return ProtectedGlobalState::getInstance()->getData('items_set_sorter');
}

/**
 *
 *
 * Currently supported by:
 *  ItemsSet
 *
 * @template T
 * @param ItemsSetSorterInterface $itemsSetSorter
 * @param callable(): T $callback
 * @return T
 */
function usingBranch(Branch $branch, callable $callback)
{
    $key = ProtectedGlobalState::getInstance()->addData('current_branch', $branch);

    $result = $callback();

    ProtectedGlobalState::getInstance()->removeData($key);

    return $result;
}

function useBranch() : ?Branch
{
    return ProtectedGlobalState::getInstance()->getData('current_branch');
}


/**
 * Exclude Testable types (basically filters and basically the NumberOfItems filter)
 *
 * @template T
 * @param callable(): T $callback
 * @return T
 */
function excludingTestableTypes(array $testableTypesToExclude, callable $callback)
{
    $key = ProtectedGlobalState::getInstance()->addData('excluded_testables', new Collection($testableTypesToExclude));

    $result = $callback();

    ProtectedGlobalState::getInstance()->removeData($key);

    return $result;
}

function useExcludedTestableTypes() : Collection
{
    return ProtectedGlobalState::getInstance()->getData('excluded_testables') ?? new Collection([]);
}

/**
 * Adds extra testables on runtime
 */
function usingTestableTypes(array $testableTypes, callable $callback)
{
    $key = ProtectedGlobalState::getInstance()->addData('extra_testable_types', new Collection($testableTypes));

    $callback();

    ProtectedGlobalState::getInstance()->removeData($key);
}

function useExtraTestableTypes() : Collection
{
    return ProtectedGlobalState::getInstance()->getData('extra_testable_types') ?? new Collection([]);
}