<?php

declare(strict_types=1);

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

use App\Administration\Application\Command\UpdateImageRights\UpdateImageRightsCommand;
use App\Administration\Application\Command\UpdateImageRights\UpdateImageRightsHandler;
use App\Administration\Domain\Event\DroitImageModifie;
use App\Administration\Domain\Exception\UserNotFoundException;
use App\Administration\Domain\Model\User\Email;
use App\Administration\Domain\Model\User\ImageRightsStatus;
use App\Administration\Domain\Model\User\Role;
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 UpdateImageRightsHandlerTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440002';
    private const string MODIFIER_ID = '550e8400-e29b-41d4-a716-446655440099';

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

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

    #[Test]
    public function updatesImageRightsToAuthorized(): void
    {
        $student = $this->createAndSaveStudent();

        $command = new UpdateImageRightsCommand(
            studentId: (string) $student->id,
            status: 'authorized',
            modifiedBy: self::MODIFIER_ID,
            tenantId: self::TENANT_ID,
        );

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

        self::assertSame(ImageRightsStatus::AUTHORIZED, $user->imageRightsStatus);
    }

    #[Test]
    public function updatesImageRightsToRefused(): void
    {
        $student = $this->createAndSaveStudent();

        $command = new UpdateImageRightsCommand(
            studentId: (string) $student->id,
            status: 'refused',
            modifiedBy: self::MODIFIER_ID,
            tenantId: self::TENANT_ID,
        );

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

        self::assertSame(ImageRightsStatus::REFUSED, $user->imageRightsStatus);
    }

    #[Test]
    public function recordsDroitImageModifieEvent(): void
    {
        $student = $this->createAndSaveStudent();
        $student->pullDomainEvents();

        $command = new UpdateImageRightsCommand(
            studentId: (string) $student->id,
            status: 'authorized',
            modifiedBy: self::MODIFIER_ID,
            tenantId: self::TENANT_ID,
        );

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

        self::assertCount(1, $events);
        self::assertInstanceOf(DroitImageModifie::class, $events[0]);
    }

    #[Test]
    public function throwsWhenStudentNotFound(): void
    {
        $this->expectException(UserNotFoundException::class);

        $command = new UpdateImageRightsCommand(
            studentId: '550e8400-e29b-41d4-a716-446655440000',
            status: 'authorized',
            modifiedBy: self::MODIFIER_ID,
            tenantId: self::TENANT_ID,
        );

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

    #[Test]
    public function throwsWhenTenantMismatch(): void
    {
        $student = $this->createAndSaveStudent();

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

        $command = new UpdateImageRightsCommand(
            studentId: (string) $student->id,
            status: 'authorized',
            modifiedBy: self::MODIFIER_ID,
            tenantId: '550e8400-e29b-41d4-a716-446655440099',
        );

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

    #[Test]
    public function throwsWhenUserIsNotStudent(): void
    {
        $admin = User::creer(
            email: new Email('admin@example.com'),
            role: Role::ADMIN,
            tenantId: TenantId::fromString(self::TENANT_ID),
            schoolName: 'École Alpha',
            dateNaissance: null,
            createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
        );
        $this->userRepository->save($admin);

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

        $command = new UpdateImageRightsCommand(
            studentId: (string) $admin->id,
            status: 'authorized',
            modifiedBy: self::MODIFIER_ID,
            tenantId: self::TENANT_ID,
        );

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

    #[Test]
    public function savesUserAfterUpdate(): void
    {
        $student = $this->createAndSaveStudent();

        $command = new UpdateImageRightsCommand(
            studentId: (string) $student->id,
            status: 'refused',
            modifiedBy: self::MODIFIER_ID,
            tenantId: self::TENANT_ID,
        );

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

        $saved = $this->userRepository->get($student->id);
        self::assertSame(ImageRightsStatus::REFUSED, $saved->imageRightsStatus);
    }

    private function createAndSaveStudent(): User
    {
        $student = User::creer(
            email: new Email('eleve@example.com'),
            role: Role::ELEVE,
            tenantId: TenantId::fromString(self::TENANT_ID),
            schoolName: 'École Alpha',
            dateNaissance: new DateTimeImmutable('2012-06-15'),
            createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
        );

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

        return $student;
    }
}
