<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Application\Command\InviteUser;

use App\Administration\Application\Command\InviteUser\InviteUserCommand;
use App\Administration\Application\Command\InviteUser\InviteUserHandler;
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\StatutCompte;
use App\Administration\Domain\Model\User\User;
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryUserRepository;
use App\Shared\Domain\Clock;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class InviteUserHandlerTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440002';
    private const string SCHOOL_NAME = 'École Alpha';

    private InMemoryUserRepository $userRepository;
    private Clock $clock;
    private InviteUserHandler $handler;

    protected function setUp(): void
    {
        $this->userRepository = new InMemoryUserRepository();
        $this->clock = new class implements Clock {
            public function now(): DateTimeImmutable
            {
                return new DateTimeImmutable('2026-02-07 10:00:00');
            }
        };
        $this->handler = new InviteUserHandler($this->userRepository, $this->clock);
    }

    #[Test]
    public function invitesUserSuccessfully(): void
    {
        $command = new InviteUserCommand(
            tenantId: self::TENANT_ID,
            schoolName: self::SCHOOL_NAME,
            email: 'teacher@example.com',
            role: Role::PROF->value,
            firstName: 'Jean',
            lastName: 'Dupont',
        );

        $user = ($this->handler)($command);

        self::assertSame(StatutCompte::EN_ATTENTE, $user->statut);
        self::assertSame('Jean', $user->firstName);
        self::assertSame('Dupont', $user->lastName);
        self::assertSame('teacher@example.com', (string) $user->email);
        self::assertNotNull($user->invitedAt);
    }

    #[Test]
    public function throwsWhenEmailAlreadyUsedInTenant(): void
    {
        // Pre-populate with existing user
        $existingUser = User::inviter(
            email: new Email('teacher@example.com'),
            role: Role::PROF,
            tenantId: TenantId::fromString(self::TENANT_ID),
            schoolName: self::SCHOOL_NAME,
            firstName: 'Existing',
            lastName: 'User',
            invitedAt: new DateTimeImmutable('2026-02-01 10:00:00'),
        );
        $this->userRepository->save($existingUser);

        $command = new InviteUserCommand(
            tenantId: self::TENANT_ID,
            schoolName: self::SCHOOL_NAME,
            email: 'teacher@example.com',
            role: Role::PROF->value,
            firstName: 'Jean',
            lastName: 'Dupont',
        );

        $this->expectException(EmailDejaUtiliseeException::class);

        ($this->handler)($command);
    }

    #[Test]
    public function savesUserToRepository(): void
    {
        $command = new InviteUserCommand(
            tenantId: self::TENANT_ID,
            schoolName: self::SCHOOL_NAME,
            email: 'teacher@example.com',
            role: Role::PROF->value,
            firstName: 'Jean',
            lastName: 'Dupont',
        );

        $user = ($this->handler)($command);

        $found = $this->userRepository->get($user->id);
        self::assertSame((string) $user->id, (string) $found->id);
    }
}
