<?php

declare(strict_types=1);

namespace App\Scolarite\Application\Query\GetChildrenHomework;

use App\Administration\Domain\Model\SchoolClass\ClassId;
use App\Scolarite\Application\Port\ParentChildrenReader;
use App\Scolarite\Application\Port\ScheduleDisplayReader;
use App\Scolarite\Application\Port\StudentClassReader;
use App\Scolarite\Application\Query\GetStudentHomework\StudentHomeworkDto;
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\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 GetChildrenHomeworkHandler
{
    public function __construct(
        private ParentChildrenReader $parentChildrenReader,
        private StudentClassReader $studentClassReader,
        private HomeworkRepository $homeworkRepository,
        private HomeworkAttachmentRepository $attachmentRepository,
        private ScheduleDisplayReader $displayReader,
    ) {
    }

    /** @return array<ChildHomeworkDto> */
    public function __invoke(GetChildrenHomeworkQuery $query): array
    {
        $tenantId = TenantId::fromString($query->tenantId);
        $allChildren = $this->parentChildrenReader->childrenOf($query->parentId, $tenantId);

        if ($allChildren === []) {
            return [];
        }

        $children = $query->childId !== null
            ? array_values(array_filter($allChildren, static fn (array $c): bool => $c['studentId'] === $query->childId))
            : $allChildren;

        if ($children === []) {
            return [];
        }

        $result = [];

        foreach ($children as $child) {
            $classId = $this->studentClassReader->currentClassId($child['studentId'], $tenantId);

            if ($classId === null) {
                $result[] = new ChildHomeworkDto(
                    childId: $child['studentId'],
                    firstName: $child['firstName'],
                    lastName: $child['lastName'],
                    homework: [],
                );

                continue;
            }

            $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);

            $enriched = $this->enrichHomeworks($homeworks, $query->tenantId);

            $result[] = new ChildHomeworkDto(
                childId: $child['studentId'],
                firstName: $child['firstName'],
                lastName: $child['lastName'],
                homework: $enriched,
            );
        }

        return $result;
    }

    /**
     * @param array<Homework> $homeworks
     *
     * @return array<StudentHomeworkDto>
     */
    private function enrichHomeworks(array $homeworks, 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);

        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,
            ),
            $homeworks,
        );
    }
}
