<?php

declare(strict_types=1);

namespace App\Scolarite\Application\Query\GetStudentSchedule;

use App\Administration\Domain\Model\SchoolClass\ClassId;
use App\Scolarite\Application\Port\CurrentCalendarProvider;
use App\Scolarite\Application\Port\ScheduleDisplayReader;
use App\Scolarite\Application\Port\StudentClassReader;
use App\Scolarite\Application\Service\ScheduleResolver;
use App\Scolarite\Domain\Model\Schedule\ResolvedScheduleSlot;
use App\Shared\Domain\Tenant\TenantId;

use function array_map;
use function array_unique;
use function array_values;

use DateTimeImmutable;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler(bus: 'query.bus')]
final readonly class GetStudentScheduleHandler
{
    public function __construct(
        private StudentClassReader $studentClassReader,
        private ScheduleResolver $scheduleResolver,
        private CurrentCalendarProvider $calendarProvider,
        private ScheduleDisplayReader $displayReader,
    ) {
    }

    /** @return array<StudentScheduleSlotDto> */
    public function __invoke(GetStudentScheduleQuery $query): array
    {
        $tenantId = TenantId::fromString($query->tenantId);

        $classId = $this->studentClassReader->currentClassId($query->studentId, $tenantId);

        if ($classId === null) {
            return [];
        }

        $date = new DateTimeImmutable($query->date);
        $weekStart = $this->mondayOfWeek($date);

        $calendar = $this->calendarProvider->forCurrentYear($tenantId);

        $resolved = $this->scheduleResolver->resolveForWeek(
            ClassId::fromString($classId),
            $weekStart,
            $tenantId,
            $calendar,
        );

        return $this->enrichSlots($resolved, $query->tenantId);
    }

    /**
     * @param array<ResolvedScheduleSlot> $slots
     *
     * @return array<StudentScheduleSlotDto>
     */
    private function enrichSlots(array $slots, string $tenantId): array
    {
        if ($slots === []) {
            return [];
        }

        $subjectIds = array_values(array_unique(
            array_map(static fn (ResolvedScheduleSlot $s): string => (string) $s->subjectId, $slots),
        ));
        $teacherIds = array_values(array_unique(
            array_map(static fn (ResolvedScheduleSlot $s): string => (string) $s->teacherId, $slots),
        ));

        $subjects = $this->displayReader->subjectDisplay($tenantId, ...$subjectIds);
        $teacherNames = $this->displayReader->teacherNames($tenantId, ...$teacherIds);

        return array_map(
            static fn (ResolvedScheduleSlot $s): StudentScheduleSlotDto => StudentScheduleSlotDto::fromResolved(
                $s,
                $subjects[(string) $s->subjectId]['name'] ?? '',
                $subjects[(string) $s->subjectId]['color'] ?? null,
                $teacherNames[(string) $s->teacherId] ?? '',
            ),
            $slots,
        );
    }

    private function mondayOfWeek(DateTimeImmutable $date): DateTimeImmutable
    {
        $dayOfWeek = (int) $date->format('N');

        if ($dayOfWeek === 1) {
            return $date;
        }

        return $date->modify('-' . ($dayOfWeek - 1) . ' days');
    }
}
