<?php

declare(strict_types=1);

namespace App\Scolarite\Infrastructure\Api\Controller;

use App\Administration\Infrastructure\Security\SecurityUser;
use App\Scolarite\Application\Query\GetChildrenHomework\ChildHomeworkDto;
use App\Scolarite\Application\Query\GetChildrenHomework\GetChildrenHomeworkDetailHandler;
use App\Scolarite\Application\Query\GetChildrenHomework\GetChildrenHomeworkHandler;
use App\Scolarite\Application\Query\GetChildrenHomework\GetChildrenHomeworkQuery;
use App\Scolarite\Application\Query\GetStudentHomework\StudentHomeworkDetailDto;
use App\Scolarite\Application\Query\GetStudentHomework\StudentHomeworkDto;
use App\Scolarite\Domain\Model\Homework\HomeworkId;
use App\Scolarite\Domain\Repository\HomeworkAttachmentRepository;
use App\Scolarite\Domain\Repository\HomeworkRepository;
use App\Scolarite\Infrastructure\Security\HomeworkParentVoter;
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;

/**
 * Endpoints de consultation des devoirs des enfants pour le parent connecté.
 */
#[IsGranted(HomeworkParentVoter::VIEW)]
final readonly class ParentHomeworkController
{
    public function __construct(
        private Security $security,
        private GetChildrenHomeworkHandler $handler,
        private GetChildrenHomeworkDetailHandler $detailHandler,
        private HomeworkRepository $homeworkRepository,
        private HomeworkAttachmentRepository $attachmentRepository,
        #[Autowire('%kernel.project_dir%/var/storage')]
        private string $uploadsDir,
    ) {
    }

    /**
     * Devoirs d'un enfant spécifique.
     */
    #[Route('/api/me/children/{childId}/homework', name: 'api_parent_child_homework', methods: ['GET'])]
    public function childHomework(string $childId, Request $request): JsonResponse
    {
        $user = $this->getSecurityUser();
        $subjectId = $request->query->get('subjectId');

        $children = ($this->handler)(new GetChildrenHomeworkQuery(
            parentId: $user->userId(),
            tenantId: $user->tenantId(),
            childId: $childId,
            subjectId: is_string($subjectId) && $subjectId !== '' ? $subjectId : null,
        ));

        if ($children === []) {
            throw new NotFoundHttpException('Enfant non trouvé ou non lié à ce parent.');
        }

        return new JsonResponse([
            'data' => $this->serializeChild($children[0]),
        ]);
    }

    /**
     * Vue consolidée des devoirs de tous les enfants.
     */
    #[Route('/api/me/children/homework', name: 'api_parent_children_homework', methods: ['GET'])]
    public function allChildrenHomework(Request $request): JsonResponse
    {
        $user = $this->getSecurityUser();
        $subjectId = $request->query->get('subjectId');

        $children = ($this->handler)(new GetChildrenHomeworkQuery(
            parentId: $user->userId(),
            tenantId: $user->tenantId(),
            subjectId: is_string($subjectId) && $subjectId !== '' ? $subjectId : null,
        ));

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

    /**
     * Détail d'un devoir (accessible si l'enfant du parent est dans la classe du devoir).
     */
    #[Route('/api/me/children/homework/{id}', name: 'api_parent_child_homework_detail', methods: ['GET'])]
    public function homeworkDetail(string $id): JsonResponse
    {
        $user = $this->getSecurityUser();

        $detail = ($this->detailHandler)($user->userId(), $user->tenantId(), $id);

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

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

    /**
     * Téléchargement d'une pièce jointe (parent).
     */
    #[Route('/api/me/children/homework/{homeworkId}/attachments/{attachmentId}', name: 'api_parent_child_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é.');
        }

        // Verify parent has a child in the homework's class
        $detail = ($this->detailHandler)($user->userId(), $user->tenantId(), $homeworkId);

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

        $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 getSecurityUser(): SecurityUser
    {
        $user = $this->security->getUser();

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

        return $user;
    }

    /**
     * @return array<string, mixed>
     */
    private function serializeChild(ChildHomeworkDto $child): array
    {
        return [
            'childId' => $child->childId,
            'firstName' => $child->firstName,
            'lastName' => $child->lastName,
            'homework' => array_map($this->serializeHomework(...), $child->homework),
        ];
    }

    /**
     * @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,
            ),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function serializeHomework(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,
        ];
    }
}
