<?php

declare(strict_types=1);

namespace App\Administration\Application\Query\GetAllAssignments;

use App\Administration\Application\Dto\PaginatedResult;
use App\Administration\Domain\Repository\ClassRepository;
use App\Administration\Domain\Repository\SubjectRepository;
use App\Administration\Domain\Repository\TeacherAssignmentRepository;
use App\Administration\Domain\Repository\UserRepository;
use App\Shared\Domain\Tenant\TenantId;

use function array_slice;
use function count;

use Psr\Log\LoggerInterface;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler(bus: 'query.bus')]
final readonly class GetAllAssignmentsHandler
{
    public function __construct(
        private TeacherAssignmentRepository $assignmentRepository,
        private UserRepository $userRepository,
        private ClassRepository $classRepository,
        private SubjectRepository $subjectRepository,
        private LoggerInterface $logger,
    ) {
    }

    /**
     * @return PaginatedResult<AssignmentWithNamesDto>
     */
    public function __invoke(GetAllAssignmentsQuery $query): PaginatedResult
    {
        $tenantId = TenantId::fromString($query->tenantId);

        $assignments = $this->assignmentRepository->findAllActiveByTenant($tenantId);

        // Build lookup maps for users, classes, and subjects
        $users = $this->userRepository->findAllByTenant($tenantId);
        /** @var array<string, array{firstName: string, lastName: string}> $userNames */
        $userNames = [];
        foreach ($users as $user) {
            $userNames[(string) $user->id] = [
                'firstName' => $user->firstName,
                'lastName' => $user->lastName,
            ];
        }

        $classes = $this->classRepository->findAllActiveByTenant($tenantId);
        /** @var array<string, string> $classNames */
        $classNames = [];
        foreach ($classes as $class) {
            $classNames[(string) $class->id] = (string) $class->name;
        }

        $subjects = $this->subjectRepository->findAllActiveByTenant($tenantId);
        /** @var array<string, string> $subjectNames */
        $subjectNames = [];
        foreach ($subjects as $subject) {
            $subjectNames[(string) $subject->id] = (string) $subject->name;
        }

        $dtos = [];
        foreach ($assignments as $assignment) {
            $teacherId = (string) $assignment->teacherId;
            $classId = (string) $assignment->classId;
            $subjectId = (string) $assignment->subjectId;

            if (!isset($userNames[$teacherId])) {
                $this->logger->warning('Assignment {assignmentId} references unknown teacher {teacherId}', [
                    'assignmentId' => (string) $assignment->id,
                    'teacherId' => $teacherId,
                ]);
            }

            if (!isset($classNames[$classId])) {
                $this->logger->warning('Assignment {assignmentId} references unknown class {classId}', [
                    'assignmentId' => (string) $assignment->id,
                    'classId' => $classId,
                ]);
            }

            if (!isset($subjectNames[$subjectId])) {
                $this->logger->warning('Assignment {assignmentId} references unknown subject {subjectId}', [
                    'assignmentId' => (string) $assignment->id,
                    'subjectId' => $subjectId,
                ]);
            }

            $teacher = $userNames[$teacherId] ?? ['firstName' => '', 'lastName' => ''];

            $dtos[] = new AssignmentWithNamesDto(
                id: (string) $assignment->id,
                teacherId: $teacherId,
                teacherFirstName: $teacher['firstName'],
                teacherLastName: $teacher['lastName'],
                classId: $classId,
                className: $classNames[$classId] ?? '',
                subjectId: $subjectId,
                subjectName: $subjectNames[$subjectId] ?? '',
                academicYearId: (string) $assignment->academicYearId,
                status: $assignment->status->value,
                startDate: $assignment->startDate,
                endDate: $assignment->endDate,
                createdAt: $assignment->createdAt,
            );
        }

        if ($query->search !== null && $query->search !== '') {
            $searchLower = mb_strtolower($query->search);
            $dtos = array_values(array_filter(
                $dtos,
                static fn (AssignmentWithNamesDto $dto) => str_contains(mb_strtolower($dto->teacherFirstName), $searchLower)
                    || str_contains(mb_strtolower($dto->teacherLastName), $searchLower)
                    || str_contains(mb_strtolower($dto->className), $searchLower)
                    || str_contains(mb_strtolower($dto->subjectName), $searchLower),
            ));
        }

        $total = count($dtos);
        $offset = ($query->page - 1) * $query->limit;
        $items = array_slice($dtos, $offset, $query->limit);

        return new PaginatedResult(
            items: $items,
            total: $total,
            page: $query->page,
            limit: $query->limit,
        );
    }
}
