<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\Tests\Controller;

require_once __DIR__.'/../../src/Bridge/Symfony/Bundle/Controller/Site/MethodPaymentController.php';

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Controller\Site\MethodPaymentController;
use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Model\CartCheckout;
use CpCreation\VitiCore\Cart\Repository\CartRepository;
use PHPUnit\Framework\MockObject\MockObject;
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;

final class MethodPaymentControllerCancellationTest extends TestCase
{
    public function testUnsuccessfulCartPaymentReleasesCheckoutReservation(): void
    {
        $initiatedAt = new \DateTimeImmutable('2026-07-28 18:00:00');
        [$cart, $checkout] = $this->createInitiatedCart($initiatedAt);
        [$requestStack, $session] = $this->createRequestStack($cart, 'Cart');

        $cartRepository = $this->createMock(CartRepository::class);
        $cartRepository
            ->expects(self::once())
            ->method('find')
            ->with($cart->getId()->toString())
            ->willReturn($cart);
        $cartRepository
            ->expects(self::once())
            ->method('save')
            ->with($cart);

        $this->releaseCheckoutReservation($requestStack, $cartRepository);

        self::assertNull(
            $checkout->getInitiatedAt(),
            'Le panier doit redevenir sélectionnable comme panier en cours.',
        );
        self::assertSame($cart->getId()->toString(), (string) $session->get('cart'));
    }

    public function testStandalonePaymentDoesNotReleaseUnrelatedCartReservation(): void
    {
        $initiatedAt = new \DateTimeImmutable('2026-07-28 18:00:00');
        [$cart, $checkout] = $this->createInitiatedCart($initiatedAt);
        [$requestStack] = $this->createRequestStack($cart, 'Payment');

        $cartRepository = $this->createMock(CartRepository::class);
        $cartRepository->expects(self::never())->method('find');
        $cartRepository->expects(self::never())->method('save');

        $this->releaseCheckoutReservation($requestStack, $cartRepository);

        self::assertSame($initiatedAt, $checkout->getInitiatedAt());
    }

    public function testPaidCheckoutReservationIsNotReleased(): void
    {
        $initiatedAt = new \DateTimeImmutable('2026-07-28 18:00:00');
        [$cart, $checkout] = $this->createInitiatedCart($initiatedAt);
        $checkout->setPayedAt(new \DateTimeImmutable('2026-07-28 18:05:00'));
        [$requestStack] = $this->createRequestStack($cart, 'Cart');

        $cartRepository = $this->createMock(CartRepository::class);
        $cartRepository
            ->expects(self::once())
            ->method('find')
            ->with($cart->getId()->toString())
            ->willReturn($cart);
        $cartRepository->expects(self::never())->method('save');

        $this->releaseCheckoutReservation($requestStack, $cartRepository);

        self::assertSame($initiatedAt, $checkout->getInitiatedAt());
    }

    /**
     * @return array{Cart, CartCheckout}
     */
    private function createInitiatedCart(\DateTimeImmutable $initiatedAt): array
    {
        $cart = new Cart();
        $checkout = new CartCheckout();
        $checkout->setInitiatedAt($initiatedAt);
        $checkout->setCart($cart);
        $cart->setCartCheckout($checkout);

        return [$cart, $checkout];
    }

    /**
     * @return array{RequestStack, Session}
     */
    private function createRequestStack(Cart $cart, string $paymentFlow): array
    {
        $session = new Session(new MockArraySessionStorage());
        $session->set('cart', $cart->getId()->toString());
        $session->set('payment', $paymentFlow);

        $request = Request::create('/paiement/cb/retour/annule');
        $request->setSession($session);

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

        return [$requestStack, $session];
    }

    private function releaseCheckoutReservation(
        RequestStack $requestStack,
        CartRepository&MockObject $cartRepository,
    ): void {
        $reflection = new \ReflectionClass(MethodPaymentController::class);
        $controller = $reflection->newInstanceWithoutConstructor();
        $reflection->getProperty('requestStack')->setValue($controller, $requestStack);
        $reflection->getProperty('cartRepository')->setValue($controller, $cartRepository);

        $reflection->getMethod('releaseCheckoutReservation')->invoke($controller);
    }
}
