<?php

declare(strict_types=1);

namespace App\Tests\Module\Core\Domain\Entity;

use App\Module\Core\Domain\Entity\Permission;
use App\Module\Core\Domain\Entity\Role;
use App\Module\Core\Domain\Entity\User;
use PHPUnit\Framework\TestCase;

/**
 * @internal
 */
final class UserTest extends TestCase
{
    public function testGetRolesReturnsRoleUserByDefault(): void
    {
        $user = new User();

        self::assertSame(['ROLE_USER'], $user->getRoles());
    }

    public function testGetRolesIncludesRoleAdminWhenIsAdminTrue(): void
    {
        $user = new User();
        $user->setIsAdmin(true);

        self::assertSame(['ROLE_USER', 'ROLE_ADMIN'], $user->getRoles());
    }

    public function testIsAdminDefaultsToFalse(): void
    {
        $user = new User();

        self::assertFalse($user->isAdmin());
    }

    public function testGetEffectivePermissionsIsEmptyByDefault(): void
    {
        $user = new User();

        self::assertSame([], $user->getEffectivePermissions());
    }

    public function testGetEffectivePermissionsUnionsRolesAndDirects(): void
    {
        $perm1 = new Permission('core.users.view', 'View users', 'core');
        $perm2 = new Permission('core.users.edit', 'Edit users', 'core');
        $perm3 = new Permission('core.users.delete', 'Delete users', 'core');

        $role = new Role('manager', 'Manager');
        $role->addPermission($perm1);
        $role->addPermission($perm2);

        $user = new User();
        $user->addRbacRole($role);
        $user->addDirectPermission($perm3);

        self::assertSame(
            ['core.users.delete', 'core.users.edit', 'core.users.view'],
            $user->getEffectivePermissions(),
        );
    }

    public function testGetEffectivePermissionsDeduplicatesAcrossRolesAndDirects(): void
    {
        $perm = new Permission('core.users.view', 'View users', 'core');

        $role = new Role('viewer', 'Viewer');
        $role->addPermission($perm);

        $user = new User();
        $user->addRbacRole($role);
        $user->addDirectPermission($perm);

        $result = $user->getEffectivePermissions();

        self::assertCount(1, $result);
        self::assertSame(['core.users.view'], $result);
    }

    public function testAddRbacRoleIsIdempotent(): void
    {
        $role = new Role('manager', 'Manager');
        $user = new User();

        $user->addRbacRole($role);
        $user->addRbacRole($role);

        self::assertSame(1, $user->getRbacRoles()->count());
    }

    public function testAddDirectPermissionIsIdempotent(): void
    {
        $perm = new Permission('core.users.view', 'View users', 'core');
        $user = new User();

        $user->addDirectPermission($perm);
        $user->addDirectPermission($perm);

        self::assertSame(1, $user->getDirectPermissions()->count());
    }

    public function testRemoveRbacRole(): void
    {
        $role = new Role('manager', 'Manager');
        $user = new User();

        $user->addRbacRole($role);
        $user->removeRbacRole($role);

        self::assertSame(0, $user->getRbacRoles()->count());
    }

    public function testGetEffectivePermissionsOutputIsSorted(): void
    {
        $permZ = new Permission('core.z.action', 'Z', 'core');
        $permA = new Permission('core.a.action', 'A', 'core');
        $permM = new Permission('core.m.action', 'M', 'core');

        $user = new User();
        $user->addDirectPermission($permZ);
        $user->addDirectPermission($permA);
        $user->addDirectPermission($permM);

        self::assertSame(
            ['core.a.action', 'core.m.action', 'core.z.action'],
            $user->getEffectivePermissions(),
        );
    }
}
