<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Domain\Model\ClassAssignment;

use App\Administration\Domain\Event\EleveAffecteAClasse;
use App\Administration\Domain\Model\ClassAssignment\ClassAssignment;
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
use App\Administration\Domain\Model\SchoolClass\ClassId;
use App\Administration\Domain\Model\User\UserId;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class ClassAssignmentTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
    private const string STUDENT_ID = '550e8400-e29b-41d4-a716-446655440010';
    private const string CLASS_ID = '550e8400-e29b-41d4-a716-446655440020';
    private const string ACADEMIC_YEAR_ID = '550e8400-e29b-41d4-a716-446655440040';

    #[Test]
    public function affecterCreeAffectationAvecIdUnique(): void
    {
        $assignment = $this->createAssignment();

        self::assertNotEmpty((string) $assignment->id);
        self::assertTrue($assignment->tenantId->equals(TenantId::fromString(self::TENANT_ID)));
        self::assertTrue($assignment->studentId->equals(UserId::fromString(self::STUDENT_ID)));
        self::assertTrue($assignment->classId->equals(ClassId::fromString(self::CLASS_ID)));
        self::assertTrue($assignment->academicYearId->equals(AcademicYearId::fromString(self::ACADEMIC_YEAR_ID)));
    }

    #[Test]
    public function affecterEnregistreEvenementEleveAffecteAClasse(): void
    {
        $assignment = $this->createAssignment();

        $events = $assignment->pullDomainEvents();

        self::assertCount(1, $events);
        self::assertInstanceOf(EleveAffecteAClasse::class, $events[0]);
        self::assertTrue($events[0]->studentId->equals(UserId::fromString(self::STUDENT_ID)));
        self::assertTrue($events[0]->classId->equals(ClassId::fromString(self::CLASS_ID)));
    }

    #[Test]
    public function affecterInitialiseUpdatedAtAvecCreatedAt(): void
    {
        $now = new DateTimeImmutable('2026-02-21 10:00:00');

        $assignment = ClassAssignment::affecter(
            tenantId: TenantId::fromString(self::TENANT_ID),
            studentId: UserId::fromString(self::STUDENT_ID),
            classId: ClassId::fromString(self::CLASS_ID),
            academicYearId: AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
            assignedAt: $now,
        );

        self::assertSame($now, $assignment->createdAt);
        self::assertSame($now, $assignment->updatedAt);
        self::assertSame($now, $assignment->assignedAt);
    }

    #[Test]
    public function changerClasseModifieLaClasseEtUpdatedAt(): void
    {
        $assignment = $this->createAssignment();
        $assignment->pullDomainEvents(); // Clear creation event

        $newClassId = ClassId::fromString('550e8400-e29b-41d4-a716-446655440099');
        $changedAt = new DateTimeImmutable('2026-03-15 14:00:00');

        $assignment->changerClasse($newClassId, $changedAt);

        self::assertTrue($assignment->classId->equals($newClassId));
        self::assertSame($changedAt, $assignment->updatedAt);
    }

    #[Test]
    public function changerClasseEnregistreEvenementEleveAffecteAClasse(): void
    {
        $assignment = $this->createAssignment();
        $assignment->pullDomainEvents(); // Clear creation event

        $newClassId = ClassId::fromString('550e8400-e29b-41d4-a716-446655440099');
        $changedAt = new DateTimeImmutable('2026-03-15 14:00:00');

        $assignment->changerClasse($newClassId, $changedAt);

        $events = $assignment->pullDomainEvents();

        self::assertCount(1, $events);
        self::assertInstanceOf(EleveAffecteAClasse::class, $events[0]);
        self::assertTrue($events[0]->classId->equals($newClassId));
        self::assertSame($changedAt, $events[0]->occurredOn());
    }

    #[Test]
    public function reconstituteNeGenereAucunEvenement(): void
    {
        $assignment = ClassAssignment::reconstitute(
            id: \App\Administration\Domain\Model\ClassAssignment\ClassAssignmentId::fromString('550e8400-e29b-41d4-a716-446655440050'),
            tenantId: TenantId::fromString(self::TENANT_ID),
            studentId: UserId::fromString(self::STUDENT_ID),
            classId: ClassId::fromString(self::CLASS_ID),
            academicYearId: AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
            assignedAt: new DateTimeImmutable('2026-02-21 10:00:00'),
            createdAt: new DateTimeImmutable('2026-02-21 10:00:00'),
            updatedAt: new DateTimeImmutable('2026-02-21 12:00:00'),
        );

        self::assertEmpty($assignment->pullDomainEvents());
    }

    private function createAssignment(): ClassAssignment
    {
        return ClassAssignment::affecter(
            tenantId: TenantId::fromString(self::TENANT_ID),
            studentId: UserId::fromString(self::STUDENT_ID),
            classId: ClassId::fromString(self::CLASS_ID),
            academicYearId: AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
            assignedAt: new DateTimeImmutable('2026-02-21 10:00:00'),
        );
    }
}
