<?php

declare(strict_types=1);

namespace App\Scolarite\Application\Command\CreateScheduleException;

use App\Administration\Domain\Model\User\UserId;
use App\Scolarite\Domain\Exception\DateExceptionInvalideException;
use App\Scolarite\Domain\Model\Schedule\ScheduleException;
use App\Scolarite\Domain\Model\Schedule\ScheduleExceptionType;
use App\Scolarite\Domain\Model\Schedule\ScheduleSlotId;
use App\Scolarite\Domain\Model\Schedule\TimeSlot;
use App\Scolarite\Domain\Repository\ScheduleExceptionRepository;
use App\Scolarite\Domain\Repository\ScheduleSlotRepository;
use App\Shared\Domain\Clock;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler(bus: 'command.bus')]
final readonly class CreateScheduleExceptionHandler
{
    public function __construct(
        private ScheduleSlotRepository $slotRepository,
        private ScheduleExceptionRepository $exceptionRepository,
        private Clock $clock,
    ) {
    }

    public function __invoke(CreateScheduleExceptionCommand $command): ScheduleException
    {
        $tenantId = TenantId::fromString($command->tenantId);
        $slotId = ScheduleSlotId::fromString($command->slotId);

        $slot = $this->slotRepository->get($slotId, $tenantId);

        $type = ScheduleExceptionType::from($command->type);
        $exceptionDate = new DateTimeImmutable($command->exceptionDate);

        if (!$slot->isActiveOnDate($exceptionDate)) {
            throw DateExceptionInvalideException::pourSlotEtDate($slotId, $exceptionDate);
        }
        $createdBy = UserId::fromString($command->createdBy);

        $exception = match ($type) {
            ScheduleExceptionType::CANCELLED => ScheduleException::annuler(
                tenantId: $tenantId,
                slotId: $slotId,
                exceptionDate: $exceptionDate,
                reason: $command->reason,
                createdBy: $createdBy,
                now: $this->clock->now(),
            ),
            ScheduleExceptionType::MODIFIED => ScheduleException::modifier(
                tenantId: $tenantId,
                slotId: $slotId,
                exceptionDate: $exceptionDate,
                newTimeSlot: $command->newStartTime !== null && $command->newEndTime !== null
                    ? new TimeSlot($command->newStartTime, $command->newEndTime)
                    : null,
                newRoom: $command->newRoom,
                newTeacherId: $command->newTeacherId !== null
                    ? UserId::fromString($command->newTeacherId)
                    : null,
                reason: $command->reason,
                createdBy: $createdBy,
                now: $this->clock->now(),
            ),
        };

        $this->exceptionRepository->save($exception);

        return $exception;
    }
}
