<?php

declare(strict_types=1);

namespace App\Tests\Unit\Module\Core\Infrastructure\Security;

use App\Module\Core\Domain\Entity\Permission;
use App\Module\Core\Domain\Entity\Role;
use App\Module\Core\Domain\Entity\User;
use App\Module\Core\Infrastructure\Security\PermissionVoter;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;

/**
 * @internal
 */
final class PermissionVoterTest extends TestCase
{
    public function testAbstainsOnNonRbacAttributes(): void
    {
        $voter = new PermissionVoter();
        $user  = new User();
        self::assertSame(VoterInterface::ACCESS_ABSTAIN, $voter->vote($this->token($user), null, ['ROLE_ADMIN']));
        self::assertSame(VoterInterface::ACCESS_ABSTAIN, $voter->vote($this->token($user), null, ['IS_AUTHENTICATED_FULLY']));
    }

    public function testGrantsWhenUserHasPermissionViaRole(): void
    {
        $voter = new PermissionVoter();
        $role  = new Role('bureau', 'Bureau');
        $role->addPermission(new Permission('core.users.view', 'Voir', 'core'));
        $user = new User();
        $user->addRbacRole($role);

        self::assertSame(VoterInterface::ACCESS_GRANTED, $voter->vote($this->token($user), null, ['core.users.view']));
        self::assertSame(VoterInterface::ACCESS_DENIED, $voter->vote($this->token($user), null, ['core.users.manage']));
    }

    public function testAdminBypassesViaRole(): void
    {
        $voter = new PermissionVoter();
        $user  = new User();
        $user->setRoles(['ROLE_ADMIN']);

        self::assertSame(VoterInterface::ACCESS_GRANTED, $voter->vote($this->token($user), null, ['core.users.manage']));
    }

    private function token(User $user): UsernamePasswordToken
    {
        return new UsernamePasswordToken($user, 'main', $user->getRoles());
    }
}
