<?php

namespace NoahVet\Reef\Test\A_Unit\Security\Voter;

use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken;
use NoahVet\Reef\Domain\Security\Model\Resource;
use NoahVet\Reef\Domain\Security\Model\ResourceType;
use NoahVet\Reef\Domain\Security\Port\PermissionCheckerInterface;
use NoahVet\Reef\Infrastructure\Security\IAM\Mapper\IAMResourceMapperInterface;
use NoahVet\Reef\Infrastructure\Security\Symfony\Voter\IAMVoter;
use NoahVet\Reef\Security\Authentication\Token\ReefOAuthToken;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\User\UserInterface;

class IAMVoterTest extends TestCase
{
    public function testVoteReefToken(): void
    {
        $checker = $this->createMock(PermissionCheckerInterface::class);
        $mapper = $this->createMock(IAMResourceMapperInterface::class);

        $checker->method('check')
            ->willReturnCallback(
                static fn ($subject, array $attributes): bool => \in_array('reef:bsmApi:veterinaryEstablishment:perm:readPublic', $attributes, true),
            )
        ;

        $voter = new IAMVoter($checker, $mapper);

        $token = new ReefOAuthToken(
            'main',
            'NDAyMzc2YTQ0ODljYzhjNjJlYTc0ZjFhZjlhMmQ2NzkxYTJiNDgxMDQ3OWZkMjJlZDY5Yjc5NmM5NTZlMTcxOA',
            null,
            new \DateTimeImmutable('+1 hour'),
        );

        $resource = Resource::fromString(
            'reef:bsmApi:veterinaryEstablishment',
            'toto',
        );

        self::assertEquals(
            VoterInterface::ACCESS_GRANTED,
            $voter->vote($token, $resource, ['reef:bsmApi:veterinaryEstablishment:perm:readPublic']),
        );

        self::assertEquals(
            VoterInterface::ACCESS_DENIED,
            $voter->vote($token, $resource, ['reef:bsmApi:veterinaryEstablishment:perm:write']),
        );
    }

    public function testVoteHWIOauthToken(): void
    {
        $checker = $this->createMock(PermissionCheckerInterface::class);
        $mapper = $this->createMock(IAMResourceMapperInterface::class);

        $checker->method('check')
            ->willReturnCallback(
                static fn ($subject, array $attributes): bool => \in_array('reef:bsmApi:veterinaryEstablishment:perm:readPublic', $attributes, true),
            )
        ;

        $voter = new IAMVoter($checker, $mapper);

        $token = new OAuthToken(
            'NDAyMzc2YTQ0ODljYzhjNjJlYTc0ZjFhZjlhMmQ2NzkxYTJiNDgxMDQ3OWZkMjJlZDY5Yjc5NmM5NTZlMTcxOA',
            ['ROLE_USER'],
        );

        $resource = Resource::fromString(
            'reef:bsmApi:veterinaryEstablishment',
            'toto',
        );

        self::assertEquals(
            VoterInterface::ACCESS_GRANTED,
            $voter->vote($token, $resource, ['reef:bsmApi:veterinaryEstablishment:perm:readPublic']),
        );

        self::assertEquals(
            VoterInterface::ACCESS_DENIED,
            $voter->vote($token, $resource, ['reef:bsmApi:veterinaryEstablishment:perm:write']),
        );
    }

    public function testVoteNonOAuthToken(): void
    {
        $checker = $this->createMock(PermissionCheckerInterface::class);
        $mapper = $this->createMock(IAMResourceMapperInterface::class);

        $voter = new IAMVoter($checker, $mapper);

        $user = $this->getMockBuilder(UserInterface::class)
            ->disableOriginalConstructor()
            ->getMock()
        ;

        $token = new UsernamePasswordToken(
            $user,
            'main',
            ['ROLE_USER'],
        );

        $resource = Resource::fromString(
            'reef:bsmApi:veterinaryEstablishment',
            'toto',
        );

        self::assertEquals(
            VoterInterface::ACCESS_ABSTAIN,
            $voter->vote($token, $resource, ['reef:bsmApi:veterinaryEstablishment:perm:readPublic']),
        );
    }

    public function testVoteSuperAdminDenied(): void
    {
        $checker = $this->createMock(PermissionCheckerInterface::class);
        $mapper = $this->createMock(IAMResourceMapperInterface::class);

        $checker->method('checkGroup')->willReturn(false);

        $voter = new IAMVoter($checker, $mapper);

        $token = new OAuthToken(
            'NDAyMzc2YTQ0ODljYzhjNjJlYTc0ZjFhZjlhMmQ2NzkxYTJiNDgxMDQ3OWZkMjJlZDY5Yjc5NmM5NTZlMTcxOA',
            ['ROLE_USER'],
        );

        self::assertEquals(
            VoterInterface::ACCESS_DENIED,
            $voter->vote($token, null, ['reef:iam:principals:superAdmins']),
        );
    }

    public function testVoteSuperAdminGranted(): void
    {
        $checker = $this->createMock(PermissionCheckerInterface::class);
        $mapper = $this->createMock(IAMResourceMapperInterface::class);

        $checker->method('checkGroup')->willReturn(true);

        $voter = new IAMVoter($checker, $mapper);

        $token = new OAuthToken(
            'NDAyMzc2YTQ0ODljYzhjNjJlYTc0ZjFhZjlhMmQ2NzkxYTJiNDgxMDQ3OWZkMjJlZDY5Yjc5NmM5NTZlMTcxOA',
            ['ROLE_USER'],
        );

        self::assertEquals(
            VoterInterface::ACCESS_GRANTED,
            $voter->vote($token, null, ['reef:iam:principals:superAdmins']),
        );
    }

    public function testVoteResourceType(): void
    {
        $checker = $this->createMock(PermissionCheckerInterface::class);
        $mapper = $this->createMock(IAMResourceMapperInterface::class);

        $checker->method('check')
            ->willReturnCallback(
                static fn ($subject, array $attributes): bool => \in_array('reef:iam:principal:perm:read', $attributes, true)
                    || \in_array('reef:iam:principal:perm:write', $attributes, true),
            )
        ;

        $voter = new IAMVoter($checker, $mapper);

        $token = new OAuthToken(
            'NDAyMzc2YTQ0ODljYzhjNjJlYTc0ZjFhZjlhMmQ2NzkxYTJiNDgxMDQ3OWZkMjJlZDY5Yjc5NmM5NTZlMTcxOA',
            ['ROLE_USER'],
        );

        self::assertEquals(
            VoterInterface::ACCESS_GRANTED,
            $voter->vote($token, ResourceType::fromString('reef:iam:principal'), ['reef:iam:principal:perm:read']),
        );

        self::assertEquals(
            VoterInterface::ACCESS_GRANTED,
            $voter->vote($token, ResourceType::fromString('reef:iam:principal'), ['reef:iam:principal:perm:write']),
        );

        self::assertEquals(
            VoterInterface::ACCESS_DENIED,
            $voter->vote($token, ResourceType::fromString('reef:iam:principal'), ['reef:iam:principal:perm:delete']),
        );
    }
}
