<?php

declare(strict_types=1);

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

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\ParentChildrenReader;
use App\Scolarite\Application\Port\ScheduleDisplayReader;
use App\Scolarite\Application\Port\StudentClassReader;
use App\Scolarite\Application\Query\GetChildrenHomework\GetChildrenHomeworkDetailHandler;
use App\Scolarite\Domain\Model\Homework\Homework;
use App\Scolarite\Domain\Model\Homework\HomeworkAttachment;
use App\Scolarite\Domain\Model\Homework\HomeworkAttachmentId;
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkAttachmentRepository;
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkRepository;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class GetChildrenHomeworkDetailHandlerTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
    private const string PARENT_ID = '550e8400-e29b-41d4-a716-446655440060';
    private const string CHILD_A_ID = '550e8400-e29b-41d4-a716-446655440050';
    private const string CLASS_A_ID = '550e8400-e29b-41d4-a716-446655440020';
    private const string CLASS_B_ID = '550e8400-e29b-41d4-a716-446655440021';
    private const string SUBJECT_MATH_ID = '550e8400-e29b-41d4-a716-446655440030';
    private const string TEACHER_ID = '550e8400-e29b-41d4-a716-446655440010';

    private InMemoryHomeworkRepository $homeworkRepository;
    private InMemoryHomeworkAttachmentRepository $attachmentRepository;

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

    #[Test]
    public function itReturnsNullWhenHomeworkDoesNotExist(): void
    {
        $handler = $this->createHandler(
            children: [
                ['studentId' => self::CHILD_A_ID, 'firstName' => 'Emma', 'lastName' => 'Dupont'],
            ],
            classMap: [self::CHILD_A_ID => self::CLASS_A_ID],
        );

        $result = $handler(
            self::PARENT_ID,
            self::TENANT_ID,
            '550e8400-e29b-41d4-a716-446655449999',
        );

        self::assertNull($result);
    }

    #[Test]
    public function itReturnsNullWhenParentHasNoChildInHomeworkClass(): void
    {
        $homework = $this->givenHomework(
            title: 'Exercices chapitre 5',
            dueDate: '2026-04-15',
            classId: self::CLASS_B_ID,
        );

        $handler = $this->createHandler(
            children: [
                ['studentId' => self::CHILD_A_ID, 'firstName' => 'Emma', 'lastName' => 'Dupont'],
            ],
            classMap: [self::CHILD_A_ID => self::CLASS_A_ID],
        );

        $result = $handler(
            self::PARENT_ID,
            self::TENANT_ID,
            (string) $homework->id,
        );

        self::assertNull($result);
    }

    #[Test]
    public function itReturnsDetailWhenParentHasChildInClass(): void
    {
        $homework = $this->givenHomework(
            title: 'Exercices chapitre 5',
            dueDate: '2026-04-15',
            classId: self::CLASS_A_ID,
        );

        $handler = $this->createHandler(
            children: [
                ['studentId' => self::CHILD_A_ID, 'firstName' => 'Emma', 'lastName' => 'Dupont'],
            ],
            classMap: [self::CHILD_A_ID => self::CLASS_A_ID],
        );

        $result = $handler(
            self::PARENT_ID,
            self::TENANT_ID,
            (string) $homework->id,
        );

        self::assertNotNull($result);
        self::assertSame((string) $homework->id, $result->id);
        self::assertSame('Exercices chapitre 5', $result->title);
        self::assertSame('2026-04-15', $result->dueDate);
        self::assertSame([], $result->attachments);
    }

    #[Test]
    public function itReturnsDetailWithAttachments(): void
    {
        $homework = $this->givenHomework(
            title: 'Devoir avec pièces jointes',
            dueDate: '2026-04-20',
            classId: self::CLASS_A_ID,
        );

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

        $handler = $this->createHandler(
            children: [
                ['studentId' => self::CHILD_A_ID, 'firstName' => 'Emma', 'lastName' => 'Dupont'],
            ],
            classMap: [self::CHILD_A_ID => self::CLASS_A_ID],
        );

        $result = $handler(
            self::PARENT_ID,
            self::TENANT_ID,
            (string) $homework->id,
        );

        self::assertNotNull($result);
        self::assertCount(1, $result->attachments);
        self::assertSame((string) $attachment->id, $result->attachments[0]->id);
        self::assertSame('exercice.pdf', $result->attachments[0]->filename);
        self::assertSame(2048, $result->attachments[0]->fileSize);
        self::assertSame('application/pdf', $result->attachments[0]->mimeType);
    }

    #[Test]
    public function itReturnsCorrectSubjectAndTeacherInfo(): void
    {
        $homework = $this->givenHomework(
            title: 'Révisions',
            dueDate: '2026-04-18',
            classId: self::CLASS_A_ID,
        );

        $handler = $this->createHandler(
            children: [
                ['studentId' => self::CHILD_A_ID, 'firstName' => 'Emma', 'lastName' => 'Dupont'],
            ],
            classMap: [self::CHILD_A_ID => self::CLASS_A_ID],
            subjectName: 'Physique-Chimie',
            subjectColor: '#ef4444',
            teacherName: 'Marie Martin',
        );

        $result = $handler(
            self::PARENT_ID,
            self::TENANT_ID,
            (string) $homework->id,
        );

        self::assertNotNull($result);
        self::assertSame(self::SUBJECT_MATH_ID, $result->subjectId);
        self::assertSame('Physique-Chimie', $result->subjectName);
        self::assertSame('#ef4444', $result->subjectColor);
        self::assertSame(self::TEACHER_ID, $result->teacherId);
        self::assertSame('Marie Martin', $result->teacherName);
    }

    /**
     * @param array<array{studentId: string, firstName: string, lastName: string}> $children
     * @param array<string, string>                                                $classMap studentId => classId
     */
    private function createHandler(
        array $children = [],
        array $classMap = [],
        string $subjectName = 'Mathématiques',
        ?string $subjectColor = '#3b82f6',
        string $teacherName = 'Jean Dupont',
    ): GetChildrenHomeworkDetailHandler {
        $parentChildrenReader = new class($children) implements ParentChildrenReader {
            /** @param array<array{studentId: string, firstName: string, lastName: string}> $children */
            public function __construct(private readonly array $children)
            {
            }

            public function childrenOf(string $guardianId, TenantId $tenantId): array
            {
                return $this->children;
            }
        };

        $studentClassReader = new class($classMap) implements StudentClassReader {
            /** @param array<string, string> $classMap */
            public function __construct(private readonly array $classMap)
            {
            }

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

        $displayReader = new class($subjectName, $subjectColor, $teacherName) implements ScheduleDisplayReader {
            public function __construct(
                private readonly string $subjectName,
                private readonly ?string $subjectColor,
                private readonly string $teacherName,
            ) {
            }

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

                foreach ($subjectIds as $id) {
                    $map[$id] = ['name' => $this->subjectName, 'color' => $this->subjectColor];
                }

                return $map;
            }

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

                foreach ($teacherIds as $id) {
                    $map[$id] = $this->teacherName;
                }

                return $map;
            }
        };

        return new GetChildrenHomeworkDetailHandler(
            $parentChildrenReader,
            $studentClassReader,
            $this->homeworkRepository,
            $this->attachmentRepository,
            $displayReader,
        );
    }

    private function givenHomework(
        string $title,
        string $dueDate,
        string $classId = self::CLASS_A_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;
    }
}
