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

use Chill\DocStoreBundle\GenericDoc\FetchQueryToSqlBuilder;
use Chill\DocStoreBundle\Repository\PersonDocumentACLAwareRepository;
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
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
 */
class PersonDocumentACLAwareRepositoryTest extends KernelTestCase
{
    use ProphecyTrait;

    private EntityManagerInterface $entityManager;

    private ScopeRepositoryInterface $scopeRepository;

    protected function setUp(): void
    {
        self::bootKernel();
        $this->entityManager = self::getContainer()->get(EntityManagerInterface::class);
        $this->scopeRepository = self::getContainer()->get(ScopeRepositoryInterface::class);
    }

    /**
     * @dataProvider provideDataBuildFetchQueryForPerson
     *
     * @throws \Doctrine\DBAL\Exception
     * @throws \Doctrine\ORM\NoResultException
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function testBuildFetchQueryForPerson(?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null): void
    {
        $centerManager = $this->prophesize(CenterResolverManagerInterface::class);
        $centerManager->resolveCenters(Argument::type(Person::class))
            ->willReturn([new Center()]);

        $scopes = $this->scopeRepository->findAll();
        $authorizationHelper = $this->prophesize(AuthorizationHelperForCurrentUserInterface::class);
        $authorizationHelper->getReachableScopes(PersonDocumentVoter::SEE, Argument::any())->willReturn($scopes);

        $repository = new PersonDocumentACLAwareRepository(
            $this->entityManager,
            $centerManager->reveal(),
            $authorizationHelper->reveal(),
            $this->prophesize(Security::class)->reveal()
        );

        $person = $this->entityManager->createQuery('SELECT p FROM '.Person::class.' p')
            ->setMaxResults(1)
            ->getSingleResult();

        if (null === $person) {
            throw new \RuntimeException('person not exists in database');
        }

        $query = $repository->buildFetchQueryForPerson($person, $startDate, $endDate, $content);
        ['sql' => $sql, 'params' => $params, 'types' => $types] = (new FetchQueryToSqlBuilder())->toSql($query);

        $nb = $this->entityManager->getConnection()
            ->fetchOne("SELECT COUNT(*) FROM ({$sql}) AS sq", $params, $types);

        self::assertIsInt($nb, 'test that the query could be executed');
    }

    public static function provideDataBuildFetchQueryForPerson(): iterable
    {
        yield [null, null, null];
        yield [new \DateTimeImmutable('1 year ago'), null, null];
        yield [null, new \DateTimeImmutable('1 year ago'), null];
        yield [new \DateTimeImmutable('2 years ago'), new \DateTimeImmutable('1 year ago'), null];
        yield [null, null, 'test'];
        yield [new \DateTimeImmutable('2 years ago'), new \DateTimeImmutable('1 year ago'), 'test'];
    }

    /**
     * @dataProvider provideDateForFetchQueryForAccompanyingPeriod
     */
    public function testBuildFetchQueryForAccompanyingPeriod(
        AccompanyingPeriod $period,
        ?\DateTimeImmutable $startDate = null,
        ?\DateTimeImmutable $endDate = null,
        ?string $content = null,
    ): void {
        $centerManager = $this->prophesize(CenterResolverManagerInterface::class);
        $centerManager->resolveCenters(Argument::type(Person::class))
            ->willReturn([new Center()]);

        $scopes = $this->scopeRepository->findAll();
        $authorizationHelper = $this->prophesize(AuthorizationHelperForCurrentUserInterface::class);
        $authorizationHelper->getReachableScopes(PersonDocumentVoter::SEE, Argument::any())->willReturn($scopes);

        $security = $this->prophesize(Security::class);
        $security->isGranted(PersonDocumentVoter::SEE, Argument::type(Person::class))->willReturn(true);

        $repository = new PersonDocumentACLAwareRepository(
            $this->entityManager,
            $centerManager->reveal(),
            $authorizationHelper->reveal(),
            $security->reveal()
        );

        $query = $repository->buildFetchQueryForAccompanyingPeriod($period, $startDate, $endDate, $content);
        ['sql' => $sql, 'params' => $params, 'types' => $types] = (new FetchQueryToSqlBuilder())->toSql($query);

        $nb = $this->entityManager->getConnection()
            ->fetchOne("SELECT COUNT(*) FROM ({$sql}) AS sq", $params, $types);

        self::assertIsInt($nb, 'test that the query could be executed');
    }

    public static function provideDateForFetchQueryForAccompanyingPeriod(): iterable
    {
        self::bootKernel();
        $entityManager = self::getContainer()->get(EntityManagerInterface::class);

        if (null === $period = $entityManager->createQuery(
            'SELECT p FROM '.AccompanyingPeriod::class.' p WHERE SIZE(p.participations) > 0'
        )
            ->setMaxResults(1)->getSingleResult()) {
            throw new \RuntimeException('no course found');
        }

        yield [$period, null, null, null];
        yield [$period, new \DateTimeImmutable('1 year ago'), null, null];
        yield [$period, null, new \DateTimeImmutable('1 year ago'), null];
        yield [$period, new \DateTimeImmutable('2 years ago'), new \DateTimeImmutable('1 year ago'), null];
        yield [$period, null, null, 'test'];
        yield [$period, new \DateTimeImmutable('2 years ago'), new \DateTimeImmutable('1 year ago'), 'test'];
    }
}
