<?php

namespace CouponsPlus\App\Coupon\Tree\ItemSetsToDiscount;

use Throwable;
use WC_Product;
use WC_Product_Variable;
use CouponsPlus\Original\Characters\StringManager;
use CouponsPlus\App\Coupon\Tree\Abilities\ItemToDiscount;

class ProductToDiscount implements ItemToDiscount
{
    protected int $id;
    protected ProductDiscountOptions $discountOptions;
    /**
     * This ia a small performance optimization so that we don't query products from the database on runtime
     * just to check if it's a variable product. We can do this once in the admin dashboard when adding the product.
     *
     * This might fail in the very rare case that the admin changes the product type
     */
    protected bool $isVariable;
    /**
     * @var callable(int $id): WC_Product
     */
    protected $productGetter;
    protected CartActions $cartActions;
    protected StringManager $mode;

    /**
     * @param int $id
     * @param ProductDiscountOptions $discountOptions
     * @param bool $isVariable
     * @param callable(int $id): WC_Product $productGetter
     */
    public function __construct(
        int $id,
        ProductDiscountOptions $discountOptions,
        /**
         * What would happen if the product type changes from variable to simple or vice versa?
         */
        bool $isVariable,
        CartActions $cartActions,
        callable $productGetter
    )
    {
        $this->id = $id;
        $this->isVariable = $isVariable;
        $this->discountOptions = $discountOptions;
        $this->productGetter = $productGetter;
        $this->cartActions = $cartActions;
        $this->mode = new StringManager('auto-add');
    }

    public function setMode(string $mode)
    {
        $this->mode = new StringManager($mode);
    }
    /**
     * @return int
     */
    public function getSubject() : int
    {
        return $this->id;
    }

    public function getDiscountOptions(): ProductDiscountOptions
    {
        return $this->discountOptions;
    }

    public function canBeAutomaticallyAddedToTheCart(): bool
    {
        /**
         * The admin has explicitly selected to let the user manually add this.
         */
        if (!$this->mode->is('auto-add')) {
            return false;
        }

        if ($this->isVariable) {
            return false;
        }

        return $this->canBeManuallyAddedToTheCart();
    }

    public function canBeManuallyAddedToTheCart(): bool
    {
        try {
            /**
             * I didn't want to do this because it can be very taxing on performance to query the db and load the product,
             * but uh, there are several scenarios where a product cannot be added to the cart
             * so we need to make this check for reliability.
             *
             * Possible scenarios:
             *  -out of stock
             *  -it's an affiliate product
             *  -the user ain't have the right permissions to buy it (set by a third party plugin)
             *  -any other weird custom combo by third party code (restricted specific ids, etc.)
             */
            $product = ($this->productGetter)($this->id);

            if ($this->isVariable) {
                // For variable products, check if any variation is purchasable and in stock
                /** @var WC_Product_Variable $product */
                $available_variations = $product->get_available_variations();

                foreach ($available_variations as $variation_data) {
                    $variation = ($this->productGetter)($variation_data['variation_id']);

                    if ($variation && $this->checkSpecificProductIsPurchasable($variation)) {
                        return true;
                    }
                }

                return false;
            }

            return $this->checkSpecificProductIsPurchasable($product);

        } catch (Throwable $e) {
            // If there's an issue  loading the product, assume it's not purchasable
            // This is a fallback to avoid breaking the code in case of an error
            return false;
        }
    }

    private function checkSpecificProductIsPurchasable(WC_Product $product): bool
    {
        return $this->cartActions->has('auto_apply') && $product->is_in_stock() && $product->is_purchasable();
    }
}