<?php

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Tests\Marketing;

use CpCreation\VitiCore\Cart\CartPromo;
use CpCreation\VitiCore\Cart\CartShipping;
use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Model\CartItem;
use CpCreation\VitiCore\Cart\Repository\CartRepository;
use CpCreation\VitiCore\Marketing\AbandonedCartRuleMatcher;
use CpCreation\VitiCore\Marketing\AbandonedCartRuleSelector;
use CpCreation\VitiCore\Marketing\Model\AbandonedCartConfiguration;
use CpCreation\VitiCore\Marketing\Model\AbandonedCartRule;
use CpCreation\VitiCore\Product\Model\ProductPriceGroup;
use CpCreation\VitiCore\Product\Repository\ProductPriceCodeRepository;
use CpCreation\VitiCore\Product\Repository\ProductPriceGroupRepository;
use PHPUnit\Framework\TestCase;

final class AbandonedCartRuleSelectorTest extends TestCase
{
    private ProductPriceGroup $defaultPriceGroup;

    protected function setUp(): void
    {
        $this->defaultPriceGroup = new ProductPriceGroup();
    }

    public function testItSelectsTheHighestMonetaryBenefit(): void
    {
        $cart = $this->createCart(subtotalTTC: 10000, shippingTTC: 1200);
        $fixedRule = $this->createRule(AbandonedCartRule::DISCOUNT_FIXED, position: 1);
        $fixedRule->setDiscountAmountTTC(1500);
        $percentRule = $this->createRule(AbandonedCartRule::DISCOUNT_PERCENT, position: 2);
        $percentRule->setDiscountPercent(20);
        $freeShippingRule = $this->createRule(AbandonedCartRule::DISCOUNT_FREE_SHIPPING, position: 3);

        $selector = $this->createSelector();

        self::assertSame(
            $percentRule,
            $selector->selectMostAdvantageous([$fixedRule, $percentRule, $freeShippingRule], $cart),
        );
        self::assertSame(1500, $selector->estimateBenefitTTC($fixedRule, $cart));
        self::assertSame(2000, $selector->estimateBenefitTTC($percentRule, $cart));
        self::assertSame(1200, $selector->estimateBenefitTTC($freeShippingRule, $cart));
    }

    public function testItUsesPositionToBreakAnEqualBenefit(): void
    {
        $cart = $this->createCart(subtotalTTC: 10000);
        $lowerPriorityRule = $this->createRule(AbandonedCartRule::DISCOUNT_FIXED, position: 5);
        $lowerPriorityRule->setDiscountAmountTTC(1000);
        $higherPriorityRule = $this->createRule(AbandonedCartRule::DISCOUNT_PERCENT, position: 2);
        $higherPriorityRule->setDiscountPercent(10);

        self::assertSame(
            $higherPriorityRule,
            $this->createSelector()->selectMostAdvantageous([$lowerPriorityRule, $higherPriorityRule], $cart),
        );
    }

    public function testItAccountsForCouponProtectionOnAnAlreadyDiscountedCart(): void
    {
        $cart = $this->createCart(subtotalTTC: 10000);
        $cart->addPromo(new CartPromo(discountItemsTTC: 100));

        $protectedRule = $this->createRule(AbandonedCartRule::DISCOUNT_PERCENT, position: 1);
        $protectedRule->setDiscountPercent(50);
        $unprotectedRule = $this->createRule(AbandonedCartRule::DISCOUNT_FIXED, position: 2);
        $unprotectedRule->setDiscountAmountTTC(500);
        $unprotectedRule->setCouponOnlyWithoutExistingDiscount(false);

        self::assertSame(
            $unprotectedRule,
            $this->createSelector()->selectMostAdvantageous([$protectedRule, $unprotectedRule], $cart),
        );
    }

    private function createSelector(): AbandonedCartRuleSelector
    {
        $cartRepository = $this->createMock(CartRepository::class);
        $priceGroupRepository = $this->createStub(ProductPriceGroupRepository::class);
        $priceGroupRepository->method('getGroupByDefault')->willReturn($this->defaultPriceGroup);

        return new AbandonedCartRuleSelector(new AbandonedCartRuleMatcher(
            $cartRepository,
            $this->createStub(ProductPriceCodeRepository::class),
            $priceGroupRepository,
        ));
    }

    private function createCart(int $subtotalTTC, int $shippingTTC = 0): Cart
    {
        $item = $this->createMock(CartItem::class);
        $item->method('getTotalFinalTTC')->willReturn($subtotalTTC);
        $cart = new Cart();
        $cart->addItem($item);
        $cart->setCartShipping(new CartShipping($shippingTTC, 0.2));

        return $cart;
    }

    private function createRule(string $discountType, int $position): AbandonedCartRule
    {
        $rule = new AbandonedCartRule(new AbandonedCartConfiguration());
        $rule->setDiscountType($discountType);
        $rule->setPosition($position);
        $rule->setPriceGroup($this->defaultPriceGroup);

        return $rule;
    }
}
