<?php

declare(strict_types=1);

namespace App\Administration\Application\Command\InviteUser;

use App\Administration\Domain\Exception\EmailDejaUtiliseeException;
use App\Administration\Domain\Model\User\Email;
use App\Administration\Domain\Model\User\Role;
use App\Administration\Domain\Model\User\User;
use App\Administration\Domain\Repository\UserRepository;
use App\Shared\Domain\Clock;
use App\Shared\Domain\Tenant\TenantId;

use function array_map;
use function array_slice;

use DateTimeImmutable;
use InvalidArgumentException;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler(bus: 'command.bus')]
final readonly class InviteUserHandler
{
    public function __construct(
        private UserRepository $userRepository,
        private Clock $clock,
    ) {
    }

    /**
     * @throws EmailDejaUtiliseeException if email is already used in this tenant
     * @throws InvalidArgumentException   if a role is invalid
     */
    public function __invoke(InviteUserCommand $command): User
    {
        $tenantId = TenantId::fromString($command->tenantId);
        $email = new Email($command->email);

        $roles = array_map(static function (string $r): Role {
            $role = Role::tryFrom($r);
            if ($role === null) {
                throw new InvalidArgumentException("Rôle invalide : \"{$r}\".");
            }

            return $role;
        }, $command->roles);

        if ($roles === []) {
            throw new InvalidArgumentException('Au moins un rôle est requis.');
        }

        $existingUser = $this->userRepository->findByEmail($email, $tenantId);
        if ($existingUser !== null) {
            throw EmailDejaUtiliseeException::dansTenant($email, $tenantId);
        }

        $now = $this->clock->now();

        $user = User::inviter(
            email: $email,
            role: $roles[0],
            tenantId: $tenantId,
            schoolName: $command->schoolName,
            firstName: $command->firstName,
            lastName: $command->lastName,
            invitedAt: $now,
            dateNaissance: $command->dateNaissance !== null
                ? new DateTimeImmutable($command->dateNaissance)
                : null,
        );

        foreach (array_slice($roles, 1) as $additionalRole) {
            $user->attribuerRole($additionalRole, $now);
        }

        $this->userRepository->save($user);

        return $user;
    }
}
