<?php

declare(strict_types=1);

namespace App\Administration\Application\Command\ChangeStudentClass;

use App\Administration\Domain\Exception\ClasseNotFoundException;
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\Administration\Domain\Repository\ClassAssignmentRepository;
use App\Administration\Domain\Repository\ClassRepository;
use App\Shared\Domain\Clock;
use App\Shared\Domain\Tenant\TenantId;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler(bus: 'command.bus')]
final readonly class ChangeStudentClassHandler
{
    public function __construct(
        private ClassAssignmentRepository $classAssignmentRepository,
        private ClassRepository $classRepository,
        private Clock $clock,
    ) {
    }

    public function __invoke(ChangeStudentClassCommand $command): ClassAssignment
    {
        $tenantId = TenantId::fromString($command->tenantId);
        $studentId = UserId::fromString($command->studentId);
        $newClassId = ClassId::fromString($command->newClassId);
        $academicYearId = AcademicYearId::fromString($command->academicYearId);

        // Valider l'existence de la nouvelle classe, son tenant et son statut
        $class = $this->classRepository->get($newClassId);

        if (!$class->tenantId->equals($tenantId)) {
            throw ClasseNotFoundException::withId($newClassId);
        }

        if (!$class->status->peutRecevoirEleves()) {
            throw ClasseNotFoundException::withId($newClassId);
        }

        $now = $this->clock->now();

        // Trouver l'affectation existante ou en créer une nouvelle
        $assignment = $this->classAssignmentRepository->findByStudent($studentId, $academicYearId, $tenantId);

        if ($assignment !== null) {
            $assignment->changerClasse($newClassId, $now);
        } else {
            $assignment = ClassAssignment::affecter(
                tenantId: $tenantId,
                studentId: $studentId,
                classId: $newClassId,
                academicYearId: $academicYearId,
                assignedAt: $now,
            );
        }

        $this->classAssignmentRepository->save($assignment);

        return $assignment;
    }
}
