<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Application\Command\RemoveAssignment;

use App\Administration\Application\Command\RemoveAssignment\RemoveAssignmentCommand;
use App\Administration\Application\Command\RemoveAssignment\RemoveAssignmentHandler;
use App\Administration\Domain\Exception\AffectationNotFoundException;
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
use App\Administration\Domain\Model\SchoolClass\ClassId;
use App\Administration\Domain\Model\Subject\SubjectId;
use App\Administration\Domain\Model\TeacherAssignment\AssignmentStatus;
use App\Administration\Domain\Model\TeacherAssignment\TeacherAssignment;
use App\Administration\Domain\Model\User\UserId;
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryTeacherAssignmentRepository;
use App\Shared\Domain\Clock;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

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

    private InMemoryTeacherAssignmentRepository $assignmentRepository;
    private Clock $clock;

    protected function setUp(): void
    {
        $this->assignmentRepository = new InMemoryTeacherAssignmentRepository();
        $this->clock = new class implements Clock {
            public function now(): DateTimeImmutable
            {
                return new DateTimeImmutable('2026-03-01 10:00:00');
            }
        };
    }

    #[Test]
    public function itRemovesAssignmentSuccessfully(): void
    {
        $assignment = $this->createAndSaveAssignment();
        $handler = new RemoveAssignmentHandler($this->assignmentRepository, $this->clock);

        $command = new RemoveAssignmentCommand(
            assignmentId: (string) $assignment->id,
            tenantId: self::TENANT_ID,
        );

        $result = $handler($command);

        self::assertSame(AssignmentStatus::REMOVED, $result->status);
        self::assertFalse($result->estActive());
        self::assertNotNull($result->endDate);
    }

    #[Test]
    public function itThrowsExceptionWhenAssignmentNotFound(): void
    {
        $handler = new RemoveAssignmentHandler($this->assignmentRepository, $this->clock);

        $command = new RemoveAssignmentCommand(
            assignmentId: '550e8400-e29b-41d4-a716-446655440099',
            tenantId: self::TENANT_ID,
        );

        $this->expectException(AffectationNotFoundException::class);
        $handler($command);
    }

    #[Test]
    public function itThrowsExceptionWhenTenantMismatch(): void
    {
        $assignment = $this->createAndSaveAssignment();
        $handler = new RemoveAssignmentHandler($this->assignmentRepository, $this->clock);

        $command = new RemoveAssignmentCommand(
            assignmentId: (string) $assignment->id,
            tenantId: '550e8400-e29b-41d4-a716-446655440099',
        );

        $this->expectException(AffectationNotFoundException::class);
        $handler($command);
    }

    #[Test]
    public function itIsIdempotentWhenAlreadyRemoved(): void
    {
        $assignment = $this->createAndSaveAssignment();
        $handler = new RemoveAssignmentHandler($this->assignmentRepository, $this->clock);

        $command = new RemoveAssignmentCommand(
            assignmentId: (string) $assignment->id,
            tenantId: self::TENANT_ID,
        );

        $result1 = $handler($command);
        $result2 = $handler($command);

        self::assertSame(AssignmentStatus::REMOVED, $result2->status);
        self::assertEquals($result1->endDate, $result2->endDate);
    }

    private function createAndSaveAssignment(): TeacherAssignment
    {
        $assignment = TeacherAssignment::creer(
            tenantId: TenantId::fromString(self::TENANT_ID),
            teacherId: UserId::fromString('550e8400-e29b-41d4-a716-446655440010'),
            classId: ClassId::fromString('550e8400-e29b-41d4-a716-446655440020'),
            subjectId: SubjectId::fromString('550e8400-e29b-41d4-a716-446655440030'),
            academicYearId: AcademicYearId::fromString('550e8400-e29b-41d4-a716-446655440040'),
            createdAt: new DateTimeImmutable('2026-02-12 10:00:00'),
        );

        $this->assignmentRepository->save($assignment);

        return $assignment;
    }
}
