<?php

declare(strict_types=1);

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

use App\Administration\Domain\Model\User\UserId;
use App\Scolarite\Domain\Event\ResultatCompetenceModifie;
use App\Scolarite\Domain\Event\ResultatCompetenceSaisi;
use App\Scolarite\Domain\Model\Competency\CompetencyEvaluationId;
use App\Scolarite\Domain\Model\Competency\CompetencyLevel;
use App\Scolarite\Domain\Model\Competency\StudentCompetencyResult;
use App\Scolarite\Domain\Model\Competency\StudentCompetencyResultId;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

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

    #[Test]
    public function saisirCreatesResultWithCorrectProperties(): void
    {
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $ceId = CompetencyEvaluationId::generate();
        $studentId = UserId::fromString(self::STUDENT_ID);
        $now = new DateTimeImmutable('2026-04-01 10:00:00');

        $result = StudentCompetencyResult::saisir(
            tenantId: $tenantId,
            competencyEvaluationId: $ceId,
            studentId: $studentId,
            levelCode: CompetencyLevel::ACQUIRED->value,
            now: $now,
        );

        self::assertTrue($result->tenantId->equals($tenantId));
        self::assertTrue($result->competencyEvaluationId->equals($ceId));
        self::assertTrue($result->studentId->equals($studentId));
        self::assertSame('acquired', $result->levelCode);
        self::assertEquals($now, $result->createdAt);
        self::assertEquals($now, $result->updatedAt);
    }

    #[Test]
    public function saisirRecordsEvent(): void
    {
        $result = $this->createResult();

        $events = $result->pullDomainEvents();

        self::assertCount(1, $events);
        self::assertInstanceOf(ResultatCompetenceSaisi::class, $events[0]);
        self::assertSame($result->id, $events[0]->resultId);
        self::assertSame('acquired', $events[0]->levelCode);
    }

    #[Test]
    public function modifierDoesNothingWhenLevelUnchanged(): void
    {
        $result = $this->createResult();
        $result->pullDomainEvents();
        $createdAt = $result->updatedAt;

        $result->modifier(
            levelCode: CompetencyLevel::ACQUIRED->value,
            now: new DateTimeImmutable('2026-04-02 14:00:00'),
        );

        self::assertSame('acquired', $result->levelCode);
        self::assertEquals($createdAt, $result->updatedAt);
        self::assertEmpty($result->pullDomainEvents());
    }

    #[Test]
    public function modifierUpdatesLevelAndRecordsEvent(): void
    {
        $result = $this->createResult();
        $result->pullDomainEvents();
        $modifiedAt = new DateTimeImmutable('2026-04-02 14:00:00');

        $result->modifier(
            levelCode: CompetencyLevel::EXCEEDED->value,
            now: $modifiedAt,
        );

        self::assertSame('exceeded', $result->levelCode);
        self::assertEquals($modifiedAt, $result->updatedAt);

        $events = $result->pullDomainEvents();
        self::assertCount(1, $events);
        self::assertInstanceOf(ResultatCompetenceModifie::class, $events[0]);
        self::assertSame('acquired', $events[0]->oldLevelCode);
        self::assertSame('exceeded', $events[0]->newLevelCode);
    }

    #[Test]
    public function reconstituteRestoresAllProperties(): void
    {
        $id = StudentCompetencyResultId::generate();
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $ceId = CompetencyEvaluationId::generate();
        $studentId = UserId::fromString(self::STUDENT_ID);
        $createdAt = new DateTimeImmutable('2026-04-01 10:00:00');
        $updatedAt = new DateTimeImmutable('2026-04-02 14:00:00');

        $result = StudentCompetencyResult::reconstitute(
            id: $id,
            tenantId: $tenantId,
            competencyEvaluationId: $ceId,
            studentId: $studentId,
            levelCode: CompetencyLevel::IN_PROGRESS->value,
            createdAt: $createdAt,
            updatedAt: $updatedAt,
        );

        self::assertTrue($result->id->equals($id));
        self::assertTrue($result->tenantId->equals($tenantId));
        self::assertTrue($result->competencyEvaluationId->equals($ceId));
        self::assertTrue($result->studentId->equals($studentId));
        self::assertSame('in_progress', $result->levelCode);
        self::assertEquals($createdAt, $result->createdAt);
        self::assertEquals($updatedAt, $result->updatedAt);
        self::assertEmpty($result->pullDomainEvents());
    }

    private function createResult(): StudentCompetencyResult
    {
        return StudentCompetencyResult::saisir(
            tenantId: TenantId::fromString(self::TENANT_ID),
            competencyEvaluationId: CompetencyEvaluationId::generate(),
            studentId: UserId::fromString(self::STUDENT_ID),
            levelCode: CompetencyLevel::ACQUIRED->value,
            now: new DateTimeImmutable('2026-04-01 10:00:00'),
        );
    }
}
