<?php

declare(strict_types=1);

namespace App\Administration\Domain\Model\ClassAssignment;

use App\Administration\Domain\Event\EleveAffecteAClasse;
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\AggregateRoot;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;

/**
 * Aggregate Root représentant l'affectation d'un élève à une classe.
 *
 * Un élève ne peut être affecté qu'à une seule classe par année scolaire.
 * Contrainte : UNIQUE(user_id, academic_year_id).
 *
 * @see FR76: Affectation élève↔classe
 */
final class ClassAssignment extends AggregateRoot
{
    public private(set) DateTimeImmutable $updatedAt;

    private function __construct(
        public private(set) ClassAssignmentId $id,
        public private(set) TenantId $tenantId,
        public private(set) UserId $studentId,
        public private(set) ClassId $classId,
        public private(set) AcademicYearId $academicYearId,
        public private(set) DateTimeImmutable $assignedAt,
        public private(set) DateTimeImmutable $createdAt,
    ) {
        $this->updatedAt = $createdAt;
    }

    public static function affecter(
        TenantId $tenantId,
        UserId $studentId,
        ClassId $classId,
        AcademicYearId $academicYearId,
        DateTimeImmutable $assignedAt,
    ): self {
        $assignment = new self(
            id: ClassAssignmentId::generate(),
            tenantId: $tenantId,
            studentId: $studentId,
            classId: $classId,
            academicYearId: $academicYearId,
            assignedAt: $assignedAt,
            createdAt: $assignedAt,
        );

        $assignment->recordEvent(new EleveAffecteAClasse(
            assignmentId: $assignment->id,
            studentId: $assignment->studentId,
            classId: $assignment->classId,
            occurredOn: $assignedAt,
        ));

        return $assignment;
    }

    public function changerClasse(ClassId $newClassId, DateTimeImmutable $changedAt): void
    {
        $this->classId = $newClassId;
        $this->updatedAt = $changedAt;

        $this->recordEvent(new EleveAffecteAClasse(
            assignmentId: $this->id,
            studentId: $this->studentId,
            classId: $newClassId,
            occurredOn: $changedAt,
        ));
    }

    /**
     * Reconstitue une ClassAssignment depuis le stockage.
     *
     * @internal Pour usage Infrastructure uniquement
     */
    public static function reconstitute(
        ClassAssignmentId $id,
        TenantId $tenantId,
        UserId $studentId,
        ClassId $classId,
        AcademicYearId $academicYearId,
        DateTimeImmutable $assignedAt,
        DateTimeImmutable $createdAt,
        DateTimeImmutable $updatedAt,
    ): self {
        $assignment = new self(
            id: $id,
            tenantId: $tenantId,
            studentId: $studentId,
            classId: $classId,
            academicYearId: $academicYearId,
            assignedAt: $assignedAt,
            createdAt: $createdAt,
        );

        $assignment->updatedAt = $updatedAt;

        return $assignment;
    }
}
