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

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Context\GroupContext;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\Components\Cart\CartForm;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\Components\Cart\CheckoutForm;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\Components\Cart\ItemForm;
use CpCreation\VitiCore\Shop\Model\ShopConfig;
use CpCreation\VitiCore\Shop\Repository\ShopConfigRepository;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Contracts\Translation\TranslatorInterface;

final class CartItemFormTest extends TestCase
{
    public function testSaveIsBlockedDuringVacation(): void
    {
        $config = new ShopConfig();
        $config->setVacationOf(new \DateTime('2020-01-01 00:00:00'));
        $config->setVacationAt(new \DateTime('2100-01-01 00:00:00'));
        $config->setDisableOrdersForVacation(true);

        $reflection = new \ReflectionClass(ItemForm::class);
        /** @var ItemForm $itemForm */
        $itemForm = $reflection->newInstanceWithoutConstructor();
        $reflection->getProperty('shopConfig')->setValue($itemForm, $config);

        GroupContext::$partnerCode = 'PARTENAIRE';

        try {
            self::assertNull($itemForm->save());
        } finally {
            GroupContext::$partnerCode = null;
        }
    }

    public function testSaveIsBlockedWhenPricesAreHiddenWithoutPartnerCode(): void
    {
        $config = new ShopConfig();
        $config->setHidePriceDefault(true);

        $reflection = new \ReflectionClass(ItemForm::class);
        /** @var ItemForm $itemForm */
        $itemForm = $reflection->newInstanceWithoutConstructor();
        $reflection->getProperty('shopConfig')->setValue($itemForm, $config);
        $reflection->getProperty('requestStack')->setValue($itemForm, $this->createRequestStack());

        GroupContext::$partnerCode = null;

        self::assertNull($itemForm->save());
    }

    /**
     * @param class-string<CartForm|CheckoutForm> $componentClass
     */
    #[DataProvider('orderingComponentsProvider')]
    public function testCartAndCheckoutAreBlockedDuringVacation(string $componentClass): void
    {
        $config = new ShopConfig();
        $config->setVacationOf(new \DateTime('2020-01-01 00:00:00'));
        $config->setVacationAt(new \DateTime('2100-01-01 00:00:00'));
        $config->setDisableOrdersForVacation(true);

        self::assertSame(
            'shop.orders_disabled_for_vacation',
            $this->resolveOrderingBlockedReason($componentClass, $config),
        );
    }

    /**
     * @param class-string<CartForm|CheckoutForm> $componentClass
     */
    #[DataProvider('orderingComponentsProvider')]
    public function testCartAndCheckoutRequirePartnerCodeWhenPricesAreHidden(string $componentClass): void
    {
        $config = new ShopConfig();
        $config->setHidePriceDefault(true);

        self::assertSame(
            'shop.view_with_partner_code',
            $this->resolveOrderingBlockedReason($componentClass, $config),
        );
    }

    /**
     * @param class-string<CartForm|CheckoutForm> $componentClass
     */
    #[DataProvider('orderingComponentsProvider')]
    public function testCartAndCheckoutAllowOrderingWithPartnerCode(string $componentClass): void
    {
        $config = new ShopConfig();
        $config->setHidePriceDefault(true);

        self::assertNull($this->resolveOrderingBlockedReason($componentClass, $config, 'PARTENAIRE'));
    }

    /**
     * @return iterable<string, array{class-string<CartForm|CheckoutForm>}>
     */
    public static function orderingComponentsProvider(): iterable
    {
        yield 'panier' => [CartForm::class];
        yield 'checkout' => [CheckoutForm::class];
    }

    /**
     * @param class-string<CartForm|CheckoutForm> $componentClass
     */
    private function resolveOrderingBlockedReason(
        string $componentClass,
        ShopConfig $config,
        ?string $partnerCode = null,
    ): ?string {
        $repository = $this->createMock(ShopConfigRepository::class);
        $repository->method('findOne')->willReturn($config);

        $translator = $this->createMock(TranslatorInterface::class);
        $translator->method('trans')
            ->willReturnCallback(static fn(string $id): string => $id);

        $reflection = new \ReflectionClass($componentClass);
        $component = $reflection->newInstanceWithoutConstructor();
        $reflection->getProperty('shopConfigRepository')->setValue($component, $repository);
        $reflection->getProperty('requestStack')->setValue($component, $this->createRequestStack($partnerCode));
        $reflection->getProperty('translator')->setValue($component, $translator);

        return $reflection->getMethod('getOrderingBlockedReason')->invoke($component);
    }

    private function createRequestStack(?string $partnerCode = null): RequestStack
    {
        $session = new Session(new MockArraySessionStorage());
        if (null !== $partnerCode) {
            $session->set('partnerCode', $partnerCode);
        }

        $request = new Request();
        $request->setSession($session);
        $requestStack = new RequestStack();
        $requestStack->push($request);

        return $requestStack;
    }
}
