<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\Tests\Marketing;

use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Model\DeliveryAddress;
use CpCreation\VitiCore\Cart\Repository\CartRepository;
use CpCreation\VitiCore\Marketing\AbandonedCartRuleMatcher;
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 AbandonedCartRuleTest extends TestCase
{
    public function testConfigurationRecordsEachActivationDate(): void
    {
        $configuration = new AbandonedCartConfiguration();

        self::assertNull($configuration->getEnabledAt());

        $configuration->setEnabled(true);
        $firstActivation = $configuration->getEnabledAt();

        self::assertNotNull($firstActivation);

        $configuration->setEnabled(false);
        $configuration->setEnabled(true);

        self::assertNotNull($configuration->getEnabledAt());
        self::assertGreaterThanOrEqual($firstActivation, $configuration->getEnabledAt());
    }

    public function testItNormalizesCountriesAndIdentifiesCouponRules(): void
    {
        $rule = new AbandonedCartRule(new AbandonedCartConfiguration());
        $priceGroup = new ProductPriceGroup();
        $rule->setPriceGroup($priceGroup);
        $rule->setCountryCodes([' fr ', 'BE', 'fr', '', 'invalid']);
        $rule->setDiscountType(AbandonedCartRule::DISCOUNT_FREE_SHIPPING);

        self::assertSame(['BE', 'FR'], $rule->getCountryCodes());
        self::assertSame($priceGroup, $rule->getPriceGroup());
        self::assertTrue($rule->createsCoupon());
        self::assertTrue($rule->isCouponOnlyWithoutExistingDiscount());

        $rule->setCouponOnlyWithoutExistingDiscount(false);

        self::assertFalse($rule->isCouponOnlyWithoutExistingDiscount());

        $rule->setDiscountType(AbandonedCartRule::DISCOUNT_NONE);

        self::assertFalse($rule->createsCoupon());
    }

    public function testMatcherCombinesCountryAmountAndCustomerHistory(): void
    {
        $cart = new Cart();
        $address = new DeliveryAddress();
        $address->setCountry('fr');
        $cart->setDeliveryAddress($address);

        $rule = new AbandonedCartRule(new AbandonedCartConfiguration());
        $rule->setPriceGroup(new ProductPriceGroup());
        $rule->setCountryCodes(['FR']);
        $rule->setMinSubtotalTTC(0);
        $rule->setCustomerType(AbandonedCartRule::CUSTOMER_RETURNING);

        $cartRepository = $this->createMock(CartRepository::class);
        $cartRepository
            ->expects(self::once())
            ->method('hasCompletedOrderForCustomer')
            ->with($cart)
            ->willReturn(true);
        $priceCodeRepository = $this->createStub(ProductPriceCodeRepository::class);
        $priceGroupRepository = $this->createStub(ProductPriceGroupRepository::class);
        $priceGroupRepository->method('getGroupByDefault')->willReturn($rule->getPriceGroup());

        $matcher = new AbandonedCartRuleMatcher(
            $cartRepository,
            $priceCodeRepository,
            $priceGroupRepository,
        );

        self::assertTrue($matcher->matches($rule, $cart));

        $rule->setCountryCodes(['BE']);

        self::assertFalse($matcher->matches($rule, $cart));
    }

    public function testMatcherRejectsARuleForAnotherPriceGroup(): void
    {
        $defaultGroup = new ProductPriceGroup();
        $otherGroup = new ProductPriceGroup();
        $rule = new AbandonedCartRule(new AbandonedCartConfiguration());
        $rule->setPriceGroup($otherGroup);

        $priceGroupRepository = $this->createStub(ProductPriceGroupRepository::class);
        $priceGroupRepository->method('getGroupByDefault')->willReturn($defaultGroup);
        $matcher = new AbandonedCartRuleMatcher(
            $this->createStub(CartRepository::class),
            $this->createStub(ProductPriceCodeRepository::class),
            $priceGroupRepository,
        );

        self::assertFalse($matcher->matches($rule, new Cart()));

        $rule->setPriceGroup($defaultGroup);

        self::assertTrue($matcher->matches($rule, new Cart()));
    }
}
