<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Application\Query\GetUsers;

use App\Administration\Application\Query\GetUsers\GetUsersHandler;
use App\Administration\Application\Query\GetUsers\GetUsersQuery;
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\Policy\ConsentementParentalPolicy;
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 GetUsersHandlerTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440002';
    private const string OTHER_TENANT_ID = '550e8400-e29b-41d4-a716-446655440099';

    private InMemoryUserRepository $userRepository;
    private Clock $clock;
    private GetUsersHandler $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 GetUsersHandler($this->userRepository, $this->clock);
    }

    #[Test]
    public function returnsAllUsersForTenant(): void
    {
        $this->seedUsers();

        $query = new GetUsersQuery(tenantId: self::TENANT_ID);
        $result = ($this->handler)($query);

        self::assertCount(3, $result);
    }

    #[Test]
    public function filtersUsersByRole(): void
    {
        $this->seedUsers();

        $query = new GetUsersQuery(
            tenantId: self::TENANT_ID,
            role: Role::PROF->value,
        );
        $result = ($this->handler)($query);

        self::assertCount(2, $result);
        foreach ($result as $dto) {
            self::assertSame(Role::PROF->value, $dto->role);
        }
    }

    #[Test]
    public function filtersUsersByStatut(): void
    {
        $this->seedUsers();

        $query = new GetUsersQuery(
            tenantId: self::TENANT_ID,
            statut: 'pending',
        );
        $result = ($this->handler)($query);

        self::assertCount(2, $result);
        foreach ($result as $dto) {
            self::assertSame('pending', $dto->statut);
        }
    }

    #[Test]
    public function excludesUsersFromOtherTenants(): void
    {
        $this->seedUsers();

        // Add user to different tenant
        $otherUser = User::inviter(
            email: new Email('other@example.com'),
            role: Role::ADMIN,
            tenantId: TenantId::fromString(self::OTHER_TENANT_ID),
            schoolName: 'Autre École',
            firstName: 'Autre',
            lastName: 'User',
            invitedAt: new DateTimeImmutable('2026-02-01 10:00:00'),
        );
        $this->userRepository->save($otherUser);

        $query = new GetUsersQuery(tenantId: self::TENANT_ID);
        $result = ($this->handler)($query);

        self::assertCount(3, $result);
    }

    #[Test]
    public function calculatesInvitationExpiree(): void
    {
        // Invited 10 days ago — should be expired
        $user = User::inviter(
            email: new Email('old@example.com'),
            role: Role::PROF,
            tenantId: TenantId::fromString(self::TENANT_ID),
            schoolName: 'École Alpha',
            firstName: 'Old',
            lastName: 'Invitation',
            invitedAt: new DateTimeImmutable('2026-01-25 10:00:00'),
        );
        $this->userRepository->save($user);

        $query = new GetUsersQuery(tenantId: self::TENANT_ID);
        $result = ($this->handler)($query);

        self::assertCount(1, $result);
        self::assertTrue($result[0]->invitationExpiree);
    }

    private function seedUsers(): void
    {
        $tenantId = TenantId::fromString(self::TENANT_ID);

        $teacher1 = User::inviter(
            email: new Email('teacher1@example.com'),
            role: Role::PROF,
            tenantId: $tenantId,
            schoolName: 'École Alpha',
            firstName: 'Jean',
            lastName: 'Dupont',
            invitedAt: new DateTimeImmutable('2026-02-01 10:00:00'),
        );
        $this->userRepository->save($teacher1);

        $teacher2 = User::inviter(
            email: new Email('teacher2@example.com'),
            role: Role::PROF,
            tenantId: $tenantId,
            schoolName: 'École Alpha',
            firstName: 'Marie',
            lastName: 'Martin',
            invitedAt: new DateTimeImmutable('2026-02-01 10:00:00'),
        );
        // Activate teacher2
        $teacher2->activer(
            '$argon2id$hashed',
            new DateTimeImmutable('2026-02-02 10:00:00'),
            new ConsentementParentalPolicy($this->clock),
        );
        $this->userRepository->save($teacher2);

        $parent = User::inviter(
            email: new Email('parent@example.com'),
            role: Role::PARENT,
            tenantId: $tenantId,
            schoolName: 'École Alpha',
            firstName: 'Pierre',
            lastName: 'Parent',
            invitedAt: new DateTimeImmutable('2026-02-01 10:00:00'),
        );
        $this->userRepository->save($parent);
    }
}
