<?php

declare(strict_types=1);

namespace App\Administration\Application\Command\AssignStudentToClass;

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

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

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

        // Valider l'existence des entités référencées et leur tenant
        $student = $this->userRepository->get($studentId);

        if (!$student->tenantId->equals($tenantId)) {
            throw UserNotFoundException::withId($studentId);
        }

        $class = $this->classRepository->get($classId);

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

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

        // Vérifier qu'il n'y a pas déjà une affectation pour cette année scolaire
        $existing = $this->classAssignmentRepository->findByStudent($studentId, $academicYearId, $tenantId);

        if ($existing !== null) {
            throw EleveDejaAffecteException::pourAnneeScolaire($studentId);
        }

        $assignment = ClassAssignment::affecter(
            tenantId: $tenantId,
            studentId: $studentId,
            classId: $classId,
            academicYearId: $academicYearId,
            assignedAt: $this->clock->now(),
        );

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

        return $assignment;
    }
}
