<?php

namespace CouponsPlus\App\Conditions\BuiltIn\Filters;

use CouponsPlus\App\Conditions\CartItem;
use CouponsPlus\App\Conditions\Filter;
use CouponsPlus\App\Conditions\IntersectionFilter;
use CouponsPlus\App\Conditions\ItemsSet;
use CouponsPlus\Original\Collections\Collection;
use CouponsPlus\Original\Collections\Mapper\Types;
use CouponsPlus\Original\Environment\Env;
use CouponsPlus\App\Coupon\Tree\Data\SelectOption;
use CouponsPlus\App\Coupon\Tree\Data\Abilities\PreloadableData;
use CouponsPlus\App\Handlers\ProductVariationSearchAdminAjaxExtender;

#[\AllowDynamicProperties]
Class Products extends Filter implements PreloadableData
{
    const TYPE = 'Products';
    const SET = IntersectionFilter::class;
    
    public static function getName() : string
    {
        return __('Products', 'coupons-plus-for-woocommerce');
    }

    public static function getDescription() : string
    {
        return __('', 'coupons-plus-for-woocommerce');
    }

    public static function getOptions() : Collection
    {
	    /**
         * mode: short:
         *
         * int[]
         *
         * mode: classic:
	     * [
	     *      (id) [
	     *          'model' => 'variation.new',
	     *          'type' => 'any'|'any.except'|'only'
	     *          'variationGroups => [
	     *              [
	     *                  [
	     *                      id: pa_ (this is the name eg: color)
	     *                  ]
	     *              ]
	     *          ]
	     *      ]
	     * ]
	     *
	     */
        return new Collection([
            'mode' => Types::STRING()->withDefault('classic')->allowed(['classic', 'short']),
            'ids' => Types::COLLECTION()->meta([
                '__allowed' => [
                    'type' => 'remote',
                    'category' => 'products',
                    'where' => 'admin-ajax.php',
                    'path' => '',//'admin-ajax.php?term=red&action=woocommerce_json_search_products_and_variations&security=ee531e9a76',
                    'searchArgumentName' => 'term',
                    'excludeIdsArgumentName' => 'exclude',
                    'arguments' => [
                        'context' => 'edit',
                    ],
                    'needsNonce' => true,
                ]
            ]),
            'inclusionType' => Types::STRING()->withDefault('allowed')
                                              ->allowed([
                                                  __('Eligible', 'coupons-plus-for-woocommerce') => 'allowed',
                                                  __('Not Eligible', 'coupons-plus-for-woocommerce') => 'forbidden',
                                              ])->meta(
                                                  [
                                                      'extraLabels' => [
                                                          'allowed' => [
                                                              'placeholder' => __('Search products to include...', 'coupons-plus-for-woocommerce'),
                                                              'aboveList' => __('Will select any of these products:', 'coupons-plus-for-woocommerce')
                                                          ],
                                                          'forbidden' => [
                                                              'placeholder' => __('Search products to exclude...', 'coupons-plus-for-woocommerce'),
                                                              'aboveList' => __('Will select any products except these:', 'coupons-plus-for-woocommerce')
                                                          ]
                                                      ],
                                                      'alternativeLabels' => [
                                                              /*'allowed' => __('Only selected', 'coupons-plus-for-woocommerce'),
                                                              'forbidden' => __('Any except', 'coupons-plus-for-woocommerce'),*/
                                                          ],
                                                      ])
        ]);   
    }

    public static function preloadedData() : Collection
    {
        return new Collection([
            'category' => 'products',
            'data' => function (array $ids) : array {
                $formater = new ProductVariationSearchAdminAjaxExtender;

                // convert the ids into formatted data from the database
                return Collection::create($formater->formatProducts($ids))->filter(function ($value) {
                    // if product doesnt exist, it will be the id, formatting would have skipped this
                    return is_array($value);
                })->map(
                    function (array $productData): SelectOption {

                    return new SelectOption(
                        $productData['id'], // id
                        [
                            'type' => 'product',
                            'option' => $productData
                        ],// value
                        $productData['name']//label
                    );
                })->asArray();
            }
        ]);
    }

    public function onOptionsLoaded()
    {
        do_action(Env::getWithPrefix('products_loaded_ids'), $this->getProductIdsData());
    }

    public function filterSet(ItemsSet $itemsSet) : ItemsSet
    {
        /**
         * 'ids' => [
                ['id' => 100, 'variationIDs' => [110, 130]]
            ],
         */
        /*
        +
        + If it's a variable product and no variation ids have been specified (emty array),
        + then ANY variation will be accepted.
        +
        +*/
        return new ItemsSet($itemsSet->getItems()->filter(function(CartItem $cartItem) : bool {
            if ($cartItem->getProduct()->get_type() === 'variation') {
                (boolean) $productWasFound = (boolean) $this->getProductIdsData()->find(function(array $idData) use ($cartItem) /*: ?int*/ {
                    $parentId = $cartItem->getProduct()->get_parent_id($context = 'edit');

                    if ($this->options->mode->is('short')) {
                        return $idData['id'] === $parentId || $cartItem->getProduct()->get_id() === $idData['id'] ;
                    }
                    (boolean) $mainProductIdMatches = $idData['id'] === $parentId;

                    (boolean) $variationIDMatches = in_array($cartItem->getProduct()->get_id(), $idData['variationIDs']);
                    (boolean) $hasNotSpecifiedAVariationSoANYVariationShouldPass = empty($idData['variationIDs']);

                    return $mainProductIdMatches 
                            && 
                           ($variationIDMatches || $hasNotSpecifiedAVariationSoANYVariationShouldPass);
                });
            } else {
                (boolean) $productWasFound = (boolean) $this->getProductIdsData()->map(function(array $idData) /*: ?int*/ {
                    return $idData['id'];
                })->contain($cartItem->getProduct()->get_id());
            }
            return $this->options->inclusionType->is('allowed') ? $productWasFound : !$productWasFound;
        })->asArray());
    }

    protected function getProductIdsData() : Collection
    {
        return $this->cache->getIfExists('productIds')->otherwise(function() : Collection {

            (array) $idDataTemplate = ['id' => null, 'variationIDs' => [/*110, 130*/]];
            // we'll make sure it's the right format.
            return $this->options->ids->map(function(/*array|int*/$idData) use ($idDataTemplate) : array {
                if ($this->options->mode->is('short')) {
                    return [
                        'id' => (int) $idData,
                        'variationIDs' => [] // unused in short mode
                    ];
                }
                (array) $ids = array_merge($idDataTemplate, (array) $idData);

                $ids['id'] = (integer) $ids['id'];
                $ids['variationIDs'] = (array) $ids['variationIDs'];

                return $ids;
            });
        });
    }
}