<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Infrastructure\Security;

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\Domain\Model\User\UserId;
use App\Administration\Infrastructure\Security\SecurityUserFactory;
use App\Shared\Infrastructure\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class SecurityUserTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440002';

    private SecurityUserFactory $factory;

    protected function setUp(): void
    {
        $this->factory = new SecurityUserFactory();
    }

    #[Test]
    public function factoryCreatesSecurityUserWithCorrectData(): void
    {
        $domainUser = $this->createActivatedUser(Role::PARENT);

        $securityUser = $this->factory->fromDomainUser($domainUser);

        self::assertSame((string) $domainUser->email, $securityUser->getUserIdentifier());
        self::assertSame((string) $domainUser->id, $securityUser->userId());
        self::assertSame((string) $domainUser->email, $securityUser->email());
        self::assertSame($domainUser->hashedPassword, $securityUser->getPassword());
        self::assertSame((string) $domainUser->tenantId, $securityUser->tenantId());
    }

    #[Test]
    #[DataProvider('roleProvider')]
    public function factoryMapsRolesToSymfonyRoles(Role $domainRole, string $expectedSymfonyRole): void
    {
        $domainUser = $this->createActivatedUser($domainRole);

        $securityUser = $this->factory->fromDomainUser($domainUser);

        self::assertContains($expectedSymfonyRole, $securityUser->getRoles());
    }

    /**
     * @return iterable<string, array{Role, string}>
     */
    public static function roleProvider(): iterable
    {
        yield 'Super Admin' => [Role::SUPER_ADMIN, 'ROLE_SUPER_ADMIN'];
        yield 'Admin' => [Role::ADMIN, 'ROLE_ADMIN'];
        yield 'Prof' => [Role::PROF, 'ROLE_PROF'];
        yield 'Vie scolaire' => [Role::VIE_SCOLAIRE, 'ROLE_VIE_SCOLAIRE'];
        yield 'Secrétariat' => [Role::SECRETARIAT, 'ROLE_SECRETARIAT'];
        yield 'Parent' => [Role::PARENT, 'ROLE_PARENT'];
        yield 'Elève' => [Role::ELEVE, 'ROLE_ELEVE'];
    }

    #[Test]
    public function eraseCredentialsDoesNothing(): void
    {
        $domainUser = $this->createActivatedUser(Role::PARENT);
        $securityUser = $this->factory->fromDomainUser($domainUser);
        $passwordBefore = $securityUser->getPassword();

        $securityUser->eraseCredentials();

        // Les credentials sont immutables, donc rien ne change
        self::assertSame($passwordBefore, $securityUser->getPassword());
    }

    private function createActivatedUser(Role $role): User
    {
        return User::reconstitute(
            id: UserId::generate(),
            email: new Email('user@example.com'),
            roles: [$role],
            tenantId: TenantId::fromString(self::TENANT_ID),
            schoolName: 'École Test',
            statut: StatutCompte::ACTIF,
            dateNaissance: null,
            createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
            hashedPassword: '$argon2id$v=19$m=65536,t=4,p=1$salt$hash',
            activatedAt: new DateTimeImmutable('2026-01-15 12:00:00'),
            consentementParental: null,
        );
    }
}
