<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CoreBundle\Tests\Security;

use Ferienpass\CoreBundle\Entity\User;
use Ferienpass\CoreBundle\Security\Exception\BlockedEmailException;
use Ferienpass\CoreBundle\Security\UserChecker;
use Ferienpass\CoreBundle\Security\UserChecker\AdminUserChecker;
use Ferienpass\CoreBundle\Security\UserChecker\FrontendUserChecker;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException;
use Symfony\Component\Security\Core\Exception\DisabledException;
use Symfony\Component\Security\Core\User\UserInterface;

final class UserCheckerTest extends TestCase
{
    // ---- UserChecker ----

    public function testCheckPreAuthSkipsNonUser(): void
    {
        $checker = new UserChecker();
        $nonUser = $this->createMock(UserInterface::class);

        // Should not throw
        $checker->checkPreAuth($nonUser);
        $this->addToAssertionCount(1);
    }

    public function testCheckPreAuthPassesWhenNotDisabled(): void
    {
        $checker = new UserChecker();
        $user = new User();

        // Should not throw
        $checker->checkPreAuth($user);
        $this->addToAssertionCount(1);
    }

    public function testCheckPreAuthThrowsWhenDisabled(): void
    {
        $checker = new UserChecker();
        $user = new User();

        $rp = new \ReflectionProperty($user, 'disable');
        $rp->setValue($user, true);

        $this->expectException(DisabledException::class);
        $checker->checkPreAuth($user);
    }

    public function testCheckPostAuthSkipsNonUser(): void
    {
        $checker = new UserChecker();
        $nonUser = $this->createMock(UserInterface::class);

        // Should not throw
        $checker->checkPostAuth($nonUser);
        $this->addToAssertionCount(1);
    }

    public function testCheckPostAuthPassesWhenNotBlocked(): void
    {
        $checker = new UserChecker();
        $user = new User();

        // Should not throw
        $checker->checkPostAuth($user);
        $this->addToAssertionCount(1);
    }

    public function testCheckPostAuthThrowsWhenEmailBlocked(): void
    {
        $checker = new UserChecker();
        $user = new User();

        $rp = new \ReflectionProperty($user, 'blockedEmail');
        $rp->setValue($user, true);

        $this->expectException(BlockedEmailException::class);
        $checker->checkPostAuth($user);
    }

    // ---- AdminUserChecker ----

    public function testAdminCheckerCheckPreAuthDoesNothing(): void
    {
        $checker = new AdminUserChecker();
        $user = new User();

        $checker->checkPreAuth($user);
        $this->addToAssertionCount(1);
    }

    public function testAdminCheckerSkipsNonUser(): void
    {
        $checker = new AdminUserChecker();
        $nonUser = $this->createMock(UserInterface::class);

        $checker->checkPostAuth($nonUser);
        $this->addToAssertionCount(1);
    }

    public function testAdminCheckerPassesForAdmin(): void
    {
        $checker = new AdminUserChecker();
        $user = new User();
        $user->setRoles(['ROLE_ADMIN']);

        $checker->checkPostAuth($user);
        $this->addToAssertionCount(1);
    }

    public function testAdminCheckerPassesForHost(): void
    {
        $checker = new AdminUserChecker();
        $user = new User();
        $user->setRoles(['ROLE_HOST']);

        $checker->checkPostAuth($user);
        $this->addToAssertionCount(1);
    }

    public function testAdminCheckerThrowsForMemberOnly(): void
    {
        $checker = new AdminUserChecker();
        $user = new User();
        $user->setRoles(['ROLE_MEMBER']);

        $this->expectException(CustomUserMessageAccountStatusException::class);
        $checker->checkPostAuth($user);
    }

    // ---- FrontendUserChecker ----

    public function testFrontendCheckerCheckPreAuthDoesNothing(): void
    {
        $checker = new FrontendUserChecker();
        $user = new User();

        $checker->checkPreAuth($user);
        $this->addToAssertionCount(1);
    }

    public function testFrontendCheckerSkipsNonUser(): void
    {
        $checker = new FrontendUserChecker();
        $nonUser = $this->createMock(UserInterface::class);

        $checker->checkPostAuth($nonUser);
        $this->addToAssertionCount(1);
    }

    public function testFrontendCheckerPassesForMember(): void
    {
        $checker = new FrontendUserChecker();
        $user = new User();
        $user->setRoles(['ROLE_MEMBER']);

        $checker->checkPostAuth($user);
        $this->addToAssertionCount(1);
    }

    public function testFrontendCheckerThrowsForAdminOnly(): void
    {
        $checker = new FrontendUserChecker();
        $user = new User();
        $user->setRoles(['ROLE_ADMIN']);

        $this->expectException(CustomUserMessageAccountStatusException::class);
        $checker->checkPostAuth($user);
    }

    // ---- BlockedEmailException ----

    public function testBlockedEmailExceptionMessageKey(): void
    {
        $ex = new BlockedEmailException();

        self::assertStringContainsString('unsubscribed', $ex->getMessageKey());
    }
}
