<?php

declare(strict_types=1);

namespace App\Tests\Unit\Scolarite\Infrastructure\Api\Resource;

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\Query\GetActiveReplacements\ReplacementDto;
use App\Scolarite\Domain\Model\TeacherReplacement\ClassSubjectPair;
use App\Scolarite\Domain\Model\TeacherReplacement\ReplacementStatus;
use App\Scolarite\Domain\Model\TeacherReplacement\TeacherReplacement;
use App\Scolarite\Infrastructure\Api\Resource\TeacherReplacementResource;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class TeacherReplacementResourceTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
    private const string REPLACED_TEACHER_ID = '550e8400-e29b-41d4-a716-446655440010';
    private const string REPLACEMENT_TEACHER_ID = '550e8400-e29b-41d4-a716-446655440011';
    private const string CLASS_ID = '550e8400-e29b-41d4-a716-446655440020';
    private const string SUBJECT_ID = '550e8400-e29b-41d4-a716-446655440030';
    private const string CREATED_BY_ID = '550e8400-e29b-41d4-a716-446655440099';

    #[Test]
    public function fromDomainCreatesCorrectResource(): void
    {
        $replacement = $this->createReplacement();

        $resource = TeacherReplacementResource::fromDomain($replacement);

        self::assertSame((string) $replacement->id, $resource->id);
        self::assertSame(self::REPLACED_TEACHER_ID, $resource->replacedTeacherId);
        self::assertSame(self::REPLACEMENT_TEACHER_ID, $resource->replacementTeacherId);
        self::assertSame('2026-03-01', $resource->startDate);
        self::assertSame('2026-03-31', $resource->endDate);
        self::assertSame('active', $resource->status);
        self::assertSame('Congé maladie', $resource->reason);
        self::assertEquals($replacement->createdAt, $resource->createdAt);
        self::assertNull($resource->endedAt);
    }

    #[Test]
    public function fromDomainMapsClassSubjectPairs(): void
    {
        $replacement = $this->createReplacement();

        $resource = TeacherReplacementResource::fromDomain($replacement);

        self::assertCount(1, $resource->classes);
        self::assertSame(self::CLASS_ID, $resource->classes[0]['classId']);
        self::assertSame(self::SUBJECT_ID, $resource->classes[0]['subjectId']);
    }

    #[Test]
    public function fromDomainPreservesEndedAtWhenTerminated(): void
    {
        $replacement = $this->createReplacement();
        $endedAt = new DateTimeImmutable('2026-03-20 14:00:00');
        $replacement->terminer($endedAt);

        $resource = TeacherReplacementResource::fromDomain($replacement);

        self::assertSame('ended', $resource->status);
        self::assertEquals($endedAt, $resource->endedAt);
    }

    #[Test]
    public function fromDtoCreatesCorrectResource(): void
    {
        $startDate = new DateTimeImmutable('2026-03-01');
        $endDate = new DateTimeImmutable('2026-03-31');

        $dto = new ReplacementDto(
            id: '550e8400-e29b-41d4-a716-446655440050',
            replacedTeacherId: self::REPLACED_TEACHER_ID,
            replacementTeacherId: self::REPLACEMENT_TEACHER_ID,
            startDate: $startDate,
            endDate: $endDate,
            status: ReplacementStatus::ACTIVE->value,
            classes: [
                ['classId' => self::CLASS_ID, 'subjectId' => self::SUBJECT_ID],
            ],
            reason: 'Formation',
        );

        $resource = TeacherReplacementResource::fromDto($dto);

        self::assertSame('550e8400-e29b-41d4-a716-446655440050', $resource->id);
        self::assertSame(self::REPLACED_TEACHER_ID, $resource->replacedTeacherId);
        self::assertSame(self::REPLACEMENT_TEACHER_ID, $resource->replacementTeacherId);
        self::assertSame('2026-03-01', $resource->startDate);
        self::assertSame('2026-03-31', $resource->endDate);
        self::assertSame('active', $resource->status);
        self::assertSame('Formation', $resource->reason);
        self::assertCount(1, $resource->classes);
        self::assertSame(self::CLASS_ID, $resource->classes[0]['classId']);
        self::assertSame(self::SUBJECT_ID, $resource->classes[0]['subjectId']);
    }

    #[Test]
    public function fromDtoPreservesNullReason(): void
    {
        $dto = new ReplacementDto(
            id: '550e8400-e29b-41d4-a716-446655440050',
            replacedTeacherId: self::REPLACED_TEACHER_ID,
            replacementTeacherId: self::REPLACEMENT_TEACHER_ID,
            startDate: new DateTimeImmutable('2026-03-01'),
            endDate: new DateTimeImmutable('2026-03-31'),
            status: ReplacementStatus::ACTIVE->value,
            classes: [],
            reason: null,
        );

        $resource = TeacherReplacementResource::fromDto($dto);

        self::assertNull($resource->reason);
    }

    private function createReplacement(): TeacherReplacement
    {
        return TeacherReplacement::designer(
            tenantId: TenantId::fromString(self::TENANT_ID),
            replacedTeacherId: UserId::fromString(self::REPLACED_TEACHER_ID),
            replacementTeacherId: UserId::fromString(self::REPLACEMENT_TEACHER_ID),
            startDate: new DateTimeImmutable('2026-03-01'),
            endDate: new DateTimeImmutable('2026-03-31'),
            classes: [
                new ClassSubjectPair(
                    ClassId::fromString(self::CLASS_ID),
                    SubjectId::fromString(self::SUBJECT_ID),
                ),
            ],
            reason: 'Congé maladie',
            createdBy: UserId::fromString(self::CREATED_BY_ID),
            now: new DateTimeImmutable('2026-02-15 10:00:00'),
        );
    }
}
