<?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\Action\Ticket\Handler;

use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserGroup;
use Chill\TicketBundle\Action\Ticket\Handler\SetAddresseesCommandHandler;
use Chill\TicketBundle\Action\Ticket\SetAddresseesCommand;
use Chill\TicketBundle\Entity\AddresseeHistory;
use Chill\TicketBundle\Entity\Ticket;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\Clock\MockClock;
use Symfony\Component\Security\Core\Security;

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

    public function testHandleOnEmptyAddresses(): void
    {
        $ticket = new Ticket();
        $command = new SetAddresseesCommand([$user1 = new User(), $group1 = new UserGroup()]);

        $entityManager = $this->prophesize(EntityManagerInterface::class);
        $entityManager->persist(Argument::that(fn ($arg) => $arg instanceof AddresseeHistory && $arg->getAddressee() === $user1))->shouldBeCalledOnce();
        $entityManager->persist(Argument::that(fn ($arg) => $arg instanceof AddresseeHistory && $arg->getAddressee() === $group1))->shouldBeCalledOnce();

        $handler = $this->buildHandler($entityManager->reveal());

        $handler->handle($ticket, $command);

        self::assertCount(2, $ticket->getCurrentAddressee());
    }

    public function testHandleExistingUserIsNotRemovedNorCreatingDouble(): void
    {
        $ticket = new Ticket();
        $user = new User();
        $history = new AddresseeHistory($user, new \DateTimeImmutable('1 month ago'), $ticket);
        $command = new SetAddresseesCommand([$user]);

        $entityManager = $this->prophesize(EntityManagerInterface::class);
        $entityManager->persist(Argument::that(fn ($arg) => $arg instanceof AddresseeHistory && $arg->getAddressee() === $user))->shouldNotBeCalled();

        $handler = $this->buildHandler($entityManager->reveal());

        $handler->handle($ticket, $command);

        self::assertNull($history->getEndDate());
        self::assertCount(1, $ticket->getCurrentAddressee());
    }

    public function testHandleRemoveExistingAddressee(): void
    {
        $ticket = new Ticket();
        $user = new User();
        $group = new UserGroup();
        $history = new AddresseeHistory($user, new \DateTimeImmutable('1 month ago'), $ticket);
        $command = new SetAddresseesCommand([$group]);

        $entityManager = $this->prophesize(EntityManagerInterface::class);
        $entityManager->persist(Argument::that(fn ($arg) => $arg instanceof AddresseeHistory && $arg->getAddressee() === $group))->shouldBeCalled();

        $handler = $this->buildHandler($entityManager->reveal());

        $handler->handle($ticket, $command);

        self::assertNotNull($history->getEndDate());
        self::assertContains($group, $ticket->getCurrentAddressee());
    }

    public function testAddingDoublingAddresseeDoesNotCreateDoubleHistories(): void
    {
        $ticket = new Ticket();
        $group = new UserGroup();
        $command = new SetAddresseesCommand([$group, $group]);

        $entityManager = $this->prophesize(EntityManagerInterface::class);
        $entityManager->persist(Argument::that(fn ($arg) => $arg instanceof AddresseeHistory && $arg->getAddressee() === $group))->shouldBeCalledOnce();

        $handler = $this->buildHandler($entityManager->reveal());

        $handler->handle($ticket, $command);

        self::assertCount(1, $ticket->getCurrentAddressee());
    }

    private function buildHandler(EntityManagerInterface $entityManager): SetAddresseesCommandHandler
    {
        $security = $this->prophesize(Security::class);
        $security->getUser()->willReturn(new User());

        return new SetAddresseesCommandHandler(new MockClock(), $entityManager, $security->reveal());
    }
}
