<?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\MainBundle\Tests\Notification\Email\NotificationEmailHandler;

use Chill\MainBundle\Entity\Notification;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserGroup;
use Chill\MainBundle\Notification\Email\NotificationEmailHandlers\SendImmediateNotificationEmailHandler;
use Chill\MainBundle\Notification\Email\NotificationEmailMessages\SendImmediateNotificationEmailMessage;
use Chill\MainBundle\Notification\Email\NotificationMailer;
use Chill\MainBundle\Repository\NotificationRepository;
use Chill\MainBundle\Repository\UserGroupRepository;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Log\NullLogger;

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

    private $notificationRepository;
    private $userRepository;
    private $userGroupRepository;
    private $notificationMailer;
    private SendImmediateNotificationEmailHandler $handler;

    protected function setUp(): void
    {
        $this->notificationRepository = $this->prophesize(NotificationRepository::class);
        $this->userRepository = $this->prophesize(UserRepositoryInterface::class);
        $this->userGroupRepository = $this->prophesize(UserGroupRepository::class);
        $this->notificationMailer = $this->prophesize(NotificationMailer::class);

        $this->handler = new SendImmediateNotificationEmailHandler(
            $this->notificationRepository->reveal(),
            $this->userRepository->reveal(),
            $this->userGroupRepository->reveal(),
            $this->notificationMailer->reveal(),
            new NullLogger()
        );
    }

    public function testInvokeWithUserAddressee(): void
    {
        $notificationId = 123;
        $userId = 456;

        $notification = $this->prophesize(Notification::class);
        $notification->getId()->willReturn($notificationId);
        $user = $this->prophesize(User::class);
        $user->getId()->willReturn($userId);

        $message = new SendImmediateNotificationEmailMessage($notification->reveal(), $user->reveal());

        $this->notificationRepository->find($notificationId)->willReturn($notification->reveal());
        $this->userRepository->find($userId)->willReturn($user->reveal());

        $this->notificationMailer->sendEmailToAddressee($notification->reveal(), $user->reveal())
            ->shouldBeCalledOnce();

        ($this->handler)($message);
    }

    public function testInvokeWithUserGroupAddressee(): void
    {
        $notificationId = 123;
        $userGroupId = 789;

        $notification = $this->prophesize(Notification::class);
        $notification->getId()->willReturn($notificationId);
        $userGroup = $this->prophesize(UserGroup::class);
        $userGroup->getId()->willReturn($userGroupId);

        $message = new SendImmediateNotificationEmailMessage($notification->reveal(), $userGroup->reveal());

        $this->notificationRepository->find($notificationId)->willReturn($notification->reveal());
        $this->userGroupRepository->find($userGroupId)->willReturn($userGroup->reveal());

        $this->notificationMailer->sendEmailToAddressee($notification->reveal(), $userGroup->reveal())
            ->shouldBeCalledOnce();

        ($this->handler)($message);
    }

    public function testInvokeThrowsExceptionWhenNotificationNotFound(): void
    {
        $notificationId = 123;
        $userId = 456;

        $notification = $this->prophesize(Notification::class);
        $notification->getId()->willReturn($notificationId);
        $user = $this->prophesize(User::class);
        $user->getId()->willReturn($userId);

        $message = new SendImmediateNotificationEmailMessage($notification->reveal(), $user->reveal());

        $this->notificationRepository->find($notificationId)->willReturn(null);
        $this->userRepository->find($userId)->willReturn($user->reveal());

        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage(sprintf('Notification with ID %s not found', $notificationId));

        ($this->handler)($message);
    }

    public function testInvokeThrowsExceptionWhenUserNotFound(): void
    {
        $notificationId = 123;
        $userId = 456;

        $notification = $this->prophesize(Notification::class);
        $notification->getId()->willReturn($notificationId);
        $user = $this->prophesize(User::class);
        $user->getId()->willReturn($userId);

        $message = new SendImmediateNotificationEmailMessage($notification->reveal(), $user->reveal());

        $this->notificationRepository->find($notificationId)->willReturn($notification->reveal());
        $this->userRepository->find($userId)->willReturn(null);

        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage(sprintf('User with ID %s or user group with id %s not found', $userId, ''));

        ($this->handler)($message);
    }

    public function testInvokeThrowsExceptionWhenUserGroupNotFound(): void
    {
        $notificationId = 123;
        $userGroupId = 789;

        $notification = $this->prophesize(Notification::class);
        $notification->getId()->willReturn($notificationId);
        $userGroup = $this->prophesize(UserGroup::class);
        $userGroup->getId()->willReturn($userGroupId);

        $message = new SendImmediateNotificationEmailMessage($notification->reveal(), $userGroup->reveal());

        $this->notificationRepository->find($notificationId)->willReturn($notification->reveal());
        $this->userGroupRepository->find($userGroupId)->willReturn(null);

        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage(sprintf('User with ID %s or user group with id %s not found', '', $userGroupId));

        ($this->handler)($message);
    }

    public function testInvokeRethrowsExceptionWhenMailerFails(): void
    {
        $notificationId = 123;
        $userId = 456;

        $notification = $this->prophesize(Notification::class);
        $notification->getId()->willReturn($notificationId);
        $user = $this->prophesize(User::class);
        $user->getId()->willReturn($userId);

        $message = new SendImmediateNotificationEmailMessage($notification->reveal(), $user->reveal());

        $this->notificationRepository->find($notificationId)->willReturn($notification->reveal());
        $this->userRepository->find($userId)->willReturn($user->reveal());

        $exception = new \Exception('Mailer error');
        $this->notificationMailer->sendEmailToAddressee($notification->reveal(), $user->reveal())
            ->willThrow($exception);

        $this->expectException(\Exception::class);
        $this->expectExceptionMessage('Mailer error');

        ($this->handler)($message);
    }
}
