<?php

declare(strict_types=1);

namespace App\Tests\Unit\Scolarite\Application\Query\GetStudentHomework;

use App\Administration\Domain\Model\SchoolClass\ClassId;
use App\Administration\Domain\Model\Subject\SubjectId;
use App\Administration\Domain\Model\User\UserId;
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\Domain\Model\Homework\Homework;
use App\Scolarite\Domain\Model\Homework\HomeworkAttachment;
use App\Scolarite\Domain\Model\Homework\HomeworkAttachmentId;
use App\Scolarite\Domain\Model\HomeworkSubmission\HomeworkSubmission;
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkAttachmentRepository;
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkRepository;
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkSubmissionRepository;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class GetStudentHomeworkHandlerTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
    private const string STUDENT_ID = '550e8400-e29b-41d4-a716-446655440050';
    private const string CLASS_ID = '550e8400-e29b-41d4-a716-446655440020';
    private const string SUBJECT_MATH_ID = '550e8400-e29b-41d4-a716-446655440030';
    private const string SUBJECT_FRENCH_ID = '550e8400-e29b-41d4-a716-446655440031';
    private const string TEACHER_ID = '550e8400-e29b-41d4-a716-446655440010';

    private InMemoryHomeworkRepository $homeworkRepository;
    private InMemoryHomeworkAttachmentRepository $attachmentRepository;
    private InMemoryHomeworkSubmissionRepository $submissionRepository;

    protected function setUp(): void
    {
        $this->homeworkRepository = new InMemoryHomeworkRepository();
        $this->attachmentRepository = new InMemoryHomeworkAttachmentRepository();
        $this->submissionRepository = new InMemoryHomeworkSubmissionRepository();
    }

    #[Test]
    public function itReturnsEmptyWhenStudentHasNoClass(): void
    {
        $handler = $this->createHandler(classId: null);

        $result = $handler(new GetStudentHomeworkQuery(
            studentId: self::STUDENT_ID,
            tenantId: self::TENANT_ID,
        ));

        self::assertSame([], $result);
    }

    #[Test]
    public function itReturnsHomeworkForStudentClass(): void
    {
        $this->givenHomework(title: 'Exercices chapitre 5', dueDate: '2026-04-15');
        $handler = $this->createHandler();

        $result = $handler(new GetStudentHomeworkQuery(
            studentId: self::STUDENT_ID,
            tenantId: self::TENANT_ID,
        ));

        self::assertCount(1, $result);
        self::assertSame('Exercices chapitre 5', $result[0]->title);
        self::assertSame('2026-04-15', $result[0]->dueDate);
        self::assertSame('Mathématiques', $result[0]->subjectName);
        self::assertSame('#3b82f6', $result[0]->subjectColor);
        self::assertSame('Jean Dupont', $result[0]->teacherName);
    }

    #[Test]
    public function itSortsByDueDateAscending(): void
    {
        $this->givenHomework(title: 'Devoir lointain', dueDate: '2026-05-20');
        $this->givenHomework(title: 'Devoir proche', dueDate: '2026-04-10');
        $this->givenHomework(title: 'Devoir milieu', dueDate: '2026-04-25');

        $handler = $this->createHandler();

        $result = $handler(new GetStudentHomeworkQuery(
            studentId: self::STUDENT_ID,
            tenantId: self::TENANT_ID,
        ));

        self::assertCount(3, $result);
        self::assertSame('Devoir proche', $result[0]->title);
        self::assertSame('Devoir milieu', $result[1]->title);
        self::assertSame('Devoir lointain', $result[2]->title);
    }

    #[Test]
    public function itFiltersDeletedHomework(): void
    {
        $homework = $this->givenHomework(title: 'Devoir supprimé', dueDate: '2026-04-15');
        $homework->supprimer(new DateTimeImmutable('2026-03-20'));
        $this->homeworkRepository->save($homework);

        $this->givenHomework(title: 'Devoir actif', dueDate: '2026-04-20');

        $handler = $this->createHandler();

        $result = $handler(new GetStudentHomeworkQuery(
            studentId: self::STUDENT_ID,
            tenantId: self::TENANT_ID,
        ));

        self::assertCount(1, $result);
        self::assertSame('Devoir actif', $result[0]->title);
    }

    #[Test]
    public function itFiltersBySubjectWhenProvided(): void
    {
        $this->givenHomework(title: 'Maths', dueDate: '2026-04-15', subjectId: self::SUBJECT_MATH_ID);
        $this->givenHomework(title: 'Français', dueDate: '2026-04-16', subjectId: self::SUBJECT_FRENCH_ID);

        $handler = $this->createHandler();

        $result = $handler(new GetStudentHomeworkQuery(
            studentId: self::STUDENT_ID,
            tenantId: self::TENANT_ID,
            subjectId: self::SUBJECT_MATH_ID,
        ));

        self::assertCount(1, $result);
        self::assertSame('Maths', $result[0]->title);
    }

    #[Test]
    public function itExcludesHomeworkFromOtherClasses(): void
    {
        $this->givenHomework(title: 'Mon devoir', dueDate: '2026-04-15', classId: self::CLASS_ID);

        $otherClassId = '550e8400-e29b-41d4-a716-446655440099';
        $this->givenHomework(title: 'Autre classe', dueDate: '2026-04-16', classId: $otherClassId);

        $handler = $this->createHandler();

        $result = $handler(new GetStudentHomeworkQuery(
            studentId: self::STUDENT_ID,
            tenantId: self::TENANT_ID,
        ));

        self::assertCount(1, $result);
        self::assertSame('Mon devoir', $result[0]->title);
    }

    #[Test]
    public function itIndicatesWhenHomeworkHasAttachments(): void
    {
        $homework = $this->givenHomework(title: 'Avec pièce jointe', dueDate: '2026-04-15');

        $attachment = new HomeworkAttachment(
            id: HomeworkAttachmentId::generate(),
            filename: 'exercice.pdf',
            filePath: '/uploads/exercice.pdf',
            fileSize: 1024,
            mimeType: 'application/pdf',
            uploadedAt: new DateTimeImmutable('2026-03-12'),
        );
        $this->attachmentRepository->save($homework->id, $attachment);

        $handler = $this->createHandler();

        $result = $handler(new GetStudentHomeworkQuery(
            studentId: self::STUDENT_ID,
            tenantId: self::TENANT_ID,
        ));

        self::assertCount(1, $result);
        self::assertTrue($result[0]->hasAttachments);
    }

    #[Test]
    public function itIndicatesWhenHomeworkHasNoAttachments(): void
    {
        $this->givenHomework(title: 'Sans pièce jointe', dueDate: '2026-04-15');

        $handler = $this->createHandler();

        $result = $handler(new GetStudentHomeworkQuery(
            studentId: self::STUDENT_ID,
            tenantId: self::TENANT_ID,
        ));

        self::assertCount(1, $result);
        self::assertFalse($result[0]->hasAttachments);
    }

    #[Test]
    public function itReturnsNullSubmissionStatusWhenNoSubmission(): void
    {
        $handler = $this->createHandler();
        $this->givenHomework('Devoir sans rendu', '2026-04-15');

        $result = $handler(new GetStudentHomeworkQuery(
            studentId: self::STUDENT_ID,
            tenantId: self::TENANT_ID,
        ));

        self::assertCount(1, $result);
        self::assertNull($result[0]->submissionStatus);
    }

    #[Test]
    public function itReturnsSubmissionStatusWhenDraftExists(): void
    {
        $handler = $this->createHandler();
        $homework = $this->givenHomework('Devoir avec brouillon', '2026-04-15');

        $submission = HomeworkSubmission::creerBrouillon(
            tenantId: TenantId::fromString(self::TENANT_ID),
            homeworkId: $homework->id,
            studentId: UserId::fromString(self::STUDENT_ID),
            responseHtml: '<p>Brouillon</p>',
            now: new DateTimeImmutable('2026-03-24 10:00:00'),
        );
        $this->submissionRepository->save($submission);

        $result = $handler(new GetStudentHomeworkQuery(
            studentId: self::STUDENT_ID,
            tenantId: self::TENANT_ID,
        ));

        self::assertCount(1, $result);
        self::assertSame('draft', $result[0]->submissionStatus);
    }

    #[Test]
    public function itReturnsSubmittedStatusAfterSubmission(): void
    {
        $handler = $this->createHandler();
        $homework = $this->givenHomework('Devoir soumis', '2026-04-15');

        $submission = HomeworkSubmission::creerBrouillon(
            tenantId: TenantId::fromString(self::TENANT_ID),
            homeworkId: $homework->id,
            studentId: UserId::fromString(self::STUDENT_ID),
            responseHtml: '<p>Réponse</p>',
            now: new DateTimeImmutable('2026-03-24 10:00:00'),
        );
        $submission->soumettre(
            dueDate: new DateTimeImmutable('2026-04-15'),
            now: new DateTimeImmutable('2026-03-24 11:00:00'),
        );
        $this->submissionRepository->save($submission);

        $result = $handler(new GetStudentHomeworkQuery(
            studentId: self::STUDENT_ID,
            tenantId: self::TENANT_ID,
        ));

        self::assertCount(1, $result);
        self::assertSame('submitted', $result[0]->submissionStatus);
    }

    private function createHandler(?string $classId = self::CLASS_ID): GetStudentHomeworkHandler
    {
        $studentClassReader = new class($classId) implements StudentClassReader {
            public function __construct(private readonly ?string $classId)
            {
            }

            public function currentClassId(string $studentId, TenantId $tenantId): ?string
            {
                return $this->classId;
            }
        };

        $displayReader = new class implements ScheduleDisplayReader {
            public function subjectDisplay(string $tenantId, string ...$subjectIds): array
            {
                $map = [];

                foreach ($subjectIds as $id) {
                    $map[$id] = ['name' => 'Mathématiques', 'color' => '#3b82f6'];
                }

                return $map;
            }

            public function teacherNames(string $tenantId, string ...$teacherIds): array
            {
                $map = [];

                foreach ($teacherIds as $id) {
                    $map[$id] = 'Jean Dupont';
                }

                return $map;
            }
        };

        return new GetStudentHomeworkHandler(
            $studentClassReader,
            $this->homeworkRepository,
            $this->attachmentRepository,
            $this->submissionRepository,
            $displayReader,
        );
    }

    private function givenHomework(
        string $title,
        string $dueDate,
        string $classId = self::CLASS_ID,
        string $subjectId = self::SUBJECT_MATH_ID,
    ): Homework {
        $homework = Homework::creer(
            tenantId: TenantId::fromString(self::TENANT_ID),
            classId: ClassId::fromString($classId),
            subjectId: SubjectId::fromString($subjectId),
            teacherId: UserId::fromString(self::TEACHER_ID),
            title: $title,
            description: null,
            dueDate: new DateTimeImmutable($dueDate),
            now: new DateTimeImmutable('2026-03-12 10:00:00'),
        );

        $this->homeworkRepository->save($homework);

        return $homework;
    }
}
