<?php

declare(strict_types=1);

namespace App\Tests\Unit\Scolarite\Infrastructure\Messaging;

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\Domain\Event\ExceptionDevoirDemandee;
use App\Scolarite\Domain\Model\Homework\Homework;
use App\Scolarite\Domain\Model\Homework\HomeworkId;
use App\Scolarite\Domain\Model\Homework\HomeworkRuleExceptionId;
use App\Scolarite\Domain\Repository\HomeworkRepository;
use App\Scolarite\Infrastructure\Messaging\OnExceptionDevoirDemandeeHandler;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
use RuntimeException;
use Symfony\Component\Mailer\MailerInterface;
use Twig\Environment;

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

    #[Test]
    public function itSendsEmailToAllDirectorsInTenant(): void
    {
        $mailer = $this->createMock(MailerInterface::class);
        $twig = $this->createMock(Environment::class);
        $homeworkRepo = $this->createMock(HomeworkRepository::class);
        $userRepo = $this->createMock(UserRepository::class);

        $homework = $this->createHomework();
        $homeworkRepo->method('findById')->willReturn($homework);

        $teacher = $this->createUser('teacher@school.fr', [Role::PROF], 'Jean', 'Dupont');
        $userRepo->method('findById')->willReturn($teacher);

        $userRepo->method('findAllByTenant')->willReturn([
            $this->createUser('director@school.fr', [Role::ADMIN], 'Marie', 'Martin'),
            $this->createUser('teacher@school.fr', [Role::PROF], 'Jean', 'Dupont'),
            $this->createUser('parent@school.fr', [Role::PARENT], 'Pierre', 'Durand'),
        ]);

        $twig->method('render')->willReturn('<html>notification</html>');

        $mailer->expects(self::exactly(1))->method('send');

        $handler = new OnExceptionDevoirDemandeeHandler(
            $homeworkRepo,
            $userRepo,
            $mailer,
            $twig,
            new NullLogger(),
        );

        ($handler)($this->createEvent());
    }

    #[Test]
    public function itSkipsWhenNoDirectorsInTenant(): void
    {
        $mailer = $this->createMock(MailerInterface::class);
        $twig = $this->createMock(Environment::class);
        $homeworkRepo = $this->createMock(HomeworkRepository::class);
        $userRepo = $this->createMock(UserRepository::class);

        $homeworkRepo->method('findById')->willReturn($this->createHomework());
        $userRepo->method('findById')->willReturn($this->createUser('teacher@school.fr', [Role::PROF]));
        $userRepo->method('findAllByTenant')->willReturn([
            $this->createUser('teacher@school.fr', [Role::PROF]),
        ]);

        $mailer->expects(self::never())->method('send');

        $handler = new OnExceptionDevoirDemandeeHandler(
            $homeworkRepo,
            $userRepo,
            $mailer,
            $twig,
            new NullLogger(),
        );

        ($handler)($this->createEvent());
    }

    #[Test]
    public function itHandlesMailerFailureGracefully(): void
    {
        $mailer = $this->createMock(MailerInterface::class);
        $twig = $this->createMock(Environment::class);
        $homeworkRepo = $this->createMock(HomeworkRepository::class);
        $userRepo = $this->createMock(UserRepository::class);

        $homeworkRepo->method('findById')->willReturn($this->createHomework());
        $userRepo->method('findById')->willReturn($this->createUser('teacher@school.fr', [Role::PROF]));
        $userRepo->method('findAllByTenant')->willReturn([
            $this->createUser('director@school.fr', [Role::ADMIN]),
        ]);
        $twig->method('render')->willReturn('<html>notification</html>');

        $mailer->method('send')->willThrowException(new RuntimeException('SMTP error'));

        $handler = new OnExceptionDevoirDemandeeHandler(
            $homeworkRepo,
            $userRepo,
            $mailer,
            $twig,
            new NullLogger(),
        );

        ($handler)($this->createEvent());

        $this->addToAssertionCount(1);
    }

    #[Test]
    public function itPassesCorrectDataToTemplate(): void
    {
        $mailer = $this->createMock(MailerInterface::class);
        $twig = $this->createMock(Environment::class);
        $homeworkRepo = $this->createMock(HomeworkRepository::class);
        $userRepo = $this->createMock(UserRepository::class);

        $homeworkRepo->method('findById')->willReturn($this->createHomework());
        $teacher = $this->createUser('teacher@school.fr', [Role::PROF], 'Jean', 'Dupont');
        $userRepo->method('findById')->willReturn($teacher);
        $userRepo->method('findAllByTenant')->willReturn([
            $this->createUser('director@school.fr', [Role::ADMIN]),
        ]);

        $twig->expects(self::once())
            ->method('render')
            ->with(
                'emails/homework_exception_notification.html.twig',
                self::callback(static function (array $params): bool {
                    return $params['teacherName'] === 'Jean Dupont'
                        && $params['homeworkTitle'] === 'Exercices chapitre 5'
                        && $params['ruleTypes'] === ['minimum_delay']
                        && $params['justification'] === 'Sortie scolaire prévue, devoir urgent.'
                        && $params['dueDate'] === '15/04/2026';
                }),
            )
            ->willReturn('<html>notification</html>');

        $mailer->expects(self::once())->method('send');

        $handler = new OnExceptionDevoirDemandeeHandler(
            $homeworkRepo,
            $userRepo,
            $mailer,
            $twig,
            new NullLogger(),
        );

        ($handler)($this->createEvent());
    }

    private function createEvent(): ExceptionDevoirDemandee
    {
        return new ExceptionDevoirDemandee(
            exceptionId: HomeworkRuleExceptionId::generate(),
            tenantId: TenantId::fromString(self::TENANT_ID),
            homeworkId: HomeworkId::fromString('550e8400-e29b-41d4-a716-446655440050'),
            ruleTypes: ['minimum_delay'],
            justification: 'Sortie scolaire prévue, devoir urgent.',
            createdBy: UserId::fromString(self::TEACHER_ID),
            occurredOn: new DateTimeImmutable('2026-03-19 10:00:00'),
        );
    }

    private function createHomework(): Homework
    {
        return Homework::reconstitute(
            id: HomeworkId::fromString('550e8400-e29b-41d4-a716-446655440050'),
            tenantId: TenantId::fromString(self::TENANT_ID),
            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: 'Faire les exercices 1 à 10',
            dueDate: new DateTimeImmutable('2026-04-15'),
            status: \App\Scolarite\Domain\Model\Homework\HomeworkStatus::PUBLISHED,
            createdAt: new DateTimeImmutable('2026-03-19 10:00:00'),
            updatedAt: new DateTimeImmutable('2026-03-19 10:00:00'),
        );
    }

    /**
     * @param Role[] $roles
     */
    private function createUser(
        string $email,
        array $roles,
        string $firstName = 'Test',
        string $lastName = 'User',
    ): User {
        return User::reconstitute(
            id: UserId::generate(),
            email: new Email($email),
            roles: $roles,
            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: $firstName,
            lastName: $lastName,
        );
    }
}
