<?php

namespace CouponsPlus\App\Conditions\BuiltIn\Filters;

use CouponsPlus\App\Conditions\Filter;
use CouponsPlus\App\Conditions\CartItem;
use CouponsPlus\App\Conditions\ItemsSet;
use CouponsPlus\Original\Environment\Env;
use CouponsPlus\Original\Collections\Collection;
use CouponsPlus\App\Coupon\Tree\Data\SelectOption;
use CouponsPlus\App\Conditions\IntersectionFilter;
use CouponsPlus\App\Handlers\RestTaxonomyAncestors;
use CouponsPlus\App\Validators\InclusionValueValidator;
use CouponsPlus\App\Conditions\Utilities\ProductUtilities;
use CouponsPlus\App\Coupon\Tree\Data\Abilities\PreloadableData;

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

    /**
     * Examples:
     *  VALID:
     *   -allowed one category, not nmixed with other filter, pass many items of that (like 3) but the result will only contain 1
     *   -MAXIMUM of 1 item, passed 3, the result is still one and valid.
     *   -allowed 2 categories, not mixed with other filter (in the passed items example, only pass items from one category so as to make clear that is *either* category and not BOTH)
     *   -allowed 2 categpries + quantity: 2 | pass 1 item of each to make clear they both sum 2 and thus they pass
     *   -exclude category
     *   
     *
     *  INVALID
     *   -allowed one catgeory, no items from that categpry were passed
     *   -allowed one category + quantity: 2 | passed only 1
     */
    public static function getDescription() : string
    {
        return '';
    }

    public static function getUseCases(): Collection
    {
        return new Collection([
            [
                'title' => __('Minimum and/or maximum quantities in categories', 'coupons-plus-for-woocommerce'),//__('Apply a discount to items in specific categories with minimum and/or maximum quantities', 'coupons-plus-for-woocommerce'),
                'content' => [
                    // examples
                    __('10% off 2 or more items in the "Clothing" category.', 'coupons-plus-for-woocommerce'),
                    __('10% off items in the "Clothing" category. Minimum of 2 items. Maxmimum 5 items.', 'coupons-plus-for-woocommerce'),
                ]
            ],
            [
                'title' => __('BUY ONE, GET ONE (BOGO)', 'coupons-plus-for-woocommerce'),//__('Apply a discount to items in specific categories with minimum and/or maximum quantities', 'coupons-plus-for-woocommerce'),
                'content' => [
                    // examples
                    // buy 1 in category "Clothing" and get 1 in FREE (or 50% off)
                    // buy 1 in category "Clothing" and 1 in category "Shoes" and get 1 additional item in category "Shoes" for FREE (or 50% off)
                ]
            ],
            [
                'title' => __('BUY X, GET Y', 'coupons-plus-for-woocommerce'),//__('Apply a discount to items in specific categories with minimum and/or maximum quantities', 'coupons-plus-for-woocommerce'),
                'content' => [
                    // examples
                    // buy 1 in category "Clothing" and get 1 in category "Shoes" for FREE (or 50% off)
                    // buy 1 in category "Clothing" and 1 in category "Shoes" and get 1 in category "Accessories" for FREE (or 50% off)
                ]
            ],
            [
                'title' => __('Separate discount for each category with a single coupon', 'coupons-plus-for-woocommerce'),
                'content' => [
                    // examples
                    __('10% off all items in "Hoodies"', 'coupons-plus-for-woocommerce'),
                    __('20% off all items in "T-shirts"', 'coupons-plus-for-woocommerce'),
                    __('15% off all items in "Shoes"', 'coupons-plus-for-woocommerce'),
                ]

            ],
            [
                'title' => __('Combos / Bundles', 'coupons-plus-for-woocommerce'),
                'content' => [
                    // examples
                    __('2 items in categories "Hoodies", "T-shirts" or "Shoes" for $50', 'coupons-plus-for-woocommerce'),
                    __('1 item in category "Hoodies" + 1 item in category "T-shirts" + 1 item in category "Shoes" for $100', 'coupons-plus-for-woocommerce'),
                    __('[2 items in categories "Hoodies" or "T-shirts"] + [1 Brown Belt (specific product)] for $59', 'coupons-plus-for-woocommerce'),
                ]

            ],
            [
                'title' => __('Bulk discounts in specific categories', 'coupons-plus-for-woocommerce'),
                'content' => [
                    // examples
                    __('1 item in "Clothing" category: 5% OFF (or $10/each, etc.)', 'coupons-plus-for-woocommerce'),
                    __('2 items in "Clothing" category: 7% OFF (or $9.50/each, etc.)', 'coupons-plus-for-woocommerce'),
                    __('3 items in "Clothing" category: 9% OFF (or $8.80 each, etc.)', 'coupons-plus-for-woocommerce'),
                ]

            ],
            [
                'title' => __('Brands / Tags / Attributes + Categories', 'coupons-plus-for-woocommerce'),
                'content' => [
                    // examples
                    __('10% off all items in category "TV" and from brand "Samsung"', 'coupons-plus-for-woocommerce'),
                    __('10% off all items in category "Clothing" and tag "Unisex"', 'coupons-plus-for-woocommerce'),
                    __('10% off all items in category "Hoodies" and attribute "color: red"', 'coupons-plus-for-woocommerce'),
                ]
            ],
            [
                'title' => __('Minimum subtotal amount in a specific category', 'coupons-plus-for-woocommerce'),
                'content' => [
                    // examples
                    __('10% off items in category "Clothing" when buying $100 or more in that category.', 'coupons-plus-for-woocommerce'),
                    __('$20 off the cart subtotal when buying $100 or more in the category "Clothing"', 'coupons-plus-for-woocommerce'),
                ]
            ],
        ]);
    }

    public static function getOptions() : Collection
    {
        return InclusionValueValidator::getOptions(
            $expectedValues = [
                'meta' => [
                    'name' => __('Categories', 'coupons-plus-for-woocommerce'),
                ],
                'allowed' => [
                    'type' => 'remote',
                    'where' => 'rest.WooCommerce',
                    'category' => 'categories',
                    'path' => 'products/categories',
                    'searchArgumentName' => 'search',
                    'excludeIdsArgumentName' => 'exclude',
                    'arguments' => [
                        'context' => 'edit',
                    ]
                ]
            ],
            $inclusionType = [
                'meta' => [
                    'extraLabels' => [
                        'allowed' => [
                            'placeholder' => __('Search categories to include...', 'coupons-plus-for-woocommerce'),
                            'aboveList' => __('Will pick items in any of these categories:', 'coupons-plus-for-woocommerce')
                        ],
                        'forbidden' => [
                            'placeholder' => __('Search categories to exclude...', 'coupons-plus-for-woocommerce'),
                            'aboveList' => __('Will pick items in any categories except these:', 'coupons-plus-for-woocommerce')
                        ]
                    ],
                    'notes' => [
                        'allowed' => [
                            'id' => 'InCategories.inclusionType',
                            'title' => __('Any combination of categories', 'coupons-plus-for-woocommerce'),
                            'content' => [
                                __('Will take any combination of products in the specified categories.', 'coupons-plus-for-woocommerce'),
                                __('Not all of the specified categories must be present in the cart, only one (or more) of them.', 'coupons-plus-for-woocommerce'),
                            ]
                        ],
                        'forbidden' => [
                            'id' => 'InCategories.inclusionType',
                            'title' => __('Any combination of categories except those specified', 'coupons-plus-for-woocommerce'),
                            'content' => [__('Will take products in any categories that are not in any of the specified categories.', 'coupons-plus-for-woocommerce')]
                        ],
                        'allowed-except-forbidden' => [
                            'id' => 'InCategories.inclusionType',
                            'title' => __('Any combination of the specified categories except specific subcategories', 'coupons-plus-for-woocommerce'),
                            'content' => [__('Will take products in any of the specified categories and will exclude any specified subcategories.', 'coupons-plus-for-woocommerce')]
                        ],
                    ]
                ]
            ],
            $excludedSubsetValues = [
                /**
                 * Why here and not in a separate filter or in a prefilter?
                 *
                 * -not in a prefilters: because what if the user has a grove with multiple trees (each tree a different discount for a different category))
                 *                       in such a case, what if the user wants to exclude a category from one of the trees?
                 *                       prefilters are applied to all trees.
                 *                       we need a way to exclude in a per-group basis.
                 * -not in a separate filter: too much complexity for the user to understand that they have to add a separate filter for excluding categories.
                 */
                'meta' => [
                    'supportsExcludingSubset' => true,
                    'extraLabels' => [
                        'allowed' => [
                            'placeholder' => __('Search subcategories to exclude...', 'coupons-plus-for-woocommerce'),
                            'aboveList' => __('Will exclude any of these subcategories:', 'coupons-plus-for-woocommerce')
                        ],
                    ]
                ],
                'allowed' => [
                    'type' => 'remote',
                    'where' => 'rest.WooCommerce',
                    'path' => 'products/categories',
                    'searchArgumentName' => 'search',
                    'excludeIdsArgumentName' => 'exclude',
                    'arguments' => [
                        'context' => 'edit',
                    ]
                ]

            ]
        );
    }

    public static function preloadedData() : Collection
    {
        return new Collection([
            'category' => 'categories',
            'data' => function (array $ids) : array {
                // convert the ids into formatted data from the database
                return Collection::create($ids)->map(
                /**
                 * @param int $id
                 * @return SelectOption|int|null
                 */ function (int $id) {
                    $term = get_term($id, 'product_cat');

                    if (!$term || is_wp_error($term)) {
                        return $id; // the client will handle this as ?
                    }

                    $ancestors = (new RestTaxonomyAncestors)->get_term_ancestors($term);

                    return new SelectOption(
                        $id, // id
                        /**
                         * This is a port of the logic in MultiSelectSearch.tsx.
                         *
                         */
                        $ancestors? [
                            'type' => 'hierarchy',
                            'value' => array_merge($ancestors, [$term->name])
                        ] : $term->name,//label
                        $term->name//label
                    );
                })->asArray();
            }
        ]);
    }
    public function onOptionsLoaded()
    {
        do_action(Env::getWithPrefix('IDsLoaded'), [
            'type' => 'productCategories',
            'ids' => $this->options->expectedValues
        ]);
    }

    public function filterSet(ItemsSet $itemsSet) : ItemsSet
    {
        (object) $inclusionValueValidator = new InclusionValueValidator($this->options->asArray());

        (object) $allAllowedOrForbiddenItems = new ItemsSet($itemsSet->getItems()->filter(function(CartItem $cartItem) use ($inclusionValueValidator) : bool {
            (object) $originalFinalproductUtilities = new ProductUtilities($cartItem->getProductUtilities()->getOrignalFinalProduct());

            $inclusionValueValidator->setValueToValidate(
                $originalFinalproductUtilities->getAllTranslationCategoryIdsWithAncestors()
            );

            return $inclusionValueValidator->isValid();
        })->asArray());

        if ($this->options->inclusionType->is('forbidden') || !$this->options->enableExcludedSubsetValues) {
            return $allAllowedOrForbiddenItems;
        }

        $inclusionValueValidatorFoSubCategories = new InclusionValueValidator([
            'expectedValues' => $this->options->excludedSubsetValues,
            'inclusionType' => 'forbidden',
        ]);

        return new ItemsSet($allAllowedOrForbiddenItems->getItems()->filter(function(CartItem $cartItem) use ($inclusionValueValidatorFoSubCategories) : bool {
            (object) $originalFinalproductUtilities = new ProductUtilities($cartItem->getProductUtilities()->getOrignalFinalProduct());

            $inclusionValueValidatorFoSubCategories->setValueToValidate($originalFinalproductUtilities->getAllTranslationCategoryIdsWithAncestors());

            return $inclusionValueValidatorFoSubCategories->isValid();

        })->asArray());
    }
}