<?php

declare(strict_types=1);

namespace App\Tests\Unit\Scolarite\Application\Query\GetHomeworkExceptionsReport;

use App\Administration\Domain\Model\SchoolClass\ClassId;
use App\Administration\Domain\Model\Subject\SubjectId;
use App\Administration\Domain\Model\User\Email;
use App\Administration\Domain\Model\User\Role;
use App\Administration\Domain\Model\User\StatutCompte;
use App\Administration\Domain\Model\User\User;
use App\Administration\Domain\Model\User\UserId;
use App\Administration\Domain\Repository\UserRepository;
use App\Scolarite\Application\Query\GetHomeworkExceptionsReport\GetHomeworkExceptionsReportHandler;
use App\Scolarite\Application\Query\GetHomeworkExceptionsReport\GetHomeworkExceptionsReportQuery;
use App\Scolarite\Domain\Model\Homework\Homework;
use App\Scolarite\Domain\Model\Homework\HomeworkId;
use App\Scolarite\Domain\Model\Homework\HomeworkRuleException;
use App\Scolarite\Domain\Model\Homework\HomeworkStatus;
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkRepository;
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkRuleExceptionRepository;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class GetHomeworkExceptionsReportHandlerTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
    private const string TEACHER_ID = '550e8400-e29b-41d4-a716-446655440010';
    private const string TEACHER2_ID = '550e8400-e29b-41d4-a716-446655440011';

    private InMemoryHomeworkRepository $homeworkRepository;
    private InMemoryHomeworkRuleExceptionRepository $exceptionRepository;

    protected function setUp(): void
    {
        $this->homeworkRepository = new InMemoryHomeworkRepository();
        $this->exceptionRepository = new InMemoryHomeworkRuleExceptionRepository();
    }

    #[Test]
    public function itReturnsAllExceptionsForTenant(): void
    {
        $this->seedData();
        $handler = $this->createHandler();

        $results = $handler(new GetHomeworkExceptionsReportQuery(tenantId: self::TENANT_ID));

        self::assertCount(2, $results);
        self::assertSame('Exercices chapitre 5', $results[0]->homeworkTitle);
        self::assertSame('Jean Dupont', $results[0]->teacherName);
    }

    #[Test]
    public function itFiltersByDateRange(): void
    {
        $this->seedData();
        $handler = $this->createHandler();

        $results = $handler(new GetHomeworkExceptionsReportQuery(
            tenantId: self::TENANT_ID,
            startDate: '2026-03-20',
            endDate: '2026-03-25',
        ));

        self::assertCount(1, $results);
        self::assertSame('Devoir histoire', $results[0]->homeworkTitle);
    }

    #[Test]
    public function itFiltersByTeacher(): void
    {
        $this->seedData();
        $handler = $this->createHandler();

        $results = $handler(new GetHomeworkExceptionsReportQuery(
            tenantId: self::TENANT_ID,
            teacherId: self::TEACHER2_ID,
        ));

        self::assertCount(1, $results);
        self::assertSame(self::TEACHER2_ID, $results[0]->teacherId);
    }

    #[Test]
    public function itFiltersByRuleType(): void
    {
        $this->seedData();
        $handler = $this->createHandler();

        $results = $handler(new GetHomeworkExceptionsReportQuery(
            tenantId: self::TENANT_ID,
            ruleType: 'no_monday_after',
        ));

        self::assertCount(1, $results);
        self::assertStringContainsString('no_monday_after', $results[0]->ruleType);
    }

    #[Test]
    public function itReturnsEmptyWhenNoExceptions(): void
    {
        $handler = $this->createHandler();

        $results = $handler(new GetHomeworkExceptionsReportQuery(tenantId: self::TENANT_ID));

        self::assertCount(0, $results);
    }

    private function seedData(): void
    {
        $tenantId = TenantId::fromString(self::TENANT_ID);

        $hw1 = Homework::reconstitute(
            id: HomeworkId::fromString('550e8400-e29b-41d4-a716-446655440050'),
            tenantId: $tenantId,
            classId: ClassId::fromString('550e8400-e29b-41d4-a716-446655440020'),
            subjectId: SubjectId::fromString('550e8400-e29b-41d4-a716-446655440030'),
            teacherId: UserId::fromString(self::TEACHER_ID),
            title: 'Exercices chapitre 5',
            description: null,
            dueDate: new DateTimeImmutable('2026-04-15'),
            status: HomeworkStatus::PUBLISHED,
            createdAt: new DateTimeImmutable('2026-03-19 10:00:00'),
            updatedAt: new DateTimeImmutable('2026-03-19 10:00:00'),
        );
        $this->homeworkRepository->save($hw1);

        $hw2 = Homework::reconstitute(
            id: HomeworkId::fromString('550e8400-e29b-41d4-a716-446655440051'),
            tenantId: $tenantId,
            classId: ClassId::fromString('550e8400-e29b-41d4-a716-446655440020'),
            subjectId: SubjectId::fromString('550e8400-e29b-41d4-a716-446655440030'),
            teacherId: UserId::fromString(self::TEACHER2_ID),
            title: 'Devoir histoire',
            description: null,
            dueDate: new DateTimeImmutable('2026-04-20'),
            status: HomeworkStatus::PUBLISHED,
            createdAt: new DateTimeImmutable('2026-03-21 10:00:00'),
            updatedAt: new DateTimeImmutable('2026-03-21 10:00:00'),
        );
        $this->homeworkRepository->save($hw2);

        $ex1 = HomeworkRuleException::demander(
            tenantId: $tenantId,
            homeworkId: $hw1->id,
            ruleTypes: ['minimum_delay'],
            justification: 'Sortie scolaire prévue, devoir urgent pour les élèves.',
            createdBy: UserId::fromString(self::TEACHER_ID),
            now: new DateTimeImmutable('2026-03-19 10:00:00'),
        );
        $this->exceptionRepository->save($ex1);

        $ex2 = HomeworkRuleException::demander(
            tenantId: $tenantId,
            homeworkId: $hw2->id,
            ruleTypes: ['no_monday_after'],
            justification: 'Rentrée après les vacances, devoir préparatoire.',
            createdBy: UserId::fromString(self::TEACHER2_ID),
            now: new DateTimeImmutable('2026-03-21 10:00:00'),
        );
        $this->exceptionRepository->save($ex2);
    }

    private function createHandler(): GetHomeworkExceptionsReportHandler
    {
        $userRepository = $this->createMock(UserRepository::class);
        $userRepository->method('findById')
            ->willReturnCallback(static function (UserId $id): ?User {
                $map = [
                    self::TEACHER_ID => ['Jean', 'Dupont'],
                    self::TEACHER2_ID => ['Marie', 'Martin'],
                ];

                $data = $map[(string) $id] ?? null;

                if ($data === null) {
                    return null;
                }

                return User::reconstitute(
                    id: $id,
                    email: new Email('test@test.fr'),
                    roles: [Role::PROF],
                    tenantId: TenantId::fromString(self::TENANT_ID),
                    schoolName: 'École Test',
                    statut: StatutCompte::ACTIF,
                    dateNaissance: null,
                    createdAt: new DateTimeImmutable('2026-01-01'),
                    hashedPassword: 'hashed',
                    activatedAt: new DateTimeImmutable('2026-01-02'),
                    consentementParental: null,
                    firstName: $data[0],
                    lastName: $data[1],
                );
            });

        return new GetHomeworkExceptionsReportHandler(
            $this->exceptionRepository,
            $this->homeworkRepository,
            $userRepository,
        );
    }
}
