<?php

declare(strict_types=1);

namespace App\Tests\Unit\Scolarite\Application\Command\SubmitHomework;

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\Command\SubmitHomework\SubmitHomeworkCommand;
use App\Scolarite\Application\Command\SubmitHomework\SubmitHomeworkHandler;
use App\Scolarite\Application\Port\StudentClassReader;
use App\Scolarite\Domain\Exception\EleveNonAffecteAuDevoirException;
use App\Scolarite\Domain\Exception\RenduDejaSoumisException;
use App\Scolarite\Domain\Exception\RenduNonTrouveException;
use App\Scolarite\Domain\Model\Homework\Homework;
use App\Scolarite\Domain\Model\HomeworkSubmission\HomeworkSubmission;
use App\Scolarite\Domain\Model\HomeworkSubmission\SubmissionStatus;
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkRepository;
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkSubmissionRepository;
use App\Shared\Domain\Clock;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class SubmitHomeworkHandlerTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
    private const string CLASS_ID = '550e8400-e29b-41d4-a716-446655440020';
    private const string SUBJECT_ID = '550e8400-e29b-41d4-a716-446655440030';
    private const string TEACHER_ID = '550e8400-e29b-41d4-a716-446655440010';
    private const string STUDENT_ID = '550e8400-e29b-41d4-a716-446655440060';

    private InMemoryHomeworkRepository $homeworkRepository;
    private InMemoryHomeworkSubmissionRepository $submissionRepository;
    private Homework $homework;

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

        $this->homework = Homework::creer(
            tenantId: TenantId::fromString(self::TENANT_ID),
            classId: ClassId::fromString(self::CLASS_ID),
            subjectId: SubjectId::fromString(self::SUBJECT_ID),
            teacherId: UserId::fromString(self::TEACHER_ID),
            title: 'Exercices chapitre 5',
            description: null,
            dueDate: new DateTimeImmutable('2026-04-15'),
            now: new DateTimeImmutable('2026-03-12 10:00:00'),
        );
        $this->homeworkRepository->save($this->homework);
    }

    #[Test]
    public function itSubmitsOnTimeSuccessfully(): void
    {
        $this->createDraft();

        $handler = $this->createHandler(now: '2026-04-10 14:00:00');
        $command = $this->createCommand();

        $submission = $handler($command);

        self::assertSame(SubmissionStatus::SUBMITTED, $submission->status);
        self::assertNotNull($submission->submittedAt);
    }

    #[Test]
    public function itSubmitsLateWhenAfterDueDate(): void
    {
        $this->createDraft();

        $handler = $this->createHandler(now: '2026-04-20 14:00:00');
        $command = $this->createCommand();

        $submission = $handler($command);

        self::assertSame(SubmissionStatus::LATE, $submission->status);
    }

    #[Test]
    public function itThrowsWhenNoDraftExists(): void
    {
        $handler = $this->createHandler(now: '2026-04-10 14:00:00');

        $this->expectException(RenduNonTrouveException::class);

        $handler($this->createCommand());
    }

    #[Test]
    public function itThrowsWhenAlreadySubmitted(): void
    {
        $this->createDraft();

        $handler = $this->createHandler(now: '2026-04-10 14:00:00');
        $handler($this->createCommand());

        $this->expectException(RenduDejaSoumisException::class);

        $handler($this->createCommand());
    }

    #[Test]
    public function itThrowsWhenStudentNotInClass(): void
    {
        $this->createDraft();

        $handler = $this->createHandler(now: '2026-04-10 14:00:00', studentClassId: null);

        $this->expectException(EleveNonAffecteAuDevoirException::class);

        $handler($this->createCommand());
    }

    private function createDraft(): void
    {
        $submission = HomeworkSubmission::creerBrouillon(
            tenantId: TenantId::fromString(self::TENANT_ID),
            homeworkId: $this->homework->id,
            studentId: UserId::fromString(self::STUDENT_ID),
            responseHtml: '<p>Ma réponse</p>',
            now: new DateTimeImmutable('2026-03-24 10:00:00'),
        );
        $this->submissionRepository->save($submission);
    }

    private function createHandler(string $now, ?string $studentClassId = self::CLASS_ID): SubmitHomeworkHandler
    {
        $clock = new class($now) implements Clock {
            public function __construct(private readonly string $time)
            {
            }

            public function now(): DateTimeImmutable
            {
                return new DateTimeImmutable($this->time);
            }
        };

        $studentClassReader = new class($studentClassId) implements StudentClassReader {
            public function __construct(private readonly ?string $classId)
            {
            }

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

        return new SubmitHomeworkHandler(
            $this->homeworkRepository,
            $this->submissionRepository,
            $studentClassReader,
            $clock,
        );
    }

    private function createCommand(): SubmitHomeworkCommand
    {
        return new SubmitHomeworkCommand(
            tenantId: self::TENANT_ID,
            homeworkId: (string) $this->homework->id,
            studentId: self::STUDENT_ID,
        );
    }
}
