<?php

declare(strict_types=1);

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

use App\Administration\Application\Command\CreateClass\CreateClassCommand;
use App\Administration\Application\Command\CreateClass\CreateClassHandler;
use App\Administration\Domain\Exception\ClasseDejaExistanteException;
use App\Administration\Domain\Model\SchoolClass\ClassId;
use App\Administration\Domain\Model\SchoolClass\ClassStatus;
use App\Administration\Domain\Model\SchoolClass\SchoolLevel;
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryClassRepository;
use App\Shared\Domain\Clock;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class CreateClassHandlerTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
    private const string SCHOOL_ID = '550e8400-e29b-41d4-a716-446655440002';
    private const string ACADEMIC_YEAR_ID = '550e8400-e29b-41d4-a716-446655440003';

    private InMemoryClassRepository $classRepository;
    private Clock $clock;

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

    #[Test]
    public function itCreatesClassSuccessfully(): void
    {
        $handler = new CreateClassHandler($this->classRepository, $this->clock);
        $command = new CreateClassCommand(
            tenantId: self::TENANT_ID,
            schoolId: self::SCHOOL_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            name: '6ème A',
            level: SchoolLevel::SIXIEME->value,
            capacity: 30,
        );

        $class = $handler($command);

        self::assertNotEmpty((string) $class->id);
        self::assertSame('6ème A', (string) $class->name);
        self::assertSame(SchoolLevel::SIXIEME, $class->level);
        self::assertSame(30, $class->capacity);
    }

    #[Test]
    public function itCreatesClassWithNullLevelAndCapacity(): void
    {
        $handler = new CreateClassHandler($this->classRepository, $this->clock);
        $command = new CreateClassCommand(
            tenantId: self::TENANT_ID,
            schoolId: self::SCHOOL_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            name: 'Classe spéciale',
            level: null,
            capacity: null,
        );

        $class = $handler($command);

        self::assertNotEmpty((string) $class->id);
        self::assertSame('Classe spéciale', (string) $class->name);
        self::assertNull($class->level);
        self::assertNull($class->capacity);
    }

    #[Test]
    public function itPersistsClassInRepository(): void
    {
        $handler = new CreateClassHandler($this->classRepository, $this->clock);
        $command = new CreateClassCommand(
            tenantId: self::TENANT_ID,
            schoolId: self::SCHOOL_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            name: '6ème A',
            level: SchoolLevel::SIXIEME->value,
            capacity: 30,
        );

        $createdClass = $handler($command);

        $class = $this->classRepository->get(
            ClassId::fromString((string) $createdClass->id),
        );

        self::assertSame('6ème A', (string) $class->name);
        self::assertSame(ClassStatus::ACTIVE, $class->status);
        self::assertSame(SchoolLevel::SIXIEME, $class->level);
        self::assertSame(30, $class->capacity);
    }

    #[Test]
    public function itThrowsExceptionWhenClassNameAlreadyExists(): void
    {
        $handler = new CreateClassHandler($this->classRepository, $this->clock);
        $command = new CreateClassCommand(
            tenantId: self::TENANT_ID,
            schoolId: self::SCHOOL_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            name: '6ème A',
            level: SchoolLevel::SIXIEME->value,
            capacity: 30,
        );

        // First creation should succeed
        $handler($command);

        // Second creation with same name should throw
        $this->expectException(ClasseDejaExistanteException::class);
        $this->expectExceptionMessage('Une classe avec le nom "6ème A" existe déjà pour cette année scolaire.');

        $handler($command);
    }

    #[Test]
    public function itAllowsSameNameInDifferentTenant(): void
    {
        $handler = new CreateClassHandler($this->classRepository, $this->clock);

        // Create in tenant 1
        $command1 = new CreateClassCommand(
            tenantId: self::TENANT_ID,
            schoolId: self::SCHOOL_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            name: '6ème A',
            level: SchoolLevel::SIXIEME->value,
            capacity: 30,
        );
        $class1 = $handler($command1);

        // Create same name in tenant 2 should succeed
        $command2 = new CreateClassCommand(
            tenantId: '550e8400-e29b-41d4-a716-446655440099',
            schoolId: self::SCHOOL_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            name: '6ème A',
            level: SchoolLevel::SIXIEME->value,
            capacity: 30,
        );
        $class2 = $handler($command2);

        self::assertFalse($class1->id->equals($class2->id));
        self::assertSame('6ème A', (string) $class1->name);
        self::assertSame('6ème A', (string) $class2->name);
    }

    #[Test]
    public function itAllowsSameNameInDifferentAcademicYear(): void
    {
        $handler = new CreateClassHandler($this->classRepository, $this->clock);

        // Create in year 1
        $command1 = new CreateClassCommand(
            tenantId: self::TENANT_ID,
            schoolId: self::SCHOOL_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            name: '6ème A',
            level: SchoolLevel::SIXIEME->value,
            capacity: 30,
        );
        $class1 = $handler($command1);

        // Create same name in year 2 should succeed
        $command2 = new CreateClassCommand(
            tenantId: self::TENANT_ID,
            schoolId: self::SCHOOL_ID,
            academicYearId: '550e8400-e29b-41d4-a716-446655440099',
            name: '6ème A',
            level: SchoolLevel::SIXIEME->value,
            capacity: 30,
        );
        $class2 = $handler($command2);

        self::assertFalse($class1->id->equals($class2->id));
        self::assertSame('6ème A', (string) $class1->name);
        self::assertSame('6ème A', (string) $class2->name);
    }
}
