<?php

declare(strict_types=1);

namespace App\Scolarite\Infrastructure\Api\Controller;

use App\Administration\Infrastructure\Security\SecurityUser;
use App\Scolarite\Application\Port\ScheduleDisplayReader;
use App\Scolarite\Application\Port\StudentClassReader;
use App\Scolarite\Application\Query\GetStudentHomework\GetStudentHomeworkHandler;
use App\Scolarite\Application\Query\GetStudentHomework\GetStudentHomeworkQuery;
use App\Scolarite\Application\Query\GetStudentHomework\StudentHomeworkDetailDto;
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\Scolarite\Infrastructure\Security\HomeworkStudentVoter;
use App\Shared\Domain\Tenant\TenantId;

use function array_map;
use function is_string;
use function realpath;
use function str_starts_with;

use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[IsGranted(HomeworkStudentVoter::VIEW)]
final readonly class StudentHomeworkController
{
    public function __construct(
        private Security $security,
        private GetStudentHomeworkHandler $handler,
        private HomeworkRepository $homeworkRepository,
        private HomeworkAttachmentRepository $attachmentRepository,
        private ScheduleDisplayReader $displayReader,
        private StudentClassReader $studentClassReader,
        #[Autowire('%kernel.project_dir%/var/storage')]
        private string $uploadsDir,
    ) {
    }

    #[Route('/api/me/homework', name: 'api_student_homework_list', methods: ['GET'])]
    public function list(Request $request): JsonResponse
    {
        $user = $this->getSecurityUser();

        $subjectId = $request->query->get('subjectId');

        $result = ($this->handler)(new GetStudentHomeworkQuery(
            studentId: $user->userId(),
            tenantId: $user->tenantId(),
            subjectId: is_string($subjectId) && $subjectId !== '' ? $subjectId : null,
        ));

        return new JsonResponse([
            'data' => array_map($this->serializeListItem(...), $result),
        ]);
    }

    #[Route('/api/me/homework/{id}', name: 'api_student_homework_detail', methods: ['GET'])]
    public function detail(string $id): JsonResponse
    {
        $user = $this->getSecurityUser();
        $tenantId = TenantId::fromString($user->tenantId());

        $homework = $this->homeworkRepository->findById(HomeworkId::fromString($id), $tenantId);

        if ($homework === null) {
            throw new NotFoundHttpException('Devoir non trouvé.');
        }

        $this->assertHomeworkBelongsToStudentClass($user, $homework);

        $attachments = $this->attachmentRepository->findByHomeworkId($homework->id);

        $subjects = $this->displayReader->subjectDisplay($user->tenantId(), (string) $homework->subjectId);
        $teacherNames = $this->displayReader->teacherNames($user->tenantId(), (string) $homework->teacherId);

        $detail = StudentHomeworkDetailDto::fromDomain(
            $homework,
            $subjects[(string) $homework->subjectId]['name'] ?? '',
            $subjects[(string) $homework->subjectId]['color'] ?? null,
            $teacherNames[(string) $homework->teacherId] ?? '',
            $attachments,
        );

        return new JsonResponse(['data' => $this->serializeDetail($detail)]);
    }

    #[Route('/api/me/homework/{homeworkId}/attachments/{attachmentId}', name: 'api_student_homework_attachment', methods: ['GET'])]
    public function downloadAttachment(string $homeworkId, string $attachmentId): BinaryFileResponse
    {
        $user = $this->getSecurityUser();
        $tenantId = TenantId::fromString($user->tenantId());

        $homework = $this->homeworkRepository->findById(HomeworkId::fromString($homeworkId), $tenantId);

        if ($homework === null) {
            throw new NotFoundHttpException('Devoir non trouvé.');
        }

        $this->assertHomeworkBelongsToStudentClass($user, $homework);

        $attachments = $this->attachmentRepository->findByHomeworkId($homework->id);

        foreach ($attachments as $attachment) {
            if ((string) $attachment->id === $attachmentId) {
                $fullPath = $this->uploadsDir . '/' . $attachment->filePath;
                $realPath = realpath($fullPath);
                $realUploadsDir = realpath($this->uploadsDir);

                if ($realPath === false || $realUploadsDir === false || !str_starts_with($realPath, $realUploadsDir)) {
                    throw new NotFoundHttpException('Pièce jointe non trouvée.');
                }

                $response = new BinaryFileResponse($realPath);
                $response->setContentDisposition(
                    ResponseHeaderBag::DISPOSITION_INLINE,
                    $attachment->filename,
                );

                return $response;
            }
        }

        throw new NotFoundHttpException('Pièce jointe non trouvée.');
    }

    private function assertHomeworkBelongsToStudentClass(SecurityUser $user, Homework $homework): void
    {
        $classId = $this->studentClassReader->currentClassId(
            $user->userId(),
            TenantId::fromString($user->tenantId()),
        );

        if ($classId === null || (string) $homework->classId !== $classId) {
            throw new NotFoundHttpException('Devoir non trouvé.');
        }
    }

    private function getSecurityUser(): SecurityUser
    {
        $user = $this->security->getUser();

        if (!$user instanceof SecurityUser) {
            throw new AccessDeniedHttpException('Authentification requise.');
        }

        return $user;
    }

    /**
     * @return array<string, mixed>
     */
    private function serializeListItem(StudentHomeworkDto $dto): array
    {
        return [
            'id' => $dto->id,
            'subjectId' => $dto->subjectId,
            'subjectName' => $dto->subjectName,
            'subjectColor' => $dto->subjectColor,
            'teacherId' => $dto->teacherId,
            'teacherName' => $dto->teacherName,
            'title' => $dto->title,
            'description' => $dto->description,
            'dueDate' => $dto->dueDate,
            'createdAt' => $dto->createdAt,
            'hasAttachments' => $dto->hasAttachments,
            'submissionStatus' => $dto->submissionStatus,
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function serializeDetail(StudentHomeworkDetailDto $dto): array
    {
        return [
            'id' => $dto->id,
            'subjectId' => $dto->subjectId,
            'subjectName' => $dto->subjectName,
            'subjectColor' => $dto->subjectColor,
            'teacherId' => $dto->teacherId,
            'teacherName' => $dto->teacherName,
            'title' => $dto->title,
            'description' => $dto->description,
            'dueDate' => $dto->dueDate,
            'createdAt' => $dto->createdAt,
            'attachments' => array_map(
                static fn ($a): array => [
                    'id' => $a->id,
                    'filename' => $a->filename,
                    'fileSize' => $a->fileSize,
                    'mimeType' => $a->mimeType,
                ],
                $dto->attachments,
            ),
        ];
    }
}
