<?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\Model\Cart;
use CpCreation\VitiCore\Cart\Repository\PromoRepository;
use CpCreation\VitiCore\Marketing\AbandonedCartPromoFactory;
use CpCreation\VitiCore\Marketing\Model\AbandonedCartConfiguration;
use CpCreation\VitiCore\Marketing\Model\AbandonedCartRule;
use CpCreation\VitiCore\Product\Model\ProductPriceGroup;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

final class AbandonedCartPromoFactoryTest extends TestCase
{
    #[DataProvider('discountProvider')]
    public function testItCreatesSingleUseRestrictedCoupons(
        string $discountType,
        ?int $amountTTC,
        ?float $percent,
        ?float $expectedPercent,
        ?float $shippingPercent,
    ): void {
        $promoRepository = $this->createMock(PromoRepository::class);
        $promoRepository->method('findPromoByCode')->willReturn(null);

        $rule = new AbandonedCartRule(new AbandonedCartConfiguration());
        $rule->setDiscountType($discountType);
        $rule->setDiscountAmountTTC($amountTTC);
        $rule->setDiscountPercent($percent);
        $rule->setMinSubtotalTTC(5000);
        $rule->setMaxSubtotalTTC(25000);
        $rule->setCountryCodes(['FR']);
        $priceGroup = new ProductPriceGroup();
        $priceGroup->setName('Particuliers');
        $rule->setPriceGroup($priceGroup);

        $beginAt = new \DateTimeImmutable('2026-07-23 12:00:00');
        $endAt = $beginAt->modify('+7 days');
        $cart = new Cart();
        $promo = new AbandonedCartPromoFactory($promoRepository)->create(
            $rule,
            $cart,
            'client@example.com',
            $beginAt,
            $endAt,
        );

        self::assertNotNull($promo);
        self::assertStringStartsWith('PANIER-', (string) $promo->getTitle());
        self::assertSame(1, $promo->getMaxCount());
        self::assertSame(1, $promo->getEffectiveMaxCountByCustomer());
        self::assertTrue($promo->isCumulative());
        self::assertSame(5000, $promo->getMinSubtotalTTC());
        self::assertSame(25000, $promo->getMaxSubtotalTTC());
        self::assertSame(['FR'], $promo->getRestrictedCountryCodes());
        self::assertCount(1, $promo->getGroups());
        self::assertSame($priceGroup, $promo->getGroups()->first());
        self::assertSame('client@example.com', $promo->getNotificationEmail());
        self::assertSame($amountTTC, $promo->getAmountTTC());
        self::assertSame($expectedPercent, $promo->getPercent());
        self::assertSame($shippingPercent, $promo->getPercentShippingCost());
    }

    /**
     * @return iterable<string, array{string, ?int, ?float, ?float, ?float}>
     */
    public static function discountProvider(): iterable
    {
        yield 'fixed' => [AbandonedCartRule::DISCOUNT_FIXED, 1500, null, null, null];
        yield 'percent' => [AbandonedCartRule::DISCOUNT_PERCENT, null, 12.5, 0.125, null];
        yield 'free shipping' => [AbandonedCartRule::DISCOUNT_FREE_SHIPPING, null, null, null, 1.0];
    }

    public function testItDoesNotCreateCouponForEmailOnlyReminder(): void
    {
        $promoRepository = $this->createMock(PromoRepository::class);
        $rule = new AbandonedCartRule(new AbandonedCartConfiguration());

        $promo = new AbandonedCartPromoFactory($promoRepository)->create(
            $rule,
            new Cart(),
            'client@example.com',
            new \DateTimeImmutable(),
            new \DateTimeImmutable('+7 days'),
        );

        self::assertNull($promo);
    }

    public function testItDoesNotCreateCouponWhenCartAlreadyHasDiscount(): void
    {
        $promoRepository = $this->createMock(PromoRepository::class);
        $promoRepository->expects(self::never())->method('findPromoByCode');
        $rule = new AbandonedCartRule(new AbandonedCartConfiguration());
        $rule->setDiscountType(AbandonedCartRule::DISCOUNT_PERCENT);
        $rule->setDiscountPercent(10);
        $cart = new Cart();
        $cart->addPromo(new CartPromo(
            code: 'DEJA-REMIS',
            discountItemsTTC: 500,
        ));

        $promo = new AbandonedCartPromoFactory($promoRepository)->create(
            $rule,
            $cart,
            'client@example.com',
            new \DateTimeImmutable(),
            new \DateTimeImmutable('+7 days'),
        );

        self::assertNull($promo);
    }

    public function testItCreatesCouponForDiscountedCartWhenProtectionIsDisabled(): void
    {
        $promoRepository = $this->createMock(PromoRepository::class);
        $promoRepository->method('findPromoByCode')->willReturn(null);
        $rule = new AbandonedCartRule(new AbandonedCartConfiguration());
        $rule->setDiscountType(AbandonedCartRule::DISCOUNT_PERCENT);
        $rule->setDiscountPercent(10);
        $rule->setCouponOnlyWithoutExistingDiscount(false);
        $cart = new Cart();
        $cart->addPromo(new CartPromo(
            code: 'DEJA-REMIS',
            discountItemsTTC: 500,
        ));

        $promo = new AbandonedCartPromoFactory($promoRepository)->create(
            $rule,
            $cart,
            'client@example.com',
            new \DateTimeImmutable(),
            new \DateTimeImmutable('+7 days'),
        );

        self::assertNotNull($promo);
    }
}
