<?php

declare(strict_types=1);

namespace App\Administration\Domain\Model\SchoolClass;

use App\Administration\Domain\Event\ClasseArchivee;
use App\Administration\Domain\Event\ClasseCreee;
use App\Administration\Domain\Event\ClasseModifiee;
use App\Shared\Domain\AggregateRoot;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;

/**
 * Aggregate Root représentant une classe scolaire.
 *
 * Une classe appartient à un établissement (tenant), une école et une année scolaire.
 * Elle peut recevoir des élèves et est organisée par niveau scolaire.
 *
 * @see FR73: Organiser les élèves par groupes pédagogiques
 */
final class SchoolClass extends AggregateRoot
{
    public private(set) ?string $description = null;
    public private(set) DateTimeImmutable $updatedAt;
    public private(set) ?DateTimeImmutable $deletedAt = null;

    private function __construct(
        public private(set) ClassId $id,
        public private(set) TenantId $tenantId,
        public private(set) SchoolId $schoolId,
        public private(set) AcademicYearId $academicYearId,
        public private(set) ClassName $name,
        public private(set) ?SchoolLevel $level,
        public private(set) ?int $capacity,
        public private(set) ClassStatus $status,
        public private(set) DateTimeImmutable $createdAt,
    ) {
        $this->updatedAt = $createdAt;
    }

    /**
     * Crée une nouvelle classe scolaire.
     */
    public static function creer(
        TenantId $tenantId,
        SchoolId $schoolId,
        AcademicYearId $academicYearId,
        ClassName $name,
        ?SchoolLevel $level,
        ?int $capacity,
        DateTimeImmutable $createdAt,
    ): self {
        $class = new self(
            id: ClassId::generate(),
            tenantId: $tenantId,
            schoolId: $schoolId,
            academicYearId: $academicYearId,
            name: $name,
            level: $level,
            capacity: $capacity,
            status: ClassStatus::ACTIVE,
            createdAt: $createdAt,
        );

        $class->recordEvent(new ClasseCreee(
            classId: $class->id,
            tenantId: $class->tenantId,
            name: $class->name,
            level: $class->level,
            occurredOn: $createdAt,
        ));

        return $class;
    }

    /**
     * Renomme la classe.
     */
    public function renommer(ClassName $nouveauNom, DateTimeImmutable $at): void
    {
        if ($this->name->equals($nouveauNom)) {
            return;
        }

        $ancienNom = $this->name;
        $this->name = $nouveauNom;
        $this->updatedAt = $at;

        $this->recordEvent(new ClasseModifiee(
            classId: $this->id,
            tenantId: $this->tenantId,
            ancienNom: $ancienNom,
            nouveauNom: $nouveauNom,
            occurredOn: $at,
        ));
    }

    /**
     * Modifie le niveau scolaire de la classe.
     */
    public function changerNiveau(?SchoolLevel $niveau, DateTimeImmutable $at): void
    {
        if ($this->level === $niveau) {
            return;
        }

        $this->level = $niveau;
        $this->updatedAt = $at;
    }

    /**
     * Modifie la capacité maximale de la classe.
     */
    public function changerCapacite(?int $capacity, DateTimeImmutable $at): void
    {
        if ($this->capacity === $capacity) {
            return;
        }

        $this->capacity = $capacity;
        $this->updatedAt = $at;
    }

    /**
     * Ajoute ou modifie la description de la classe.
     */
    public function decrire(?string $description, DateTimeImmutable $at): void
    {
        $this->description = $description;
        $this->updatedAt = $at;
    }

    /**
     * Archive la classe (soft delete).
     *
     * Note: La vérification des élèves affectés doit être faite par l'Application Layer
     * via une Query avant d'appeler cette méthode.
     */
    public function archiver(DateTimeImmutable $at): void
    {
        if ($this->status === ClassStatus::ARCHIVED) {
            return;
        }

        $this->status = ClassStatus::ARCHIVED;
        $this->deletedAt = $at;
        $this->updatedAt = $at;

        $this->recordEvent(new ClasseArchivee(
            classId: $this->id,
            tenantId: $this->tenantId,
            occurredOn: $at,
        ));
    }

    /**
     * Vérifie si la classe est active.
     */
    public function estActive(): bool
    {
        return $this->status === ClassStatus::ACTIVE;
    }

    /**
     * Vérifie si la classe peut recevoir des élèves.
     */
    public function peutRecevoirEleves(): bool
    {
        return $this->status->peutRecevoirEleves();
    }

    /**
     * Reconstitue une SchoolClass depuis le stockage.
     *
     * @internal Pour usage Infrastructure uniquement
     */
    public static function reconstitute(
        ClassId $id,
        TenantId $tenantId,
        SchoolId $schoolId,
        AcademicYearId $academicYearId,
        ClassName $name,
        ?SchoolLevel $level,
        ?int $capacity,
        ClassStatus $status,
        ?string $description,
        DateTimeImmutable $createdAt,
        DateTimeImmutable $updatedAt,
        ?DateTimeImmutable $deletedAt,
    ): self {
        $class = new self(
            id: $id,
            tenantId: $tenantId,
            schoolId: $schoolId,
            academicYearId: $academicYearId,
            name: $name,
            level: $level,
            capacity: $capacity,
            status: $status,
            createdAt: $createdAt,
        );

        $class->description = $description;
        $class->updatedAt = $updatedAt;
        $class->deletedAt = $deletedAt;

        return $class;
    }
}
