<?php

declare(strict_types=1);

namespace App\Scolarite\Application\Query\GetStudentHomework;

use App\Administration\Domain\Model\SchoolClass\ClassId;
use App\Administration\Domain\Model\User\UserId;
use App\Scolarite\Application\Port\ScheduleDisplayReader;
use App\Scolarite\Application\Port\StudentClassReader;
use App\Scolarite\Domain\Model\Homework\Homework;
use App\Scolarite\Domain\Model\Homework\HomeworkId;
use App\Scolarite\Domain\Repository\HomeworkAttachmentRepository;
use App\Scolarite\Domain\Repository\HomeworkRepository;
use App\Scolarite\Domain\Repository\HomeworkSubmissionRepository;
use App\Shared\Domain\Tenant\TenantId;

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

use Symfony\Component\Messenger\Attribute\AsMessageHandler;

use function usort;

#[AsMessageHandler(bus: 'query.bus')]
final readonly class GetStudentHomeworkHandler
{
    public function __construct(
        private StudentClassReader $studentClassReader,
        private HomeworkRepository $homeworkRepository,
        private HomeworkAttachmentRepository $attachmentRepository,
        private HomeworkSubmissionRepository $submissionRepository,
        private ScheduleDisplayReader $displayReader,
    ) {
    }

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

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

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

        $homeworks = $this->homeworkRepository->findByClass(ClassId::fromString($classId), $tenantId);

        if ($query->subjectId !== null) {
            $filterSubjectId = $query->subjectId;
            $homeworks = array_values(array_filter(
                $homeworks,
                static fn (Homework $h): bool => (string) $h->subjectId === $filterSubjectId,
            ));
        }

        usort($homeworks, static fn (Homework $a, Homework $b): int => $a->dueDate <=> $b->dueDate);

        return $this->enrichHomeworks($homeworks, $query->studentId, $query->tenantId);
    }

    /**
     * @param array<Homework> $homeworks
     *
     * @return array<StudentHomeworkDto>
     */
    private function enrichHomeworks(array $homeworks, string $studentId, string $tenantId): array
    {
        if ($homeworks === []) {
            return [];
        }

        $subjectIds = array_values(array_unique(
            array_map(static fn (Homework $h): string => (string) $h->subjectId, $homeworks),
        ));
        $teacherIds = array_values(array_unique(
            array_map(static fn (Homework $h): string => (string) $h->teacherId, $homeworks),
        ));

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

        $homeworkIds = array_map(static fn (Homework $h): HomeworkId => $h->id, $homeworks);
        $attachmentMap = $this->attachmentRepository->hasAttachments(...$homeworkIds);

        $studentUserId = UserId::fromString($studentId);
        $tenantIdObj = TenantId::fromString($tenantId);
        $submissionStatusMap = $this->submissionRepository->findStatusesByStudent(
            $studentUserId,
            $tenantIdObj,
            ...$homeworkIds,
        );

        return array_map(
            static fn (Homework $h): StudentHomeworkDto => StudentHomeworkDto::fromDomain(
                $h,
                $subjects[(string) $h->subjectId]['name'] ?? '',
                $subjects[(string) $h->subjectId]['color'] ?? null,
                $teacherNames[(string) $h->teacherId] ?? '',
                $attachmentMap[(string) $h->id] ?? false,
                $submissionStatusMap[(string) $h->id] ?? null,
            ),
            $homeworks,
        );
    }
}
