<?php

declare(strict_types=1);

namespace App\Tests\Unit\Scolarite\Domain\Model\HomeworkSubmission;

use App\Administration\Domain\Model\User\UserId;
use App\Scolarite\Domain\Event\DevoirRendu;
use App\Scolarite\Domain\Event\DevoirRenduEnRetard;
use App\Scolarite\Domain\Exception\RenduDejaSoumisException;
use App\Scolarite\Domain\Model\Homework\HomeworkId;
use App\Scolarite\Domain\Model\HomeworkSubmission\HomeworkSubmission;
use App\Scolarite\Domain\Model\HomeworkSubmission\SubmissionStatus;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class HomeworkSubmissionTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
    private const string HOMEWORK_ID = '550e8400-e29b-41d4-a716-446655440050';
    private const string STUDENT_ID = '550e8400-e29b-41d4-a716-446655440060';

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

        self::assertSame(SubmissionStatus::DRAFT, $submission->status);
        self::assertNull($submission->submittedAt);
        self::assertEmpty($submission->pullDomainEvents());
    }

    #[Test]
    public function creerBrouillonSetsAllProperties(): void
    {
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $homeworkId = HomeworkId::fromString(self::HOMEWORK_ID);
        $studentId = UserId::fromString(self::STUDENT_ID);
        $now = new DateTimeImmutable('2026-03-24 10:00:00');

        $submission = HomeworkSubmission::creerBrouillon(
            tenantId: $tenantId,
            homeworkId: $homeworkId,
            studentId: $studentId,
            responseHtml: '<p>Ma réponse</p>',
            now: $now,
        );

        self::assertTrue($submission->tenantId->equals($tenantId));
        self::assertTrue($submission->homeworkId->equals($homeworkId));
        self::assertTrue($submission->studentId->equals($studentId));
        self::assertSame('<p>Ma réponse</p>', $submission->responseHtml);
        self::assertSame(SubmissionStatus::DRAFT, $submission->status);
        self::assertEquals($now, $submission->createdAt);
        self::assertEquals($now, $submission->updatedAt);
    }

    #[Test]
    public function creerBrouillonAllowsNullResponse(): void
    {
        $submission = HomeworkSubmission::creerBrouillon(
            tenantId: TenantId::fromString(self::TENANT_ID),
            homeworkId: HomeworkId::fromString(self::HOMEWORK_ID),
            studentId: UserId::fromString(self::STUDENT_ID),
            responseHtml: null,
            now: new DateTimeImmutable('2026-03-24 10:00:00'),
        );

        self::assertNull($submission->responseHtml);
    }

    #[Test]
    public function modifierBrouillonUpdatesResponse(): void
    {
        $submission = $this->createDraft();
        $updatedAt = new DateTimeImmutable('2026-03-24 11:00:00');

        $submission->modifierBrouillon('<p>Réponse modifiée</p>', $updatedAt);

        self::assertSame('<p>Réponse modifiée</p>', $submission->responseHtml);
        self::assertEquals($updatedAt, $submission->updatedAt);
    }

    #[Test]
    public function modifierBrouillonThrowsWhenAlreadySubmitted(): void
    {
        $submission = $this->createDraft();
        $submission->soumettre(
            dueDate: new DateTimeImmutable('2026-04-15'),
            now: new DateTimeImmutable('2026-03-24 12:00:00'),
        );

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

        $submission->modifierBrouillon('<p>Modification</p>', new DateTimeImmutable('2026-03-24 13:00:00'));
    }

    #[Test]
    public function soumettreOnTimeRecordsDevoirRenduEvent(): void
    {
        $submission = $this->createDraft();
        $dueDate = new DateTimeImmutable('2026-04-15 23:59:59');
        $now = new DateTimeImmutable('2026-04-10 14:00:00');

        $submission->soumettre(dueDate: $dueDate, now: $now);

        self::assertSame(SubmissionStatus::SUBMITTED, $submission->status);
        self::assertEquals($now, $submission->submittedAt);

        $events = $submission->pullDomainEvents();
        self::assertCount(1, $events);
        self::assertInstanceOf(DevoirRendu::class, $events[0]);
        self::assertSame($submission->id, $events[0]->submissionId);
    }

    #[Test]
    public function soumettreLateSetsLateStatusAndRecordsEvent(): void
    {
        $submission = $this->createDraft();
        $dueDate = new DateTimeImmutable('2026-03-20 23:59:59');
        $now = new DateTimeImmutable('2026-03-24 14:00:00');

        $submission->soumettre(dueDate: $dueDate, now: $now);

        self::assertSame(SubmissionStatus::LATE, $submission->status);
        self::assertEquals($now, $submission->submittedAt);

        $events = $submission->pullDomainEvents();
        self::assertCount(1, $events);
        self::assertInstanceOf(DevoirRenduEnRetard::class, $events[0]);
        self::assertSame($submission->id, $events[0]->submissionId);
    }

    #[Test]
    public function soumettreThrowsWhenAlreadySubmitted(): void
    {
        $submission = $this->createDraft();
        $submission->soumettre(
            dueDate: new DateTimeImmutable('2026-04-15'),
            now: new DateTimeImmutable('2026-03-24 12:00:00'),
        );

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

        $submission->soumettre(
            dueDate: new DateTimeImmutable('2026-04-15'),
            now: new DateTimeImmutable('2026-03-24 13:00:00'),
        );
    }

    #[Test]
    public function reconstituteRestoresAllPropertiesWithoutEvents(): void
    {
        $id = \App\Scolarite\Domain\Model\HomeworkSubmission\HomeworkSubmissionId::generate();
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $homeworkId = HomeworkId::fromString(self::HOMEWORK_ID);
        $studentId = UserId::fromString(self::STUDENT_ID);
        $createdAt = new DateTimeImmutable('2026-03-24 10:00:00');
        $updatedAt = new DateTimeImmutable('2026-03-24 12:00:00');
        $submittedAt = new DateTimeImmutable('2026-03-24 11:30:00');

        $submission = HomeworkSubmission::reconstitute(
            id: $id,
            tenantId: $tenantId,
            homeworkId: $homeworkId,
            studentId: $studentId,
            responseHtml: '<p>Réponse</p>',
            status: SubmissionStatus::SUBMITTED,
            submittedAt: $submittedAt,
            createdAt: $createdAt,
            updatedAt: $updatedAt,
        );

        self::assertTrue($submission->id->equals($id));
        self::assertTrue($submission->tenantId->equals($tenantId));
        self::assertTrue($submission->homeworkId->equals($homeworkId));
        self::assertTrue($submission->studentId->equals($studentId));
        self::assertSame('<p>Réponse</p>', $submission->responseHtml);
        self::assertSame(SubmissionStatus::SUBMITTED, $submission->status);
        self::assertEquals($submittedAt, $submission->submittedAt);
        self::assertEquals($createdAt, $submission->createdAt);
        self::assertEquals($updatedAt, $submission->updatedAt);
        self::assertEmpty($submission->pullDomainEvents());
    }

    private function createDraft(): HomeworkSubmission
    {
        return HomeworkSubmission::creerBrouillon(
            tenantId: TenantId::fromString(self::TENANT_ID),
            homeworkId: HomeworkId::fromString(self::HOMEWORK_ID),
            studentId: UserId::fromString(self::STUDENT_ID),
            responseHtml: '<p>Ma réponse</p>',
            now: new DateTimeImmutable('2026-03-24 10:00:00'),
        );
    }
}
