<?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\Cart;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Context\GroupContext;
use CpCreation\VitiCore\Cart\CartPricing;
use CpCreation\VitiCore\Cart\CartPricingFactory;
use CpCreation\VitiCore\Cart\CartService;
use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Model\CartCheckout;
use CpCreation\VitiCore\Product\DegressiveCalculatorInterface;
use CpCreation\VitiCore\Product\DegressiveContextFactory;
use CpCreation\VitiCore\Product\DegressiveManager;
use CpCreation\VitiCore\Product\Model\ProductPriceGroup;
use CpCreation\VitiCore\Product\Repository\ProductPriceGroupRepository;
use CpCreation\VitiCore\User\Model\User;
use Dunglas\DoctrineJsonOdm\Serializer;
use Dunglas\DoctrineJsonOdm\TypeMapper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;

final class CartPricingPriceGroupSnapshotTest extends TestCase
{
    protected function tearDown(): void
    {
        GroupContext::$groupId = null;
        GroupContext::$partnerCode = null;
    }

    public function testCartServiceSnapshotsTheCustomerPriceGroupWithoutPartnerCode(): void
    {
        $group = new ProductPriceGroup();
        $group->setName('Club clients');

        $user = new User();
        $user->setGroup($group);

        $cart = new Cart();
        $cart->setUser($user);

        $pricing = $this->createCartService()->hydratePricing($cart)->getPricing();

        self::assertNotNull($pricing);
        self::assertSame($group->getId()->toString(), $pricing->priceGroupId);
        self::assertSame('Club clients', $pricing->priceGroupName);
        self::assertNull($cart->getGroupCode());
    }

    public function testDoctrineJsonOdmDeserializesALegacySnapshotWithoutPriceGroupMetadata(): void
    {
        $serializer = new Serializer(
            [new ArrayDenormalizer(), new ObjectNormalizer()],
            [new JsonEncoder()],
            new TypeMapper([]),
        );

        $pricing = $serializer->deserialize(json_encode([
            '#type' => CartPricing::class,
            'lines' => [],
            'taxRate' => 0.2,
            'subtotalHT' => 1000,
            'subtotalTax' => 200,
            'subtotalTTC' => 1200,
            'discountHT' => 0,
            'discountTax' => 0,
            'discountTTC' => 0,
            'degressiveHT' => 0,
            'degressiveTax' => 0,
            'degressiveTTC' => 0,
            'totalHT' => 1000,
            'totalTax' => 200,
            'totalTTC' => 1200,
            'degressiveIsBottleOffered' => false,
        ], \JSON_THROW_ON_ERROR), '', 'json');

        self::assertInstanceOf(CartPricing::class, $pricing);
        self::assertNull($pricing->priceGroupId);
        self::assertNull($pricing->priceGroupName);
    }

    public function testOrderedCartKeepsItsAppliedPriceGroupSnapshot(): void
    {
        $originalGroup = new ProductPriceGroup();
        $originalGroup->setName('Club clients');

        $user = new User();
        $user->setGroup($originalGroup);

        $cart = new Cart();
        $cart->setUser($user);

        $cartService = $this->createCartService();
        $originalPricing = $cartService->hydratePricing($cart)->getPricing();

        $checkout = new CartCheckout();
        $checkout->setOrderedAt(new \DateTimeImmutable());
        $cart->setCartCheckout($checkout);

        $newGroup = new ProductPriceGroup();
        $newGroup->setName('Nouveau groupe');
        $user->setGroup($newGroup);

        self::assertTrue($cart->hasFrozenCommerceSnapshot());
        self::assertSame($originalPricing, $cartService->hydratePricing($cart)->getPricing());
        self::assertSame('Club clients', $cart->getPricing()?->priceGroupName);
    }

    private function createCartService(): CartService
    {
        $priceGroupRepository = $this->createStub(ProductPriceGroupRepository::class);
        $degressiveManager = new DegressiveManager(
            new DegressiveContextFactory(new RequestStack(), $priceGroupRepository),
            $this->createStub(DegressiveCalculatorInterface::class),
        );

        $reflection = new \ReflectionClass(CartService::class);
        /** @var CartService $cartService */
        $cartService = $reflection->newInstanceWithoutConstructor();
        $reflection->getProperty('degressiveManager')->setValue($cartService, $degressiveManager);
        $reflection->getProperty('cartPricingFactory')->setValue($cartService, new CartPricingFactory());

        return $cartService;
    }
}
