<?php

declare(strict_types=1);

namespace App\Administration\Application\Query\ValidateHomeworkDueDate;

use App\Administration\Domain\Model\SchoolCalendar\SchoolCalendarRepository;
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;

use function preg_match;

use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler(bus: 'query.bus')]
final readonly class ValidateHomeworkDueDateHandler
{
    public function __construct(
        private SchoolCalendarRepository $calendarRepository,
    ) {
    }

    public function __invoke(ValidateHomeworkDueDateQuery $query): DueDateValidationResult
    {
        if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $query->dueDate) !== 1) {
            return DueDateValidationResult::invalide('La date doit être au format YYYY-MM-DD.');
        }

        [$y, $m, $d] = explode('-', $query->dueDate);
        if (!checkdate((int) $m, (int) $d, (int) $y)) {
            return DueDateValidationResult::invalide('La date n\'existe pas dans le calendrier.');
        }

        $dueDate = new DateTimeImmutable($query->dueDate);

        // Weekend
        $dayOfWeek = (int) $dueDate->format('N');
        if ($dayOfWeek >= 6) {
            return DueDateValidationResult::invalide(
                "L'échéance ne peut pas être un weekend.",
            );
        }

        $tenantId = TenantId::fromString($query->tenantId);
        $academicYearId = AcademicYearId::fromString($query->academicYearId);

        $calendar = $this->calendarRepository->findByTenantAndYear($tenantId, $academicYearId);

        if ($calendar === null) {
            return DueDateValidationResult::ok();
        }

        if (!$calendar->estJourOuvre($dueDate)) {
            return DueDateValidationResult::invalide(
                "L'échéance ne peut pas être un jour férié ou pendant les vacances.",
            );
        }

        if ($calendar->estJourRetourVacances($dueDate)) {
            return DueDateValidationResult::ok(
                'Attention : cette date est le jour du retour de vacances.',
            );
        }

        return DueDateValidationResult::ok();
    }
}
