<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Infrastructure\Security;

use App\Administration\Infrastructure\Security\UserVoter;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;

final class UserVoterTest extends TestCase
{
    private UserVoter $voter;

    protected function setUp(): void
    {
        $this->voter = new UserVoter();
    }

    #[Test]
    public function itAbstainsForUnrelatedAttributes(): void
    {
        $user = $this->createMock(UserInterface::class);
        $user->method('getRoles')->willReturn(['ROLE_ADMIN']);

        $token = $this->createMock(TokenInterface::class);
        $token->method('getUser')->willReturn($user);

        $result = $this->voter->vote($token, null, ['SOME_OTHER_ATTRIBUTE']);

        self::assertSame(UserVoter::ACCESS_ABSTAIN, $result);
    }

    #[Test]
    public function itDeniesAccessToUnauthenticatedUsers(): void
    {
        $token = $this->createMock(TokenInterface::class);
        $token->method('getUser')->willReturn(null);

        $result = $this->voter->vote($token, null, [UserVoter::VIEW]);

        self::assertSame(UserVoter::ACCESS_DENIED, $result);
    }

    #[Test]
    public function itGrantsViewToSuperAdmin(): void
    {
        $result = $this->voteWithRole('ROLE_SUPER_ADMIN', UserVoter::VIEW);
        self::assertSame(UserVoter::ACCESS_GRANTED, $result);
    }

    #[Test]
    public function itGrantsViewToAdmin(): void
    {
        $result = $this->voteWithRole('ROLE_ADMIN', UserVoter::VIEW);
        self::assertSame(UserVoter::ACCESS_GRANTED, $result);
    }

    #[Test]
    public function itGrantsViewToSecretariat(): void
    {
        $result = $this->voteWithRole('ROLE_SECRETARIAT', UserVoter::VIEW);
        self::assertSame(UserVoter::ACCESS_GRANTED, $result);
    }

    #[Test]
    public function itDeniesViewToProf(): void
    {
        $result = $this->voteWithRole('ROLE_PROF', UserVoter::VIEW);
        self::assertSame(UserVoter::ACCESS_DENIED, $result);
    }

    #[Test]
    public function itDeniesViewToParent(): void
    {
        $result = $this->voteWithRole('ROLE_PARENT', UserVoter::VIEW);
        self::assertSame(UserVoter::ACCESS_DENIED, $result);
    }

    #[Test]
    public function itGrantsCreateToAdmin(): void
    {
        $result = $this->voteWithRole('ROLE_ADMIN', UserVoter::CREATE);
        self::assertSame(UserVoter::ACCESS_GRANTED, $result);
    }

    #[Test]
    public function itDeniesCreateToSecretariat(): void
    {
        $result = $this->voteWithRole('ROLE_SECRETARIAT', UserVoter::CREATE);
        self::assertSame(UserVoter::ACCESS_DENIED, $result);
    }

    #[Test]
    public function itGrantsBlockToAdmin(): void
    {
        $result = $this->voteWithRole('ROLE_ADMIN', UserVoter::BLOCK);
        self::assertSame(UserVoter::ACCESS_GRANTED, $result);
    }

    #[Test]
    public function itDeniesBlockToProf(): void
    {
        $result = $this->voteWithRole('ROLE_PROF', UserVoter::BLOCK);
        self::assertSame(UserVoter::ACCESS_DENIED, $result);
    }

    #[Test]
    public function itGrantsUnblockToAdmin(): void
    {
        $result = $this->voteWithRole('ROLE_ADMIN', UserVoter::UNBLOCK);
        self::assertSame(UserVoter::ACCESS_GRANTED, $result);
    }

    #[Test]
    public function itDeniesUnblockToProf(): void
    {
        $result = $this->voteWithRole('ROLE_PROF', UserVoter::UNBLOCK);
        self::assertSame(UserVoter::ACCESS_DENIED, $result);
    }

    #[Test]
    public function itGrantsManageRolesToAdmin(): void
    {
        $result = $this->voteWithRole('ROLE_ADMIN', UserVoter::MANAGE_ROLES);
        self::assertSame(UserVoter::ACCESS_GRANTED, $result);
    }

    #[Test]
    public function itGrantsManageRolesToSuperAdmin(): void
    {
        $result = $this->voteWithRole('ROLE_SUPER_ADMIN', UserVoter::MANAGE_ROLES);
        self::assertSame(UserVoter::ACCESS_GRANTED, $result);
    }

    #[Test]
    public function itDeniesManageRolesToProf(): void
    {
        $result = $this->voteWithRole('ROLE_PROF', UserVoter::MANAGE_ROLES);
        self::assertSame(UserVoter::ACCESS_DENIED, $result);
    }

    #[Test]
    public function itDeniesManageRolesToSecretariat(): void
    {
        $result = $this->voteWithRole('ROLE_SECRETARIAT', UserVoter::MANAGE_ROLES);
        self::assertSame(UserVoter::ACCESS_DENIED, $result);
    }

    #[Test]
    public function itGrantsResendInvitationToAdmin(): void
    {
        $result = $this->voteWithRole('ROLE_ADMIN', UserVoter::RESEND_INVITATION);
        self::assertSame(UserVoter::ACCESS_GRANTED, $result);
    }

    #[Test]
    public function itDeniesResendInvitationToProf(): void
    {
        $result = $this->voteWithRole('ROLE_PROF', UserVoter::RESEND_INVITATION);
        self::assertSame(UserVoter::ACCESS_DENIED, $result);
    }

    private function voteWithRole(string $role, string $attribute): int
    {
        $user = $this->createMock(UserInterface::class);
        $user->method('getRoles')->willReturn([$role]);

        $token = $this->createMock(TokenInterface::class);
        $token->method('getUser')->willReturn($user);

        return $this->voter->vote($token, null, [$attribute]);
    }
}
