<?php

namespace CouponsPlus\App\Offers;

USE WC_Coupon;
use CouponsPlus\App\Coupon\Coupon;
use CouponsPlus\App\Coupon\CouponRegistry;

#[\AllowDynamicProperties]
Abstract Class CouponDiscountAmount
{
    protected /*float*/ $amount = 0.0;
    protected /*WC_Coupon*/ $classicCoupon;
    protected /*integer*/ $numberOfItemsAppliedTo;
    protected static /*boolean*/ $discountHasBeenSent = [];
    protected $maximumAmount = 0;
    protected Coupon $coupon;

    abstract public function getDiscountedAmount(float $sumOfAllDiscounts, float $discountingAmount, /*array|null*/ $cartItemArray, bool $single, \WC_Coupon $coupon) /* : float*/;

    abstract public function registerEvents() : void;
    abstract public function unregisterEvents() : void;

    public function __construct(WC_Coupon $coupon)
    {
        $this->classicCoupon = $coupon;
        $this->coupon = CouponRegistry::getInstance()->get($coupon);

        /**
         * classes are required to specifcy they hooks ()
         */
        $this->registerEvents();

        add_action('woocommerce_cart_reset', [$this, 'unregisterEvent']);
        add_action('woocommerce_after_calculate_totals', [$this, 'unregisterEvent']);
    }

    public function getCoupon(): Coupon
    {
        return $this->coupon;
    }

    public function setHardLimit(float $maximumAmount)
    {
        $this->maximumAmount = $maximumAmount;
    }

    /**
     * NOTE THE *ADD*, this *adds* to the exisiting amount,
     * do not confuse this with *set* (which overrides the previous amount)
     */
    public function addAmount(float $amount, int $numberOfItemsAppliedTo = 0)
    {
        $this->amount += $amount;
        $this->numberOfItemsAppliedTo = $numberOfItemsAppliedTo;
    }

    /**
     * Very important, since a new CouponDiscountAmount object
     * is created every time in woocommerce_before_calculate_totals 
     * so if $cart->calculate_totals() is run thrice:
     *
     * $cart->calculate_totals();
     * $cart->calculate_totals();
     * $cart->calculate_totals();
     *
     * we'll protentially have three times the same woocommerce_coupon_get_discount_amount
     * handler. So we remove it after successfully sending the discount amount.
     * So if another call to $cart->calculate_totals() happens, we're not running this twice.
     *
    */ 
    public function unregisterEvent()
    {
        remove_filter(
            'woocommerce_coupon_get_discount_amount',
            [$this, 'getDiscountedAmount'],
            $priority = 10 
        );
    }

    public function getFinalDiscount(... $arguments): float
    {
        $intendedDiscount = $this->getDiscountedAmount(... $arguments);
        $intendedDiscountIsLargerThanTheHardLimit = bccomp($intendedDiscount, $this->maximumAmount, 2) > 0;

        /** @noinspection NotOptimalIfConditionsInspection */
        if ($this->hasHardLimit() && $intendedDiscountIsLargerThanTheHardLimit) {
            return $this->maximumAmount;
        }

        return $intendedDiscount;
    }

    /**
     * @return bool
     */
    public function hasHardLimit(): bool
    {
        return bccomp($this->maximumAmount, '0', 2) > 0;
    }

    public function getClassicCoupon(): WC_Coupon
    {
        return $this->classicCoupon;
    }

    public function getNumberOfItemsAppliedTo() : int
    {
        return $this->numberOfItemsAppliedTo;
    }

    protected function isOurCoupon(WC_Coupon $coupon): bool
    {
        return $this->coupon->getClassic()->get_code('edit') === $coupon->get_code('edit');
    }

}