<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\TicketBundle\Tests\Controller;

use Chill\TicketBundle\Action\Ticket\ChangeStateCommand;
use Chill\TicketBundle\Action\Ticket\Handler\ChangeStateCommandHandler;
use Chill\TicketBundle\Controller\ChangeStateApiController;
use Chill\TicketBundle\Entity\StateEnum;
use Chill\TicketBundle\Entity\Ticket;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Serializer\SerializerInterface;

/**
 * @internal
 *
 * @coversNothing
 */
final class ChangeStateApiControllerTest extends TestCase
{
    use ProphecyTrait;

    public function testCloseWithoutPermission(): void
    {
        $ticket = new Ticket();
        $security = $this->prophesize(Security::class);
        $security->isGranted('ROLE_USER')->willReturn(false);

        $changeStateCommandHandler = $this->prophesize(ChangeStateCommandHandler::class);
        $entityManager = $this->prophesize(EntityManagerInterface::class);
        $serializer = $this->prophesize(SerializerInterface::class);

        $controller = new ChangeStateApiController(
            $changeStateCommandHandler->reveal(),
            $security->reveal(),
            $entityManager->reveal(),
            $serializer->reveal(),
        );

        $this->expectException(AccessDeniedHttpException::class);
        $controller->close($ticket);
    }

    public function testCloseWithPermission(): void
    {
        $ticket = new Ticket();

        $security = $this->prophesize(Security::class);
        $security->isGranted('ROLE_USER')->willReturn(true);

        $changeStateCommandHandler = $this->prophesize(ChangeStateCommandHandler::class);
        $changeStateCommandHandler->__invoke(
            $ticket,
            Argument::that(fn (ChangeStateCommand $command) => StateEnum::CLOSED === $command->newState)
        )->willReturn($ticket);

        $entityManager = $this->prophesize(EntityManagerInterface::class);
        $entityManager->flush()->shouldBeCalled();

        $serializer = $this->prophesize(SerializerInterface::class);
        $serializer->serialize($ticket, 'json', ['groups' => ['read']])
            ->willReturn('{}')
            ->shouldBeCalled();

        $controller = new ChangeStateApiController(
            $changeStateCommandHandler->reveal(),
            $security->reveal(),
            $entityManager->reveal(),
            $serializer->reveal(),
        );

        $response = $controller->close($ticket);

        $this->assertInstanceOf(JsonResponse::class, $response);
    }

    public function testOpenWithoutPermission(): void
    {
        $ticket = new Ticket();
        $security = $this->prophesize(Security::class);
        $security->isGranted('ROLE_USER')->willReturn(false);

        $changeStateCommandHandler = $this->prophesize(ChangeStateCommandHandler::class);
        $entityManager = $this->prophesize(EntityManagerInterface::class);
        $serializer = $this->prophesize(SerializerInterface::class);

        $controller = new ChangeStateApiController(
            $changeStateCommandHandler->reveal(),
            $security->reveal(),
            $entityManager->reveal(),
            $serializer->reveal(),
        );

        $this->expectException(AccessDeniedHttpException::class);
        $controller->open($ticket);
    }

    public function testOpenWithPermission(): void
    {
        $ticket = new Ticket();

        $security = $this->prophesize(Security::class);
        $security->isGranted('ROLE_USER')->willReturn(true);

        $changeStateCommandHandler = $this->prophesize(ChangeStateCommandHandler::class);
        $changeStateCommandHandler->__invoke(
            $ticket,
            Argument::that(fn (ChangeStateCommand $command) => StateEnum::OPEN === $command->newState)
        )->willReturn($ticket);

        $entityManager = $this->prophesize(EntityManagerInterface::class);
        $entityManager->flush()->shouldBeCalled();

        $serializer = $this->prophesize(SerializerInterface::class);
        $serializer->serialize($ticket, 'json', ['groups' => ['read']])
            ->willReturn('{}')
            ->shouldBeCalled();

        $controller = new ChangeStateApiController(
            $changeStateCommandHandler->reveal(),
            $security->reveal(),
            $entityManager->reveal(),
            $serializer->reveal(),
        );

        $response = $controller->open($ticket);

        $this->assertInstanceOf(JsonResponse::class, $response);
    }
}
