<?php

declare(strict_types=1);

namespace App\Scolarite\Infrastructure\Api\Controller;

use App\Administration\Infrastructure\Security\SecurityUser;
use App\Scolarite\Application\Port\ClassStudentsReader;
use App\Scolarite\Domain\Model\Homework\HomeworkId;
use App\Scolarite\Domain\Model\HomeworkSubmission\HomeworkSubmission;
use App\Scolarite\Domain\Model\HomeworkSubmission\HomeworkSubmissionId;
use App\Scolarite\Domain\Model\HomeworkSubmission\SubmissionAttachment;
use App\Scolarite\Domain\Repository\HomeworkRepository;
use App\Scolarite\Domain\Repository\HomeworkSubmissionRepository;
use App\Scolarite\Domain\Repository\SubmissionAttachmentRepository;
use App\Shared\Domain\Tenant\TenantId;

use function array_column;
use function array_diff;
use function array_filter;
use function array_map;
use function array_values;
use function count;

use DateTimeImmutable;

use function in_array;
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\ResponseHeaderBag;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Attribute\Route;

final readonly class TeacherSubmissionController
{
    public function __construct(
        private Security $security,
        private HomeworkRepository $homeworkRepository,
        private HomeworkSubmissionRepository $submissionRepository,
        private SubmissionAttachmentRepository $attachmentRepository,
        private ClassStudentsReader $classStudentsReader,
        #[Autowire('%kernel.project_dir%/var/storage')]
        private string $storageDir,
    ) {
    }

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

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

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

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

        $submissions = $this->submissionRepository->findByHomework($homeworkId, $tenantId);
        $students = $this->classStudentsReader->studentsInClass((string) $homework->classId, $tenantId);

        $studentNameMap = [];
        foreach ($students as $student) {
            /** @var string $studentId */
            $studentId = $student['id'];
            /** @var string $studentName */
            $studentName = $student['name'];
            $studentNameMap[$studentId] = $studentName;
        }

        $submittedStudentIds = array_map(
            static fn (HomeworkSubmission $s): string => (string) $s->studentId,
            $submissions,
        );

        $rows = array_map(
            static fn (HomeworkSubmission $s): array => [
                'id' => (string) $s->id,
                'studentId' => (string) $s->studentId,
                'studentName' => $studentNameMap[(string) $s->studentId] ?? '',
                'status' => $s->status->value,
                'submittedAt' => $s->submittedAt?->format(DateTimeImmutable::ATOM),
                'createdAt' => $s->createdAt->format(DateTimeImmutable::ATOM),
            ],
            $submissions,
        );

        foreach ($students as $student) {
            /** @var string $sId */
            $sId = $student['id'];

            if (!in_array($sId, $submittedStudentIds, true)) {
                /** @var string $sName */
                $sName = $student['name'];
                $rows[] = [
                    'id' => null,
                    'studentId' => $sId,
                    'studentName' => $sName,
                    'status' => 'not_submitted',
                    'submittedAt' => null,
                    'createdAt' => null,
                ];
            }
        }

        return new JsonResponse(['data' => $rows]);
    }

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

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

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

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

        $submissions = $this->submissionRepository->findByHomework($homeworkId, $tenantId);
        $students = $this->classStudentsReader->studentsInClass((string) $homework->classId, $tenantId);

        $submittedStudentIds = array_map(
            static fn (HomeworkSubmission $s): string => (string) $s->studentId,
            array_filter(
                $submissions,
                static fn (HomeworkSubmission $s): bool => !$s->status->estModifiable(),
            ),
        );

        $allStudentIds = array_column($students, 'id');
        $missingStudentIds = array_diff($allStudentIds, $submittedStudentIds);

        $studentNameMap = [];
        foreach ($students as $student) {
            /** @var string $sId */
            $sId = $student['id'];
            /** @var string $sName */
            $sName = $student['name'];
            $studentNameMap[$sId] = $sName;
        }

        $missingStudents = array_values(array_map(
            static fn (string $studentId): array => [
                'id' => $studentId,
                'name' => $studentNameMap[$studentId] ?? '',
            ],
            array_filter(
                $missingStudentIds,
                static fn (string $studentId): bool => !in_array($studentId, $submittedStudentIds, true),
            ),
        ));

        return new JsonResponse([
            'data' => [
                'totalStudents' => count($students),
                'submittedCount' => count($submittedStudentIds),
                'missingStudents' => $missingStudents,
            ],
        ]);
    }

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

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

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

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

        $submission = $this->submissionRepository->findById(
            HomeworkSubmissionId::fromString($submissionId),
            $tenantId,
        );

        if ($submission === null || !$submission->homeworkId->equals($homeworkId)) {
            throw new NotFoundHttpException('Rendu non trouvé.');
        }

        $attachments = $this->attachmentRepository->findBySubmissionId($submission->id);
        $students = $this->classStudentsReader->studentsInClass((string) $homework->classId, $tenantId);

        $studentName = '';
        foreach ($students as $student) {
            if ($student['id'] === (string) $submission->studentId) {
                /** @var string $name */
                $name = $student['name'];
                $studentName = $name;

                break;
            }
        }

        return new JsonResponse([
            'data' => [
                'id' => (string) $submission->id,
                'studentId' => (string) $submission->studentId,
                'studentName' => $studentName,
                'responseHtml' => $submission->responseHtml,
                'status' => $submission->status->value,
                'submittedAt' => $submission->submittedAt?->format(DateTimeImmutable::ATOM),
                'createdAt' => $submission->createdAt->format(DateTimeImmutable::ATOM),
                'attachments' => array_map(
                    static fn (SubmissionAttachment $a): array => [
                        'id' => (string) $a->id,
                        'filename' => $a->filename,
                        'fileSize' => $a->fileSize,
                        'mimeType' => $a->mimeType,
                    ],
                    $attachments,
                ),
            ],
        ]);
    }

    #[Route('/api/homework/{homeworkId}/submissions/{submissionId}/attachments/{attachmentId}', name: 'api_teacher_submission_attachment_download', methods: ['GET'])]
    public function downloadAttachment(string $homeworkId, string $submissionId, 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é.');
        }

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

        $submission = $this->submissionRepository->findById(
            HomeworkSubmissionId::fromString($submissionId),
            $tenantId,
        );

        if ($submission === null || !$submission->homeworkId->equals(HomeworkId::fromString($homeworkId))) {
            throw new NotFoundHttpException('Rendu non trouvé.');
        }

        $attachments = $this->attachmentRepository->findBySubmissionId($submission->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.');
    }

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

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

        return $user;
    }
}
