<?php

declare(strict_types=1);

namespace App\Scolarite\Infrastructure\Api\Controller;

use App\Administration\Infrastructure\Security\SecurityUser;
use App\Scolarite\Application\Command\UploadHomeworkAttachment\UploadHomeworkAttachmentCommand;
use App\Scolarite\Application\Command\UploadHomeworkAttachment\UploadHomeworkAttachmentHandler;
use App\Scolarite\Application\Port\FileStorage;
use App\Scolarite\Domain\Exception\PieceJointeInvalideException;
use App\Scolarite\Domain\Model\Homework\HomeworkAttachment;
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_map;
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\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Attribute\Route;

final readonly class HomeworkAttachmentController
{
    public function __construct(
        private Security $security,
        private HomeworkRepository $homeworkRepository,
        private HomeworkAttachmentRepository $attachmentRepository,
        private UploadHomeworkAttachmentHandler $uploadHandler,
        private FileStorage $fileStorage,
        #[Autowire('%kernel.project_dir%/var/storage')]
        private string $storageDir,
    ) {
    }

    #[Route('/api/homework/{id}/attachments', name: 'api_homework_attachment_list', methods: ['GET'])]
    public function list(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é.');
        }

        if ((string) $homework->teacherId !== $user->userId()) {
            throw new AccessDeniedHttpException('Accès non autorisé.');
        }

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

        return new JsonResponse(array_map(
            static fn (HomeworkAttachment $a): array => [
                'id' => (string) $a->id,
                'filename' => $a->filename,
                'fileSize' => $a->fileSize,
                'mimeType' => $a->mimeType,
            ],
            $attachments,
        ));
    }

    #[Route('/api/homework/{id}/attachments', name: 'api_homework_attachment_upload', methods: ['POST'])]
    public function upload(string $id, Request $request): 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é.');
        }

        if ((string) $homework->teacherId !== $user->userId()) {
            throw new AccessDeniedHttpException('Seul le propriétaire peut ajouter des pièces jointes.');
        }

        $file = $request->files->get('file');

        if ($file === null) {
            throw new BadRequestHttpException('Aucun fichier envoyé.');
        }

        /** @var \Symfony\Component\HttpFoundation\File\UploadedFile $file */
        $originalName = $file->getClientOriginalName();
        $mimeType = $file->getMimeType() ?? $file->getClientMimeType() ?? '';
        $fileSize = $file->getSize();

        try {
            $attachment = ($this->uploadHandler)(new UploadHomeworkAttachmentCommand(
                tenantId: $user->tenantId(),
                homeworkId: $id,
                filename: $originalName,
                mimeType: $mimeType,
                fileSize: (int) $fileSize,
                tempFilePath: $file->getPathname(),
            ));

            $this->attachmentRepository->save($homework->id, $attachment);
        } catch (PieceJointeInvalideException $e) {
            throw new BadRequestHttpException($e->getMessage());
        }

        return new JsonResponse([
            'id' => (string) $attachment->id,
            'filename' => $attachment->filename,
            'fileSize' => $attachment->fileSize,
            'mimeType' => $attachment->mimeType,
        ], Response::HTTP_CREATED);
    }

    #[Route('/api/homework/{id}/attachments/{attachmentId}', name: 'api_homework_attachment_download', methods: ['GET'])]
    public function download(string $id, string $attachmentId): BinaryFileResponse
    {
        $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é.');
        }

        if ((string) $homework->teacherId !== $user->userId()) {
            throw new AccessDeniedHttpException('Accès non autorisé.');
        }

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

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

                if ($realPath === false || $realStorageDir === false || !str_starts_with($realPath, $realStorageDir)) {
                    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.');
    }

    #[Route('/api/homework/{id}/attachments/{attachmentId}', name: 'api_homework_attachment_delete', methods: ['DELETE'])]
    public function delete(string $id, string $attachmentId): Response
    {
        $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é.');
        }

        if ((string) $homework->teacherId !== $user->userId()) {
            throw new AccessDeniedHttpException('Seul le propriétaire peut supprimer des pièces jointes.');
        }

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

        foreach ($attachments as $attachment) {
            if ((string) $attachment->id === $attachmentId) {
                $this->fileStorage->delete($attachment->filePath);
                $this->attachmentRepository->delete($homework->id, $attachment);

                return new Response(status: Response::HTTP_NO_CONTENT);
            }
        }

        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;
    }
}
