<?php

declare(strict_types=1);

namespace App\Administration\Domain\Model\TeacherAssignment;

use App\Administration\Domain\Event\AffectationRetiree;
use App\Administration\Domain\Event\EnseignantAffecte;
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
use App\Administration\Domain\Model\SchoolClass\ClassId;
use App\Administration\Domain\Model\Subject\SubjectId;
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 enseignant à une classe et une matière.
 *
 * Relation ternaire : Teacher × Class × Subject, scopée par année scolaire.
 * Supporte le multi-matière (un enseignant, plusieurs matières par classe)
 * et le co-enseignement (plusieurs enseignants, même matière dans une classe).
 *
 * @see FR78: Affecter enseignant à classe et matière
 */
final class TeacherAssignment extends AggregateRoot
{
    public private(set) DateTimeImmutable $updatedAt;

    private function __construct(
        public private(set) TeacherAssignmentId $id,
        public private(set) TenantId $tenantId,
        public private(set) UserId $teacherId,
        public private(set) ClassId $classId,
        public private(set) SubjectId $subjectId,
        public private(set) AcademicYearId $academicYearId,
        public private(set) DateTimeImmutable $startDate,
        public private(set) ?DateTimeImmutable $endDate,
        public private(set) AssignmentStatus $status,
        public private(set) DateTimeImmutable $createdAt,
    ) {
        $this->updatedAt = $createdAt;
    }

    public static function creer(
        TenantId $tenantId,
        UserId $teacherId,
        ClassId $classId,
        SubjectId $subjectId,
        AcademicYearId $academicYearId,
        DateTimeImmutable $createdAt,
    ): self {
        $assignment = new self(
            id: TeacherAssignmentId::generate(),
            tenantId: $tenantId,
            teacherId: $teacherId,
            classId: $classId,
            subjectId: $subjectId,
            academicYearId: $academicYearId,
            startDate: $createdAt,
            endDate: null,
            status: AssignmentStatus::ACTIVE,
            createdAt: $createdAt,
        );

        $assignment->recordEvent(new EnseignantAffecte(
            assignmentId: $assignment->id,
            teacherId: $assignment->teacherId,
            classId: $assignment->classId,
            subjectId: $assignment->subjectId,
            tenantId: $assignment->tenantId,
            occurredOn: $createdAt,
        ));

        return $assignment;
    }

    /**
     * Retire l'affectation. Les notes existantes sont conservées (historique),
     * mais l'enseignant ne peut plus en ajouter.
     */
    public function retirer(DateTimeImmutable $at): void
    {
        if ($this->status === AssignmentStatus::REMOVED) {
            return;
        }

        $this->status = AssignmentStatus::REMOVED;
        $this->endDate = $at;
        $this->updatedAt = $at;

        $this->recordEvent(new AffectationRetiree(
            assignmentId: $this->id,
            teacherId: $this->teacherId,
            classId: $this->classId,
            subjectId: $this->subjectId,
            tenantId: $this->tenantId,
            occurredOn: $at,
        ));
    }

    /**
     * Réactive une affectation précédemment retirée.
     *
     * Permet de ré-affecter un enseignant au même triplet sans violer
     * la contrainte d'unicité en base.
     */
    public function reactiver(DateTimeImmutable $at): void
    {
        if ($this->status === AssignmentStatus::ACTIVE) {
            return;
        }

        $this->status = AssignmentStatus::ACTIVE;
        $this->endDate = null;
        $this->startDate = $at;
        $this->updatedAt = $at;

        $this->recordEvent(new EnseignantAffecte(
            assignmentId: $this->id,
            teacherId: $this->teacherId,
            classId: $this->classId,
            subjectId: $this->subjectId,
            tenantId: $this->tenantId,
            occurredOn: $at,
        ));
    }

    public function estActive(): bool
    {
        return $this->status->estActive();
    }

    /**
     * Reconstitue une TeacherAssignment depuis le stockage.
     *
     * @internal Pour usage Infrastructure uniquement
     */
    public static function reconstitute(
        TeacherAssignmentId $id,
        TenantId $tenantId,
        UserId $teacherId,
        ClassId $classId,
        SubjectId $subjectId,
        AcademicYearId $academicYearId,
        DateTimeImmutable $startDate,
        ?DateTimeImmutable $endDate,
        AssignmentStatus $status,
        DateTimeImmutable $createdAt,
        DateTimeImmutable $updatedAt,
    ): self {
        $assignment = new self(
            id: $id,
            tenantId: $tenantId,
            teacherId: $teacherId,
            classId: $classId,
            subjectId: $subjectId,
            academicYearId: $academicYearId,
            startDate: $startDate,
            endDate: $endDate,
            status: $status,
            createdAt: $createdAt,
        );

        $assignment->updatedAt = $updatedAt;

        return $assignment;
    }
}
