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

use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
use Chill\MainBundle\Security\Authorization\EntityWorkflowTransitionVoter;
use Chill\MainBundle\Security\Authorization\EntityWorkflowVoter;
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use Chill\MainBundle\Workflow\EntityWorkflowManager;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;

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

    public function testVoteOnAttributeHappyScenario(): void
    {
        $entityWorkflow = new EntityWorkflow();
        $object = new \stdClass();
        $center = new Center();
        $user = new User();

        $handler = $this->prophesize(EntityWorkflowHandlerInterface::class);
        $handler->getRelatedEntity($entityWorkflow)->willReturn($object);

        $entityWorkflowManager = $this->prophesize(EntityWorkflowManager::class);
        $entityWorkflowManager->getHandler($entityWorkflow)->willReturn($handler);

        $centerResolver = $this->prophesize(CenterResolverManagerInterface::class);
        $centerResolver->resolveCenters($object)->willReturn([$center, new Center()]);

        $autorizationHelper = $this->prophesize(AuthorizationHelperForCurrentUserInterface::class);
        $autorizationHelper->getReachableCenters('CHILL_MAIN_WORKFLOW_APPLY_ALL_TRANSITION')
            ->willReturn([$center, new Center()]);

        $token = new UsernamePasswordToken($user, 'default', $user->getRoles());

        $accessDecision = $this->prophesize(AccessDecisionManagerInterface::class);
        $accessDecision->decide($token, [EntityWorkflowVoter::SEE], $entityWorkflow)
            ->willReturn(true)->shouldBeCalled();

        $voter = new EntityWorkflowTransitionVoter(
            $entityWorkflowManager->reveal(),
            $autorizationHelper->reveal(),
            $centerResolver->reveal(),
            $accessDecision->reveal(),
        );

        self::assertEquals(Voter::ACCESS_GRANTED, $voter->vote($token, $entityWorkflow->getCurrentStep(), ['CHILL_MAIN_WORKFLOW_APPLY_ALL_TRANSITION']));
    }

    public function testVoteOnAttributeCenterNotReachable(): void
    {
        $entityWorkflow = new EntityWorkflow();
        $object = new \stdClass();
        $user = new User();

        $handler = $this->prophesize(EntityWorkflowHandlerInterface::class);
        $handler->getRelatedEntity($entityWorkflow)->willReturn($object);

        $entityWorkflowManager = $this->prophesize(EntityWorkflowManager::class);
        $entityWorkflowManager->getHandler($entityWorkflow)->willReturn($handler);

        $centerResolver = $this->prophesize(CenterResolverManagerInterface::class);
        $centerResolver->resolveCenters($object)->willReturn([new Center()]);

        $autorizationHelper = $this->prophesize(AuthorizationHelperForCurrentUserInterface::class);
        $autorizationHelper->getReachableCenters('CHILL_MAIN_WORKFLOW_APPLY_ALL_TRANSITION')
            ->willReturn([new Center()]);

        $token = new UsernamePasswordToken($user, 'default', $user->getRoles());

        $accessDecision = $this->prophesize(AccessDecisionManagerInterface::class);
        $accessDecision->decide($token, [EntityWorkflowVoter::SEE], $entityWorkflow)
            ->willReturn(true)->shouldBeCalled();

        $voter = new EntityWorkflowTransitionVoter(
            $entityWorkflowManager->reveal(),
            $autorizationHelper->reveal(),
            $centerResolver->reveal(),
            $accessDecision->reveal(),
        );

        self::assertEquals(Voter::ACCESS_DENIED, $voter->vote($token, $entityWorkflow->getCurrentStep(), ['CHILL_MAIN_WORKFLOW_APPLY_ALL_TRANSITION']));
    }

    public function testVoteNotOnSupportedAttribute(): void
    {
        $entityWorkflow = new EntityWorkflow();
        $object = new \stdClass();
        $user = new User();

        $handler = $this->prophesize(EntityWorkflowHandlerInterface::class);
        $handler->getRelatedEntity($entityWorkflow)->willReturn($object);

        $entityWorkflowManager = $this->prophesize(EntityWorkflowManager::class);
        $entityWorkflowManager->getHandler($entityWorkflow)->willReturn($handler);

        $centerResolver = $this->prophesize(CenterResolverManagerInterface::class);
        $centerResolver->resolveCenters($object)->willReturn([new Center()]);

        $autorizationHelper = $this->prophesize(AuthorizationHelperForCurrentUserInterface::class);
        $autorizationHelper->getReachableCenters('CHILL_MAIN_WORKFLOW_APPLY_ALL_TRANSITION')
            ->willReturn([new Center()]);

        $token = new UsernamePasswordToken($user, 'default', $user->getRoles());

        $accessDecision = $this->prophesize(AccessDecisionManagerInterface::class);
        $accessDecision->decide($token, [EntityWorkflowVoter::SEE], $entityWorkflow)
            ->willReturn(true);

        $voter = new EntityWorkflowTransitionVoter(
            $entityWorkflowManager->reveal(),
            $autorizationHelper->reveal(),
            $centerResolver->reveal(),
            $accessDecision->reveal(),
        );

        self::assertEquals(Voter::ACCESS_ABSTAIN, $voter->vote($token, new \stdClass(), ['CHILL_MAIN_WORKFLOW_APPLY_ALL_TRANSITION']));
        self::assertEquals(Voter::ACCESS_ABSTAIN, $voter->vote($token, $entityWorkflow->getCurrentStep(), ['SOMETHING_ELSE']));
    }
}
