<?php

declare(strict_types=1);

namespace App\Module\Core\Application\Rbac;

use App\Module\Core\Domain\Entity\User;
use App\Module\Core\Domain\Repository\RoleRepositoryInterface;
use App\Module\Core\Domain\Security\SystemRoles;
use Doctrine\ORM\EntityManagerInterface;

use function count;

/**
 * Garantit que chaque utilisateur porte le rôle RBAC de base « user ».
 *
 * Le rôle « user » est le socle commun : il porte les permissions par défaut
 * des non-admins. Sans rattachement explicite dans user_role,
 * User::getEffectivePermissions() reste vide — le ROLE_USER legacy n'a aucun
 * lien avec le rôle RBAC « user ».
 */
final readonly class DefaultUserRoleAssigner
{
    public function __construct(
        private RoleRepositoryInterface $roles,
        private EntityManagerInterface $em,
    ) {}

    /**
     * Ajoute le rôle « user » à l'utilisateur s'il ne l'a pas déjà.
     * Ne flush pas : appelé en prePersist (création) ou par le backfill.
     */
    public function ensureDefaultRole(User $user): void
    {
        $userRole = $this->roles->findByCode(SystemRoles::USER_CODE);
        if (null === $userRole) {
            // Rôle non seedé : dégradation gracieuse, on ne bloque pas la création.
            return;
        }

        foreach ($user->getRbacRoles() as $role) {
            if (SystemRoles::USER_CODE === $role->getCode()) {
                return;
            }
        }

        $user->addRbacRole($userRole);
    }

    /**
     * Rattache le rôle « user » à tous les utilisateurs qui ne l'ont pas.
     * Idempotent. Retourne le nombre d'utilisateurs modifiés.
     */
    public function backfill(): int
    {
        $userRole = $this->roles->findByCode(SystemRoles::USER_CODE);
        if (null === $userRole) {
            return 0;
        }

        /** @var list<User> $users */
        $users = $this->em
            ->createQuery('SELECT u FROM '.User::class.' u WHERE :role NOT MEMBER OF u.rbacRoles')
            ->setParameter('role', $userRole)
            ->getResult()
        ;

        foreach ($users as $user) {
            $user->addRbacRole($userRole);
        }

        $this->em->flush();

        return count($users);
    }
}
