<?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\TaskBundle\Tests\Repository;

use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\CenterRepositoryInterface;
use Chill\MainBundle\Repository\ScopeRepository;
use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
use Chill\PersonBundle\DataFixtures\Helper\PersonRandomHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\TaskBundle\Repository\SingleTaskAclAwareRepository;
use Chill\TaskBundle\Security\Authorization\TaskVoter;
use Doctrine\ORM\EntityManagerInterface;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Security\Core\Security;

/**
 * @internal
 *
 * @coversNothing
 */
final class SingleTaskACLAwareRepositoryTest extends KernelTestCase
{
    use PersonRandomHelper;

    use ProphecyTrait;

    private readonly AuthorizationHelperInterface $authorizationHelper;

    private CenterRepositoryInterface $centerRepository;

    private EntityManagerInterface $em;

    private PersonRepository $personRepository;

    private ScopeRepository $scopeRepository;

    private UserRepository $userRepository;

    protected function setUp(): void
    {
        self::bootKernel();

        $this->em = self::getContainer()->get(EntityManagerInterface::class);
        $this->userRepository = self::getContainer()->get(UserRepository::class);
        $this->centerRepository = self::getContainer()->get(CenterRepositoryInterface::class);
        $this->scopeRepository = self::getContainer()->get(ScopeRepository::class);
        $this->personRepository = self::getContainer()->get(PersonRepository::class);
    }

    public function testCountByAllViewable(): void
    {
        $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']);
        $user = new User();
        $scopes = $this->scopeRepository->findAll();

        $security = $this->prophesize(Security::class);
        $security->getUser()->willReturn($user);

        $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class);
        $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any())
            ->willReturn([$centerA]);

        $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class);
        $authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(TaskVoter::SHOW))
            ->willReturn([$centerA]);
        $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA))
            ->willReturn($scopes);

        $repository = new SingleTaskAclAwareRepository(
            $centerResolverDispatcher->reveal(),
            $this->em,
            $security->reveal(),
            $authorizationHelper->reveal()
        );

        $nb = $repository->countByAllViewable(null, []);

        $this->assertGreaterThanOrEqual(0, $nb);
    }

    public function testCountByPerson(): void
    {
        $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']);
        $user = new User();
        $scopes = $this->scopeRepository->findAll();
        $person = $this->getRandomPerson($this->em);

        $security = $this->prophesize(Security::class);
        $security->getUser()->willReturn($user);

        $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class);
        $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any())
            ->willReturn([$centerA]);

        $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class);
        $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA))
            ->willReturn($scopes);

        $repository = new SingleTaskAclAwareRepository(
            $centerResolverDispatcher->reveal(),
            $this->em,
            $security->reveal(),
            $authorizationHelper->reveal()
        );

        $nb = $repository->countByPerson($person, null, []);

        $this->assertGreaterThanOrEqual(0, $nb);
    }

    public function testFindByAllViewable(): void
    {
        $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']);
        $user = new User();
        $scopes = $this->scopeRepository->findAll();

        $security = $this->prophesize(Security::class);
        $security->getUser()->willReturn($user);

        $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class);
        $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any())
            ->willReturn([$centerA]);

        $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class);
        $authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(TaskVoter::SHOW))
            ->willReturn([$centerA]);
        $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA))
            ->willReturn($scopes);

        $repository = new SingleTaskAclAwareRepository(
            $centerResolverDispatcher->reveal(),
            $this->em,
            $security->reveal(),
            $authorizationHelper->reveal()
        );

        $tasks = $repository->findByAllViewable(null, []);

        $this->assertGreaterThanOrEqual(0, \count($tasks));
    }

    public function testFindByCourse(): void
    {
        $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']);
        $user = new User();
        $scopes = $this->scopeRepository->findAll();
        /** @var Person $person */
        $person = $this->em->createQuery(
            'SELECT p FROM '.Person::class.' p JOIN p.centerCurrent cc
            WHERE SIZE(p.accompanyingPeriodParticipations) > 0
            AND cc.center = :center'
        )
            ->setParameter('center', $centerA)
            ->setMaxResults(1)
            ->getSingleResult();
        $period = $person->getAccompanyingPeriodParticipations()->first()->getAccompanyingPeriod();

        $security = $this->prophesize(Security::class);
        $security->getUser()->willReturn($user);

        $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class);
        $centerResolverDispatcher->resolveCenters(Argument::type(AccompanyingPeriod::class), Argument::any())
            ->willReturn([$centerA]);

        $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class);
        $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::any())
            ->willReturn($scopes);

        $repository = new SingleTaskAclAwareRepository(
            $centerResolverDispatcher->reveal(),
            $this->em,
            $security->reveal(),
            $authorizationHelper->reveal()
        );

        $tasks = $repository->findByCourse($period);

        $this->assertGreaterThanOrEqual(0, \count($tasks));
    }

    public function testFindByPerson(): void
    {
        $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']);
        $user = new User();
        $scopes = $this->scopeRepository->findAll();
        $person = $this->getRandomPerson($this->em);

        $security = $this->prophesize(Security::class);
        $security->getUser()->willReturn($user);

        $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class);
        $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any())
            ->willReturn([$centerA]);

        $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class);
        $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA))
            ->willReturn($scopes);

        $repository = new SingleTaskAclAwareRepository(
            $centerResolverDispatcher->reveal(),
            $this->em,
            $security->reveal(),
            $authorizationHelper->reveal()
        );

        $tasks = $repository->findByPerson($person, null, []);

        $this->assertGreaterThanOrEqual(0, \count($tasks));
    }
}
