<?php

declare(strict_types=1);

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

use App\Administration\Application\Dto\PaginatedResult;
use App\Administration\Application\Port\PaginatedAssignmentsReader;
use App\Administration\Application\Query\GetAllAssignments\AssignmentWithNamesDto;
use App\Administration\Application\Query\GetAllAssignments\GetAllAssignmentsHandler;
use App\Administration\Application\Query\GetAllAssignments\GetAllAssignmentsQuery;
use App\Administration\Application\Service\Cache\PaginatedQueryCache;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\TagAwareAdapter;

final class GetAllAssignmentsHandlerTest extends TestCase
{
    private PaginatedAssignmentsReader $reader;
    private PaginatedQueryCache $cache;
    private GetAllAssignmentsHandler $handler;

    protected function setUp(): void
    {
        $this->reader = $this->createMock(PaginatedAssignmentsReader::class);
        $this->cache = new PaginatedQueryCache(
            new TagAwareAdapter(new ArrayAdapter()),
        );
        $this->handler = new GetAllAssignmentsHandler($this->reader, $this->cache);
    }

    #[Test]
    public function returnsItemsForTenant(): void
    {
        $dto = $this->createAssignmentDto();
        $this->reader->method('findPaginated')->willReturn(
            new PaginatedResult(items: [$dto], total: 1, page: 1, limit: 30),
        );

        $result = ($this->handler)(new GetAllAssignmentsQuery(tenantId: 'tenant-1'));

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

    #[Test]
    public function mapsDtoFields(): void
    {
        $dto = $this->createAssignmentDto();
        $this->reader->method('findPaginated')->willReturn(
            new PaginatedResult(items: [$dto], total: 1, page: 1, limit: 30),
        );

        $result = ($this->handler)(new GetAllAssignmentsQuery(tenantId: 'tenant-1'));

        $item = $result->items[0];
        self::assertSame('assign-1', $item->id);
        self::assertSame('teacher-1', $item->teacherId);
        self::assertSame('Jean', $item->teacherFirstName);
        self::assertSame('Dupont', $item->teacherLastName);
        self::assertSame('class-1', $item->classId);
        self::assertSame('6eme A', $item->className);
        self::assertSame('subj-1', $item->subjectId);
        self::assertSame('Mathematiques', $item->subjectName);
        self::assertSame('year-1', $item->academicYearId);
        self::assertSame('active', $item->status);
        self::assertNull($item->endDate);
    }

    #[Test]
    public function paginatesResults(): void
    {
        $this->reader->method('findPaginated')->willReturn(
            new PaginatedResult(items: [], total: 50, page: 2, limit: 10),
        );

        $result = ($this->handler)(new GetAllAssignmentsQuery(tenantId: 'tenant-1', page: 2, limit: 10));

        self::assertSame(50, $result->total);
        self::assertSame(2, $result->page);
        self::assertSame(10, $result->limit);
    }

    #[Test]
    public function cachesResult(): void
    {
        $dto = $this->createAssignmentDto();
        $this->reader->expects(self::once())->method('findPaginated')->willReturn(
            new PaginatedResult(items: [$dto], total: 1, page: 1, limit: 30),
        );

        $query = new GetAllAssignmentsQuery(tenantId: 'tenant-1');
        ($this->handler)($query);
        $result = ($this->handler)($query);

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

    #[Test]
    public function clampsPageToMinimumOne(): void
    {
        $this->reader->method('findPaginated')->willReturn(
            new PaginatedResult(items: [], total: 0, page: 1, limit: 30),
        );

        $result = ($this->handler)(new GetAllAssignmentsQuery(tenantId: 'tenant-1', page: -5));

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

    #[Test]
    public function clampsLimitToMaximumHundred(): void
    {
        $this->reader->method('findPaginated')->willReturn(
            new PaginatedResult(items: [], total: 0, page: 1, limit: 100),
        );

        $result = ($this->handler)(new GetAllAssignmentsQuery(tenantId: 'tenant-1', limit: 500));

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

    private function createAssignmentDto(): AssignmentWithNamesDto
    {
        return new AssignmentWithNamesDto(
            id: 'assign-1',
            teacherId: 'teacher-1',
            teacherFirstName: 'Jean',
            teacherLastName: 'Dupont',
            classId: 'class-1',
            className: '6eme A',
            subjectId: 'subj-1',
            subjectName: 'Mathematiques',
            academicYearId: 'year-1',
            status: 'active',
            startDate: new DateTimeImmutable('2026-02-01'),
            endDate: null,
            createdAt: new DateTimeImmutable('2026-01-15'),
        );
    }
}
