<?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\ActivityBundle\Tests\Security\Authorization;

use Chill\ActivityBundle\Test\PrepareActivityTrait;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Test\PrepareCenterTrait;
use Chill\MainBundle\Test\PrepareScopeTrait;
use Chill\MainBundle\Test\PrepareUserTrait;
use Chill\PersonBundle\Test\PreparePersonTrait;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;

/**
 * @internal
 *
 * @coversNothing
 */
final class ActivityVoterTest extends KernelTestCase
{
    use PrepareActivityTrait;

    use PrepareCenterTrait;

    use PreparePersonTrait;

    use PrepareScopeTrait;

    use PrepareUserTrait;

    /**
     * @var \Prophecy\Prophet
     */
    protected $prophet;

    /**
     * @var \Chill\PersonBundle\Security\Authorization\PersonVoter
     */
    protected $voter;

    protected function setUp(): void
    {
        self::bootKernel();
        $this->voter = self::$kernel->getContainer()
            ->get('chill.activity.security.authorization.activity_voter');
        $this->prophet = new \Prophecy\Prophet();
    }

    public function testNullUser()
    {
        $token = $this->prepareToken();
        $center = $this->prepareCenter(1, 'center');
        $person = $this->preparePerson($center);
        $scope = $this->prepareScope(1, 'default');
        $activity = $this->prepareActivity($scope, $person);

        $this->assertEquals(
            VoterInterface::ACCESS_DENIED,
            $this->voter->vote($token, $activity, ['CHILL_ACTIVITY_SEE']),
            'assert that a null user is not allowed to see'
        );
    }

    /**
     * @dataProvider dataProvider_testVoteAction
     *
     * @param type   $expectedResult
     * @param string $attribute
     * @param string $message
     */
    public function testVoteAction(
        $expectedResult,
        User $user,
        Scope $scope,
        Center $center,
        $attribute,
        $message,
    ) {
        $token = $this->prepareToken($user);
        $activity = $this->prepareActivity($scope, $this->preparePerson($center));

        $this->assertEquals(
            $expectedResult,
            $this->voter->vote($token, $activity, [$attribute]),
            $message
        );
    }

    public function dataProvider_testVoteAction()
    {
        $centerA = $this->prepareCenter(1, 'center A');
        $centerB = $this->prepareCenter(2, 'center B');
        $scopeA = $this->prepareScope(1, 'scope default');
        $scopeB = $this->prepareScope(2, 'scope B');
        $scopeC = $this->prepareScope(3, 'scope C');

        $userA = $this->prepareUser([
            [
                'center' => $centerA,
                'permissionsGroup' => [
                    ['scope' => $scopeB, 'role' => 'CHILL_ACTIVITY_CREATE'],
                    ['scope' => $scopeA, 'role' => 'CHILL_ACTIVITY_SEE'],
                ],
            ],
            [
                'center' => $centerB,
                'permissionsGroup' => [
                    ['scope' => $scopeA, 'role' => 'CHILL_ACTIVITY_CREATE'],
                    ['scope' => $scopeC, 'role' => 'CHILL_ACTIVITY_CREATE'],
                ],
            ],
        ]);

        return [
            [
                VoterInterface::ACCESS_GRANTED,
                $userA,
                $scopeB,
                $centerA,
                'CHILL_ACTIVITY_CREATE',
                'assert that a user granted with same rights',
            ],
            [
                VoterInterface::ACCESS_GRANTED,
                $userA,
                $scopeB,
                $centerA,
                'CHILL_ACTIVITY_SEE',
                'assert that a user granted with inheritance rights',
            ],
            [
                VoterInterface::ACCESS_DENIED,
                $userA,
                $scopeC,
                $centerA,
                'CHILL_ACTIVITY_SEE',
                'assert that a suer is denied if he is not granted right on this center',
            ],
        ];
    }

    /**
     * prepare a token interface with correct rights.
     *
     * if $permissions = null, user will be null (no user associated with token
     *
     * @return \Symfony\Component\Security\Core\Authentication\Token\TokenInterface
     */
    protected function prepareToken(?User $user = null)
    {
        $token = $this->prophet->prophesize();
        $token
            ->willImplement('\\'.\Symfony\Component\Security\Core\Authentication\Token\TokenInterface::class);

        if (null === $user) {
            $token->getUser()->willReturn(null);
        } else {
            $token->getUser()->willReturn($user);
        }

        return $token->reveal();
    }
}
