<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Application\Query\GetClasses;

use App\Administration\Application\Query\GetClasses\GetClassesHandler;
use App\Administration\Application\Query\GetClasses\GetClassesQuery;
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
use App\Administration\Domain\Model\SchoolClass\ClassName;
use App\Administration\Domain\Model\SchoolClass\SchoolClass;
use App\Administration\Domain\Model\SchoolClass\SchoolId;
use App\Administration\Domain\Model\SchoolClass\SchoolLevel;
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryClassRepository;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class GetClassesHandlerTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440002';
    private const string ACADEMIC_YEAR_ID = '550e8400-e29b-41d4-a716-446655440010';

    private InMemoryClassRepository $classRepository;
    private GetClassesHandler $handler;

    protected function setUp(): void
    {
        $this->classRepository = new InMemoryClassRepository();
        $this->handler = new GetClassesHandler($this->classRepository);
    }

    #[Test]
    public function returnsAllActiveClassesForTenantAndYear(): void
    {
        $this->seedClasses();

        $query = new GetClassesQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
        );
        $result = ($this->handler)($query);

        self::assertCount(3, $result->items);
        self::assertSame(3, $result->total);
        self::assertSame(1, $result->page);
        self::assertSame(30, $result->limit);
    }

    #[Test]
    public function filtersClassesByName(): void
    {
        $this->seedClasses();

        $query = new GetClassesQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            search: '6ème',
        );
        $result = ($this->handler)($query);

        self::assertCount(1, $result->items);
        self::assertSame('6ème A', $result->items[0]->name);
    }

    #[Test]
    public function filtersClassesByLevel(): void
    {
        $this->seedClasses();

        $query = new GetClassesQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            search: SchoolLevel::CM2->value,
        );
        $result = ($this->handler)($query);

        self::assertCount(1, $result->items);
        self::assertSame('CM2 B', $result->items[0]->name);
    }

    #[Test]
    public function searchIsCaseInsensitive(): void
    {
        $this->seedClasses();

        $query = new GetClassesQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            search: 'cm2',
        );
        $result = ($this->handler)($query);

        self::assertCount(1, $result->items);
    }

    #[Test]
    public function paginatesResults(): void
    {
        $this->seedClasses();

        $query = new GetClassesQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            page: 1,
            limit: 2,
        );
        $result = ($this->handler)($query);

        self::assertCount(2, $result->items);
        self::assertSame(3, $result->total);
        self::assertSame(1, $result->page);
        self::assertSame(2, $result->limit);
        self::assertSame(2, $result->totalPages());
    }

    #[Test]
    public function returnsSecondPage(): void
    {
        $this->seedClasses();

        $query = new GetClassesQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            page: 2,
            limit: 2,
        );
        $result = ($this->handler)($query);

        self::assertCount(1, $result->items);
        self::assertSame(3, $result->total);
        self::assertSame(2, $result->page);
    }

    #[Test]
    public function returnsEmptyWhenNoMatches(): void
    {
        $this->seedClasses();

        $query = new GetClassesQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            search: 'nonexistent',
        );
        $result = ($this->handler)($query);

        self::assertCount(0, $result->items);
        self::assertSame(0, $result->total);
    }

    #[Test]
    public function clampsInvalidPageToOne(): void
    {
        $this->seedClasses();

        $query = new GetClassesQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            page: -1,
        );

        self::assertSame(1, $query->page);
    }

    #[Test]
    public function clampsExcessiveLimitToMax(): void
    {
        $query = new GetClassesQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            limit: 999,
        );

        self::assertSame(100, $query->limit);
    }

    private function seedClasses(): void
    {
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $schoolId = SchoolId::generate();
        $academicYearId = AcademicYearId::fromString(self::ACADEMIC_YEAR_ID);
        $now = new DateTimeImmutable('2026-02-01 10:00:00');

        $this->classRepository->save(SchoolClass::creer(
            tenantId: $tenantId,
            schoolId: $schoolId,
            academicYearId: $academicYearId,
            name: new ClassName('6ème A'),
            level: SchoolLevel::SIXIEME,
            capacity: 30,
            createdAt: $now,
        ));

        $this->classRepository->save(SchoolClass::creer(
            tenantId: $tenantId,
            schoolId: $schoolId,
            academicYearId: $academicYearId,
            name: new ClassName('CM2 B'),
            level: SchoolLevel::CM2,
            capacity: 25,
            createdAt: $now,
        ));

        $this->classRepository->save(SchoolClass::creer(
            tenantId: $tenantId,
            schoolId: $schoolId,
            academicYearId: $academicYearId,
            name: new ClassName('CP Alpha'),
            level: SchoolLevel::CP,
            capacity: 20,
            createdAt: $now,
        ));
    }
}
